diff --git a/.claude/skills/contracts.md b/.claude/skills/contracts.md deleted file mode 100644 index 98832927..00000000 --- a/.claude/skills/contracts.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -description: This skill should be used when the user asks about "ev-reth contracts", "FeeVault", "AdminProxy", "Permit2", "fee distribution", "Foundry deployment scripts", "genesis allocations", or wants to understand how base fees are redirected and distributed. ---- - -# Contracts Onboarding - -## Overview - -The contracts live in `contracts/` and use Foundry for development. There are two main contracts: - -1. **AdminProxy** (`src/AdminProxy.sol`) - Bootstrap contract for admin addresses at genesis -2. **FeeVault** (`src/FeeVault.sol`) - Collects base fees and distributes them between configured recipients -3. **Permit2** (`lib/permit2`) - Uniswap's canonical token approval manager, deployed at genesis via `ev-deployer` (no Foundry deploy script — bytecode is embedded in Rust) - -## Key Files - -### Contract Sources -- `contracts/src/AdminProxy.sol` - Transparent proxy pattern for admin control -- `contracts/src/FeeVault.sol` - Fee collection and distribution logic -- `contracts/lib/permit2` - Uniswap Permit2 submodule (bytecode used by ev-deployer) - -### Deployment Scripts -- `contracts/script/DeployFeeVault.s.sol` - FeeVault deployment with CREATE2 -- `contracts/script/GenerateAdminProxyAlloc.s.sol` - Admin proxy allocation for genesis -- `contracts/script/GenerateFeeVaultAlloc.s.sol` - Fee vault allocation for genesis - -### Tests -- `contracts/test/AdminProxy.t.sol` - AdminProxy test suite -- `contracts/test/FeeVault.t.sol` - FeeVault test suite - -## Architecture - -### AdminProxy -The AdminProxy contract provides a bootstrap mechanism for setting admin addresses at genesis. It uses a transparent proxy pattern allowing upgrades. - -### FeeVault -The FeeVault serves as the destination for redirected base fees (instead of burning them). Key responsibilities: -- Receive base fees from block production -- Distribute accumulated fees between configured recipients -- Manage withdrawal permissions - -### Permit2 -Uniswap's canonical token approval manager deployed at genesis. Unlike AdminProxy and FeeVault, Permit2 has no Foundry deploy script — its bytecode is embedded directly in the Rust `ev-deployer` (`bin/ev-deployer/src/contracts/permit2.rs`), which patches EIP-712 immutables (chain ID, domain separator) at genesis time. - -## Connection to Rust Code - -The contracts integrate with ev-reth through: -1. **Base Fee Redirect** - `crates/ev-revm/src/base_fee.rs` redirects fees to the configured sink address -2. **Chainspec Config** - `crates/node/src/config.rs` defines `base_fee_sink` field for the fee recipient address -3. **Genesis Allocation** - Scripts generate allocations included in chainspec - -## Development Commands - -```bash -cd contracts - -# Build contracts -forge build - -# Run tests -forge test - -# Run specific test -forge test --match-test testFeeCollection - -# Generate allocations -forge script script/GenerateFeeVaultAlloc.s.sol -``` - -## Exploration Starting Points - -1. Read `contracts/src/FeeVault.sol` for fee handling logic -2. Read `contracts/src/AdminProxy.sol` for admin patterns -3. Check `contracts/script/` for deployment patterns -4. See how `crates/ev-revm/src/base_fee.rs` interacts with the sink address diff --git a/.claude/skills/ev-reth-core.md b/.claude/skills/ev-reth-core.md deleted file mode 100644 index 4d3a988d..00000000 --- a/.claude/skills/ev-reth-core.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -description: This skill should be used when learning ev-reth core node architecture, understanding how payload building works, or getting started with the codebase. Use when the user asks "how does ev-reth work", "explain the architecture", "where is the payload builder", "how are transactions submitted", "what is EvolveNode", "show me the node composition", or wants to understand Engine API integration. ---- - -# Core Node Architecture Onboarding - -## Overview - -The core node logic lives in `crates/node/` and `bin/ev-reth/`. This is where ev-reth extends Reth to work with Evolve's transaction submission model. - -## Key Files - -### Entry Point -- `bin/ev-reth/src/main.rs` - Node binary, initializes tracing, extends RPC - -### Node Composition -- `crates/node/src/node.rs` - `EvolveNode` unit struct with trait implementations -- `crates/node/src/lib.rs` - Public API exports - -### Payload Building -- `crates/node/src/builder.rs` - `EvolvePayloadBuilder` - executes transactions, builds blocks -- `crates/node/src/payload_service.rs` - Integrates builder with Reth's payload service -- `crates/node/src/attributes.rs` - `EvolveEnginePayloadBuilderAttributes` - -### Validation -- `crates/node/src/validator.rs` - `EvolveEngineValidator` - custom block validation - -### Configuration -- `crates/node/src/config.rs` - `EvolvePayloadBuilderConfig`, parses chainspec extras -- `crates/node/src/chainspec.rs` - `EvolveChainSpecParser` with EIP-1559 config parsing -- `crates/node/src/args.rs` - CLI argument handling -- `crates/node/src/error.rs` - Error types - -### Execution -- `crates/node/src/executor.rs` - EVM config and executor wiring - -## Architecture - -### Transaction Flow (Key Innovation) - -Unlike standard Ethereum, ev-reth accepts transactions directly through Engine API: - -``` -engine_forkchoiceUpdatedV3 (with transactions in payload attributes) - ↓ -EvolveEnginePayloadBuilderAttributes (decodes transactions) - ↓ -EvolvePayloadBuilder.build_payload() - ↓ -Execute transactions against current state - ↓ -Sealed block returned via engine_getPayloadV3 -``` - -### Node Composition Pattern - -`EvolveNode` is a unit struct that implements `NodeTypes` and `Node` traits: - -```rust -pub struct EvolveNode; - -impl NodeTypes for EvolveNode { - type Primitives = EthPrimitives; - type ChainSpec = ChainSpec; - type StateCommitment = MerklePatriciaTrie; - type Storage = EthStorage; - type Payload = EthEngineTypes; -} -``` - -The composition happens via trait implementations, connecting: -- `EvolveEngineTypes` for custom payload types -- `EvolveEngineValidator` for relaxed validation -- `EvolvePayloadBuilderBuilder` for custom block building -- `EvolveConsensusBuilder` from `evolve_ev_reth::consensus` - -### Validator Customizations - -`EvolveEngineValidator` bypasses certain checks for Evolve compatibility: -- Block hash validation bypassed (Evolve uses prev block's apphash) -- Equal timestamp blocks allowed -- Custom gas limits per payload supported - -### Chainspec Extensions - -The chainspec parser supports Evolve-specific extras via `EvolveEip1559Config`: -- EIP-1559 custom parameters (base fee settings) -- Additional fields parsed from `evolve` key in chainspec extras - -## Key Design Decisions - -1. **No Mempool** - Transactions submitted directly via Engine API -2. **Relaxed Validation** - Block hashes not validated (Evolve-specific) -3. **Configurable Gas Limits** - Per-payload gas limits supported -4. **Modular Builder** - Separates concerns between general and Evolve-specific logic - -## Development Commands - -```bash -just build # Release build -just run-dev # Run with debug logs -just test-node # Test node crate -``` - -## Exploration Starting Points - -1. Start with `bin/ev-reth/src/main.rs` for entry point -2. Read `crates/node/src/node.rs` for component composition -3. Read `crates/node/src/builder.rs` for payload building (this is the heart) -4. Check `crates/node/src/validator.rs` for validation customizations -5. See `crates/node/src/chainspec.rs` for config parsing diff --git a/.claude/skills/ev-reth-evm.md b/.claude/skills/ev-reth-evm.md deleted file mode 100644 index f187d0cf..00000000 --- a/.claude/skills/ev-reth-evm.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -description: This skill should be used when the user asks about "EVM customizations", "base fee redirect", "fee sink", "mint precompile", "native token minting", "contract size limit", "EvEvmFactory", "EvHandler", or wants to understand how ev-reth modifies EVM execution behavior. ---- - -# EVM Customizations Onboarding - -## Overview - -EVM customizations live in `crates/ev-revm/` and `crates/ev-precompiles/`. These modify how the EVM executes transactions. - -## Key Files - -### EVM Factory -- `crates/ev-revm/src/factory.rs` - `EvEvmFactory` wraps `EthEvmFactory` -- `crates/ev-revm/src/evm.rs` - `EvEvm`, `DefaultEvEvm` implementations -- `crates/ev-revm/src/config.rs` - EVM configuration structs - -### Handlers -- `crates/ev-revm/src/handler.rs` - `EvHandler` for execution handling -- `crates/ev-revm/src/base_fee.rs` - `BaseFeeRedirect` logic - -### Precompiles -- `crates/ev-precompiles/src/mint.rs` - `MintPrecompile` at address 0xF100 - -### Shared -- `crates/common/src/constants.rs` - Shared constants - -## Architecture - -### EvEvmFactory - -Wraps Reth's `EthEvmFactory` to inject custom behavior. See `crates/ev-revm/src/factory.rs:103-108`: - -```rust -pub struct EvEvmFactory { - inner: F, - redirect: Option, - mint_precompile: Option, - contract_size_limit: Option, -} -``` - -### Base Fee Redirect - -Instead of burning base fees, redirects them to a configurable address. See `crates/ev-revm/src/factory.rs:27-49`: - -```rust -pub struct BaseFeeRedirectSettings { - redirect: BaseFeeRedirect, // Contains fee_sink address - activation_height: u64, // When redirect activates -} -``` - -The `EvHandler` overrides `reward_beneficiary` (in `handler.rs:126-141`) to credit the sink address with base fees before paying the standard tip to the block producer. - -### Mint Precompile (0xF100) - -Custom precompile for native token minting/burning at address `0xF100`. See `crates/ev-precompiles/src/mint.rs`. - -**INativeToken Interface** (5 functions): -```solidity -interface INativeToken { - function mint(address to, uint256 amount) external; - function burn(address from, uint256 amount) external; - function addToAllowList(address account) external; - function removeFromAllowList(address account) external; - function allowlist(address account) external view returns (bool); -} -``` - -Settings in `crates/ev-revm/src/factory.rs:52-74`: -```rust -pub struct MintPrecompileSettings { - admin: Address, // Who can mint/burn and manage allowlist - activation_height: u64, // When precompile activates -} -``` - -### Contract Size Limits - -Override EIP-170 default (24KB) contract size limit. See `crates/ev-revm/src/factory.rs:77-99`: - -```rust -pub struct ContractSizeLimitSettings { - limit: usize, // Custom limit in bytes - activation_height: u64, // When limit changes -} -``` - -## Configuration Flow - -1. Chainspec defines settings in `extras` field -2. `EvolveChainSpecParser` parses into config structs -3. `EvEvmFactory` receives settings at construction -4. Settings applied during EVM execution based on block height - -## Key Design Decisions - -1. **Configurable Activation** - All features have activation heights for upgrades -2. **Wrapper Pattern** - `EvEvmFactory` wraps standard factory, minimizing changes -3. **Admin Control** - Mint precompile requires admin authorization (or allowlist) -4. **Fee Preservation** - Base fees collected rather than burned (for bridging) - -## Development Commands - -```bash -cargo test -p ev-revm # Test EVM crate -cargo test -p ev-precompiles # Test precompiles -``` - -## Exploration Starting Points - -1. Start with `crates/ev-revm/src/factory.rs` for the wrapper pattern -2. Read `crates/ev-revm/src/handler.rs:126-141` for `reward_beneficiary` override -3. Read `crates/ev-precompiles/src/mint.rs` for precompile implementation -4. Check `crates/ev-revm/src/base_fee.rs` for redirect logic -5. See `crates/node/src/config.rs` for how settings are configured diff --git a/.claude/skills/ev-reth-evolve.md b/.claude/skills/ev-reth-evolve.md deleted file mode 100644 index 28957ffb..00000000 --- a/.claude/skills/ev-reth-evolve.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -description: This skill should be used when the user asks about "Evolve integration", "payload attributes", "EvolvePayloadAttributes", "consensus modifications", "txpoolExt RPC", "how transactions flow through Evolve", or wants to understand how ev-reth connects to the Evolve system. ---- - -# Evolve Integration Onboarding - -## Overview - -Evolve-specific integration lives in `crates/evolve/`. This handles the protocol-level differences between standard Ethereum and Evolve. - -## Key Files - -### Types -- `crates/evolve/src/types.rs` - `EvolvePayloadAttributes`, validation errors -- `crates/evolve/src/lib.rs` - Public API exports - -### Configuration -- `crates/evolve/src/config.rs` - `EvolveConfig`, txpool limits - -### Consensus -- `crates/evolve/src/consensus.rs` - `EvolveConsensus` modifications - -### RPC Extensions -- `crates/evolve/src/rpc/mod.rs` - RPC module exports -- `crates/evolve/src/rpc/txpool.rs` - `EvolveTxpoolApiServer` extension - -## Architecture - -### Payload Attributes - -`EvolvePayloadAttributes` (from `crates/evolve/src/types.rs:7-22`): - -```rust -pub struct EvolvePayloadAttributes { - pub transactions: Vec, // Signed transactions (not raw bytes) - pub gas_limit: Option, // Optional gas limit - pub timestamp: u64, // Block timestamp - pub prev_randao: B256, // Prev randao value - pub suggested_fee_recipient: Address, // Fee recipient - pub parent_hash: B256, // Parent block hash - pub block_number: u64, // Block number -} -``` - -This is the key innovation: transactions are submitted through `engine_forkchoiceUpdatedV3` rather than pulled from a mempool. - -### Consensus Modifications - -`EvolveConsensus` wraps `EthBeaconConsensus` with relaxed rules. The key difference: -- Allows blocks with equal timestamps (`>=` instead of `>`) -- Standard Ethereum requires strictly increasing timestamps - -This is needed because Evolve may produce multiple blocks with the same timestamp. - -### RPC Extensions - -`EvolveTxpoolApiServer` adds custom txpool RPC methods. See `crates/evolve/src/rpc/txpool.rs:10-15`: - -```rust -#[rpc(server, namespace = "txpoolExt")] -pub trait EvolveTxpoolApi { - /// Get transactions from the pool up to the configured limits - #[method(name = "getTxs")] - async fn get_txs(&self) -> RpcResult>; -} -``` - -Note: `get_txs` takes no parameters - limits are configured at API creation time via `EvolveConfig`. - -## Transaction Flow - -``` -Evolve submits transactions - ↓ -engine_forkchoiceUpdatedV3 with EvolvePayloadAttributes - ↓ -Transactions (Vec) validated - ↓ -Passed to EvolvePayloadBuilder - ↓ -Executed and included in block -``` - -## Key Design Decisions - -1. **Direct Submission** - No mempool, transactions in payload attributes -2. **Equal Timestamps** - Consensus allows same-timestamp blocks -3. **RPC Extensions** - Custom namespace for Evolve-specific operations -4. **Signed Transactions** - Payload attributes contain `TransactionSigned`, not raw bytes - -## Connection to Other Components - -- **crates/node** - Uses `EvolvePayloadAttributes` in builder -- **crates/ev-revm** - Executes transactions from attributes -- **bin/ev-reth** - Registers RPC extensions - -## Development Commands - -```bash -just test-evolve -# Or directly: -cargo test -p evolve-ev-reth -``` - -## Exploration Starting Points - -1. Start with `crates/evolve/src/types.rs` for payload attributes -2. Read `crates/evolve/src/consensus.rs` for consensus modifications -3. Check `crates/evolve/src/rpc/txpool.rs` for RPC extensions -4. See `crates/node/src/builder.rs` for how attributes are processed diff --git a/.claude/skills/ev-reth-testing.md b/.claude/skills/ev-reth-testing.md deleted file mode 100644 index c73c55af..00000000 --- a/.claude/skills/ev-reth-testing.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -description: This skill should be used when the user asks to "add a test", "write tests for", "understand the test setup", "run integration tests", "test ev-reth", "test the Engine API", or needs guidance on e2e test patterns, test fixtures, or EvolveTestFixture setup. ---- - -# Testing Infrastructure Onboarding - -## Overview - -Tests live in `crates/tests/` and include e2e and integration tests. The test suite uses `reth-e2e-test-utils` for realistic node testing. - -## Key Files - -### E2E Tests -- `crates/tests/src/e2e_tests.rs` - End-to-end tests (~1,060 lines) -- `crates/tests/src/test_evolve_engine_api.rs` - Engine API specific tests (~370 lines) - -### Test Utilities -- `crates/tests/src/common.rs` - Test utilities and fixtures (~265 lines) -- `crates/tests/src/lib.rs` - Module exports - -## Test Utilities (common.rs) - -### Chain Spec Helpers - -```rust -// Basic test chain spec -pub fn create_test_chain_spec() -> Arc - -// With base fee redirect configured -pub fn create_test_chain_spec_with_base_fee_sink( - base_fee_sink: Option
-) -> Arc - -// With mint precompile admin configured -pub fn create_test_chain_spec_with_mint_admin( - mint_admin: Address -) -> Arc -``` - -### EvolveTestFixture - -The main test fixture for payload builder tests. See `crates/tests/src/common.rs:89-103`: - -```rust -pub struct EvolveTestFixture { - pub builder: EvolvePayloadBuilder, - pub provider: MockEthProvider, - pub genesis_hash: B256, - pub genesis_state_root: B256, - pub temp_dir: TempDir, -} - -impl EvolveTestFixture { - pub async fn new() -> Result - pub fn setup_test_accounts(&self) - pub fn add_mock_header(&self, hash: B256, number: u64, state_root: B256, timestamp: u64) - pub fn create_payload_attributes( - &self, - transactions: Vec, - block_number: u64, - timestamp: u64, - parent_hash: B256, - gas_limit: Option, - ) -> EvolvePayloadAttributes -} -``` - -### Transaction Helpers - -```rust -// Create multiple test transactions -pub fn create_test_transactions(count: usize, nonce_start: u64) -> Vec - -// Create a single test transaction -pub fn create_test_transaction(nonce: u64) -> TransactionSigned -``` - -## Test Patterns - -### Basic Fixture Test - -```rust -#[tokio::test] -async fn test_payload_with_transactions() { - // Setup fixture - let fixture = EvolveTestFixture::new().await.unwrap(); - - // Create transactions - let transactions = create_test_transactions(2, 0); - - // Create payload attributes - let attrs = fixture.create_payload_attributes( - transactions, - 1, // block_number - TEST_TIMESTAMP + 12, // timestamp - fixture.genesis_hash, // parent_hash - None, // gas_limit - ); - - // Build payload using the builder - let result = fixture.builder.build_payload(attrs).await; - - // Assert on result - assert!(result.is_ok()); -} -``` - -### Testing with Custom Chain Spec - -```rust -#[tokio::test] -async fn test_base_fee_redirect() { - let fee_sink = Address::random(); - let chain_spec = create_test_chain_spec_with_base_fee_sink(Some(fee_sink)); - - // Build EVM config with redirect - let evm_config = EthEvmConfig::new(chain_spec.clone()); - let config = EvolvePayloadBuilderConfig::from_chain_spec(chain_spec.as_ref()).unwrap(); - - let base_fee_redirect = config - .base_fee_redirect_settings() - .map(|(sink, activation)| { - BaseFeeRedirectSettings::new(BaseFeeRedirect::new(sink), activation) - }); - - let wrapped_evm = with_ev_handler(evm_config, base_fee_redirect, None, None); - // ... continue with test -} -``` - -## Test Constants - -Defined in `crates/tests/src/common.rs`: - -```rust -pub const TEST_CHAIN_ID: u64 = 1234; -pub const TEST_TIMESTAMP: u64 = 1710338135; -pub const TEST_GAS_LIMIT: u64 = 30_000_000; -pub const TEST_BASE_FEE: u64 = 0; -``` - -## Development Commands - -```bash -just test # Run all tests -just test-verbose # Run with output -just test-integration # Integration tests only - -# Run specific test -cargo test -p ev-reth-tests test_payload_with_transactions - -# Run tests matching pattern -cargo test -p ev-reth-tests mint -``` - -## Key Design Decisions - -1. **MockEthProvider** - Uses Reth's mock provider for unit testing -2. **Fixture Pattern** - `EvolveTestFixture` encapsulates common setup -3. **Chain Spec Variants** - Helpers for different config scenarios -4. **Test Transactions** - `create_test_transactions` uses `Signature::test_signature()` - -## Exploration Starting Points - -1. Start with `crates/tests/src/common.rs` for fixture and helpers -2. Read a simple test in `crates/tests/src/e2e_tests.rs` -3. Check how chain spec is configured for different test scenarios -4. See `EvolveTestFixture::new()` for the full setup flow diff --git a/.github/workflows/ev_deployer.yml b/.github/workflows/ev_deployer.yml deleted file mode 100644 index 91513c0e..00000000 --- a/.github/workflows/ev_deployer.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: EV Deployer CI - -on: - push: - paths: - - 'Cargo.toml' - - 'Cargo.lock' - - '.github/workflows/ev_deployer.yml' - - 'contracts/src/**' - - 'contracts/foundry.toml' - - 'bin/ev-deployer/**' - pull_request: - paths: - - 'Cargo.toml' - - 'Cargo.lock' - - '.github/workflows/ev_deployer.yml' - - 'contracts/src/**' - - 'contracts/foundry.toml' - - 'bin/ev-deployer/**' - workflow_dispatch: - -env: - CARGO_TERM_COLOR: always - CARGO_INCREMENTAL: "0" - CARGO_NET_RETRY: "10" - CARGO_HTTP_MULTIPLEXING: "false" - CARGO_HTTP_TIMEOUT: "600" - CARGO_PROFILE_DEV_DEBUG: "0" - CARGO_PROFILE_TEST_DEBUG: "0" - -jobs: - verify-bytecodes: - name: EV Deployer bytecode verification - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v7 - with: - submodules: recursive - - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - - name: Run bytecode verification tests - run: cargo test -p ev-deployer -- --ignored --test-threads=1 - - unit-tests: - name: EV Deployer unit tests - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v7 - - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: Run unit tests - run: cargo test -p ev-deployer - - e2e-genesis: - name: EV Deployer e2e genesis test - runs-on: ubuntu-24.04 - timeout-minutes: 30 - steps: - - uses: actions/checkout@v7 - with: - submodules: recursive - - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - - name: Run e2e genesis test - run: bash bin/ev-deployer/tests/e2e_genesis.sh diff --git a/.github/workflows/update-skills.yml b/.github/workflows/update-skills.yml deleted file mode 100644 index b7720d6f..00000000 --- a/.github/workflows/update-skills.yml +++ /dev/null @@ -1,246 +0,0 @@ -name: Weekly Skills Update Check - -on: - schedule: - # Run every Friday at 5pm UTC - - cron: '0 17 * * 5' - workflow_dispatch: - inputs: - force: - description: 'Force PR creation even without changes' - required: false - default: 'false' - type: boolean - -permissions: - contents: write - pull-requests: write - -jobs: - check-and-update: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - - name: Check for commits in the past week - id: check-commits - run: | - WEEK_AGO=$(date -d '7 days ago' +%Y-%m-%d 2>/dev/null || date -v-7d +%Y-%m-%d) - - # Count commits from the past week (excluding skill updates) - COMMIT_COUNT=$(git log --since="$WEEK_AGO" --oneline --no-merges -- \ - 'crates/' 'bin/' 'contracts/' 'docs/' \ - ':!.claude/skills/' \ - | wc -l | tr -d ' ') - - if [ "$COMMIT_COUNT" -eq 0 ] && [ "${{ inputs.force }}" != "true" ]; then - echo "No relevant commits in the past week" - echo "has_commits=false" >> $GITHUB_OUTPUT - exit 0 - fi - - echo "has_commits=true" >> $GITHUB_OUTPUT - echo "commit_count=$COMMIT_COUNT" >> $GITHUB_OUTPUT - - - name: Analyze changes by area - if: steps.check-commits.outputs.has_commits == 'true' - id: analyze - run: | - # Check each area for changes (file names only, safe output) - CONTRACTS_COUNT=$(git diff --name-only HEAD~50..HEAD -- 'contracts/' 2>/dev/null | wc -l | tr -d ' ') - CORE_COUNT=$(git diff --name-only HEAD~50..HEAD -- 'crates/node/' 'bin/ev-reth/' 2>/dev/null | wc -l | tr -d ' ') - EVM_COUNT=$(git diff --name-only HEAD~50..HEAD -- 'crates/ev-revm/' 'crates/ev-precompiles/' 'crates/common/' 2>/dev/null | wc -l | tr -d ' ') - EVOLVE_COUNT=$(git diff --name-only HEAD~50..HEAD -- 'crates/evolve/' 2>/dev/null | wc -l | tr -d ' ') - TESTS_COUNT=$(git diff --name-only HEAD~50..HEAD -- 'crates/tests/' 2>/dev/null | wc -l | tr -d ' ') - - # Output counts - echo "contracts_count=$CONTRACTS_COUNT" >> $GITHUB_OUTPUT - echo "core_count=$CORE_COUNT" >> $GITHUB_OUTPUT - echo "evm_count=$EVM_COUNT" >> $GITHUB_OUTPUT - echo "evolve_count=$EVOLVE_COUNT" >> $GITHUB_OUTPUT - echo "tests_count=$TESTS_COUNT" >> $GITHUB_OUTPUT - - TOTAL=$((CONTRACTS_COUNT + CORE_COUNT + EVM_COUNT + EVOLVE_COUNT + TESTS_COUNT)) - if [ "$TOTAL" -eq 0 ]; then - echo "No relevant file changes detected" - echo "has_changes=false" >> $GITHUB_OUTPUT - else - echo "has_changes=true" >> $GITHUB_OUTPUT - fi - - - name: Check for existing PR - if: steps.check-commits.outputs.has_commits == 'true' && steps.analyze.outputs.has_changes == 'true' - id: check-pr - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - EXISTING_PR=$(gh pr list --state open --label "skills-update" --json number --jq '.[0].number') - if [ -n "$EXISTING_PR" ]; then - echo "Existing PR found: #$EXISTING_PR" - echo "existing_pr=$EXISTING_PR" >> $GITHUB_OUTPUT - else - echo "No existing PR found" - echo "existing_pr=" >> $GITHUB_OUTPUT - fi - - - name: Create or update branch - if: steps.check-commits.outputs.has_commits == 'true' && steps.analyze.outputs.has_changes == 'true' && steps.check-pr.outputs.existing_pr == '' - id: create-branch - run: | - DATE=$(date +%Y-%m-%d) - BRANCH="skills-update-${DATE}" - - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Create branch - git checkout -b "$BRANCH" - - # Add a timestamp to skills to track when they were last reviewed - for skill in .claude/skills/*.md; do - if [ -f "$skill" ]; then - # Add or update last-reviewed comment at end of file - if grep -q "//" "$skill" - else - echo "" >> "$skill" - echo "" >> "$skill" - fi - fi - done - - git add .claude/skills/ - git commit -m "chore: flag skills for review (${DATE})" - git push origin "$BRANCH" - - echo "branch=$BRANCH" >> $GITHUB_OUTPUT - - - name: Generate PR body - if: steps.check-commits.outputs.has_commits == 'true' && steps.analyze.outputs.has_changes == 'true' && steps.check-pr.outputs.existing_pr == '' - id: pr-body - env: - COMMIT_COUNT: ${{ steps.check-commits.outputs.commit_count }} - CONTRACTS_COUNT: ${{ steps.analyze.outputs.contracts_count }} - CORE_COUNT: ${{ steps.analyze.outputs.core_count }} - EVM_COUNT: ${{ steps.analyze.outputs.evm_count }} - EVOLVE_COUNT: ${{ steps.analyze.outputs.evolve_count }} - TESTS_COUNT: ${{ steps.analyze.outputs.tests_count }} - run: | - DATE=$(date +%Y-%m-%d) - - # Build PR body safely without user-controlled content - cat > /tmp/pr_body.md << 'HEREDOC_END' - ## Weekly Skills Update Check - - This PR was automatically created because there were code changes this week that may require updates to the onboarding skills. - - ### Summary - HEREDOC_END - - echo "- **Total commits this week:** ${COMMIT_COUNT:-0}" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - - echo "### Skills Review Checklist" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - - if [ "${CONTRACTS_COUNT:-0}" -gt 0 ]; then - echo "#### contracts.md" >> /tmp/pr_body.md - echo "- **Files changed:** $CONTRACTS_COUNT" >> /tmp/pr_body.md - echo "- [ ] Review and update contracts skill" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - fi - - if [ "${CORE_COUNT:-0}" -gt 0 ]; then - echo "#### ev-reth-core.md" >> /tmp/pr_body.md - echo "- **Files changed:** $CORE_COUNT" >> /tmp/pr_body.md - echo "- [ ] Review and update core skill" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - fi - - if [ "${EVM_COUNT:-0}" -gt 0 ]; then - echo "#### ev-reth-evm.md" >> /tmp/pr_body.md - echo "- **Files changed:** $EVM_COUNT" >> /tmp/pr_body.md - echo "- [ ] Review and update EVM skill" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - fi - - if [ "${EVOLVE_COUNT:-0}" -gt 0 ]; then - echo "#### ev-reth-evolve.md" >> /tmp/pr_body.md - echo "- **Files changed:** $EVOLVE_COUNT" >> /tmp/pr_body.md - echo "- [ ] Review and update Evolve skill" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - fi - - if [ "${TESTS_COUNT:-0}" -gt 0 ]; then - echo "#### ev-reth-testing.md" >> /tmp/pr_body.md - echo "- **Files changed:** $TESTS_COUNT" >> /tmp/pr_body.md - echo "- [ ] Review and update testing skill" >> /tmp/pr_body.md - echo "" >> /tmp/pr_body.md - fi - - cat >> /tmp/pr_body.md << 'HEREDOC_END' - - ### Instructions - 1. Review each skill file against the changed code - 2. Update descriptions, file references, and code examples as needed - 3. Check the boxes above as you complete each review - 4. Merge when all relevant skills are updated - - ### Useful Commands - ```bash - # See what changed in each area - git diff main -- crates/node/ bin/ev-reth/ - git diff main -- crates/ev-revm/ crates/ev-precompiles/ - git diff main -- crates/evolve/ - git diff main -- crates/tests/ - git diff main -- contracts/ - ``` - - --- - *This PR was auto-generated by the weekly skills update workflow.* - HEREDOC_END - - - name: Create Pull Request - if: steps.check-commits.outputs.has_commits == 'true' && steps.analyze.outputs.has_changes == 'true' && steps.check-pr.outputs.existing_pr == '' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: ${{ steps.create-branch.outputs.branch }} - run: | - DATE=$(date +%Y-%m-%d) - gh pr create \ - --title "chore: weekly skills review (${DATE})" \ - --label "skills-update" \ - --body-file /tmp/pr_body.md - - - name: Update existing PR with comment - if: steps.check-commits.outputs.has_commits == 'true' && steps.analyze.outputs.has_changes == 'true' && steps.check-pr.outputs.existing_pr != '' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ steps.check-pr.outputs.existing_pr }} - CONTRACTS_COUNT: ${{ steps.analyze.outputs.contracts_count }} - CORE_COUNT: ${{ steps.analyze.outputs.core_count }} - EVM_COUNT: ${{ steps.analyze.outputs.evm_count }} - EVOLVE_COUNT: ${{ steps.analyze.outputs.evolve_count }} - TESTS_COUNT: ${{ steps.analyze.outputs.tests_count }} - run: | - DATE=$(date +%Y-%m-%d) - - # Build comment safely using environment variables - cat > /tmp/comment.md << HEREDOC_END - ## Additional changes detected (${DATE}) - - New commits detected. Please review the updated file counts: - - | Skill | Files Changed | - |-------|---------------| - | contracts | ${CONTRACTS_COUNT:-0} | - | ev-reth-core | ${CORE_COUNT:-0} | - | ev-reth-evm | ${EVM_COUNT:-0} | - | ev-reth-evolve | ${EVOLVE_COUNT:-0} | - | ev-reth-testing | ${TESTS_COUNT:-0} | - HEREDOC_END - - gh pr comment "$PR_NUMBER" --body-file /tmp/comment.md diff --git a/Cargo.lock b/Cargo.lock index b4738e16..6c6acbdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1073,26 +1073,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "arboard" -version = "3.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" -dependencies = [ - "clipboard-win", - "image", - "log", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation", - "parking_lot", - "percent-encoding", - "windows-sys 0.60.2", - "x11rb", -] - [[package]] name = "archery" version = "1.2.2" @@ -1540,15 +1520,6 @@ dependencies = [ "rustc_version 0.4.1", ] -[[package]] -name = "atomic" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" -dependencies = [ - "bytemuck", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -1689,7 +1660,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.13.1", + "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1701,30 +1672,15 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec 0.6.3", -] - [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit-vec" version = "0.8.0" @@ -1767,12 +1723,6 @@ dependencies = [ "hex-conservative 0.2.2", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.13.1" @@ -1965,12 +1915,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" version = "1.12.1" @@ -2076,7 +2020,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2140,15 +2084,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" -[[package]] -name = "clipboard-win" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" -dependencies = [ - "error-code", -] - [[package]] name = "cmake" version = "0.1.58" @@ -2462,11 +2397,10 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.13.1", + "bitflags", "crossterm_winapi", "derive_more", "document-features", - "futures-core", "mio", "parking_lot", "rustix", @@ -2522,16 +2456,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "csscolorparser" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" -dependencies = [ - "lab", - "phf 0.11.3", -] - [[package]] name = "ctr" version = "0.9.2" @@ -2662,12 +2586,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "deltae" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" - [[package]] name = "der" version = "0.7.10" @@ -2832,16 +2750,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags 2.13.1", - "objc2", -] - [[package]] name = "displaydoc" version = "0.2.6" @@ -3017,12 +2925,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "error-code" -version = "3.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" - [[package]] name = "ethereum_hashing" version = "0.8.0" @@ -3074,15 +2976,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - [[package]] name = "ev-common" version = "0.1.0" @@ -3091,53 +2984,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "ev-deployer" -version = "0.1.0" -dependencies = [ - "alloy", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-signer-local", - "async-trait", - "clap", - "eyre", - "rand 0.10.2", - "serde", - "serde_json", - "tempfile", - "tokio", - "toml 1.1.3+spec-1.1.0", -] - -[[package]] -name = "ev-dev" -version = "0.1.0" -dependencies = [ - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types", - "alloy-signer-local", - "arboard", - "clap", - "crossterm", - "ev-deployer", - "ev-node", - "evolve-ev-reth", - "eyre", - "futures", - "ratatui", - "reth-cli-util", - "reth-ethereum-cli", - "reth-tracing", - "serde_json", - "tempfile", - "tokio", - "tracing", - "tracing-subscriber 0.3.23", -] - [[package]] name = "ev-node" version = "0.1.0" @@ -3414,16 +3260,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set 0.5.3", - "regex", -] - [[package]] name = "fast-srgb8" version = "1.0.0" @@ -3458,21 +3294,6 @@ dependencies = [ "bytes", ] -[[package]] -name = "fax" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - [[package]] name = "fdlimit" version = "0.3.0" @@ -3499,17 +3320,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "filedescriptor" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" -dependencies = [ - "libc", - "thiserror 1.0.69", - "winapi", -] - [[package]] name = "filetime" version = "0.2.29" @@ -3526,12 +3336,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "finl_unicode" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" - [[package]] name = "fixed-cache" version = "0.1.10" @@ -3576,12 +3380,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" version = "1.1.9" @@ -3749,8 +3547,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link", - "windows-result", + "windows-link 0.2.1", + "windows-result 0.3.4", ] [[package]] @@ -3764,16 +3562,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "gethostname" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" -dependencies = [ - "rustix", - "windows-link", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -3829,7 +3617,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddddbf932745a6be37109b6112d3ee09696106f848449069d3a57bba937ab82e" dependencies = [ - "bitflags 2.13.1", + "bitflags", "libc", "libgit2-sys", "log", @@ -3917,17 +3705,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - [[package]] name = "hash-db" version = "0.15.2" @@ -4305,7 +4082,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -4436,20 +4213,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "image" -version = "0.25.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "moxcms", - "num-traits", - "png", - "tiff", -] - [[package]] name = "imbl" version = "7.0.1" @@ -4561,7 +4324,7 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8" dependencies = [ - "bitflags 2.13.1", + "bitflags", "inotify-sys", "libc", ] @@ -4621,7 +4384,7 @@ dependencies = [ "socket2", "widestring", "windows-registry", - "windows-result", + "windows-result 0.4.1", "windows-sys 0.61.2", ] @@ -4703,7 +4466,7 @@ dependencies = [ "simd_cesu8", "thiserror 2.0.19", "walkdir", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -4759,12 +4522,13 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", + "once_cell", "wasm-bindgen", ] @@ -5044,16 +4808,10 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.13.1", + "bitflags", "libc", ] -[[package]] -name = "lab" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" - [[package]] name = "lazy_static" version = "1.5.0" @@ -5096,7 +4854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -5177,7 +4935,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -5292,16 +5050,6 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90071f8077f8e40adfc4b7fe9cd495ce316263f19e75c2211eeff3fdf475a3d9" -[[package]] -name = "mac_address" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" -dependencies = [ - "nix", - "winapi", -] - [[package]] name = "mach2" version = "0.5.0" @@ -5363,21 +5111,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memmem" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "metrics" version = "0.24.6" @@ -5427,7 +5160,7 @@ dependencies = [ "once_cell", "procfs", "rlimit", - "windows", + "windows 0.62.2", ] [[package]] @@ -5535,16 +5268,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - [[package]] name = "multiaddr" version = "0.18.2" @@ -5592,19 +5315,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.13.1", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nom" version = "7.1.3" @@ -5630,7 +5340,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.13.1", + "bitflags", "fsevent-sys", "inotify", "kqueue", @@ -5648,7 +5358,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -5708,17 +5418,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -5814,66 +5513,13 @@ dependencies = [ "smallvec", ] -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-app-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" -dependencies = [ - "bitflags 2.13.1", - "objc2", - "objc2-core-graphics", - "objc2-foundation", -] - [[package]] name = "objc2-core-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.1", - "dispatch2", - "objc2", -] - -[[package]] -name = "objc2-core-graphics" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" -dependencies = [ - "bitflags 2.13.1", - "dispatch2", - "objc2", - "objc2-core-foundation", - "objc2-io-surface", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" -dependencies = [ - "bitflags 2.13.1", - "objc2", - "objc2-core-foundation", + "bitflags", ] [[package]] @@ -5886,17 +5532,6 @@ dependencies = [ "objc2-core-foundation", ] -[[package]] -name = "objc2-io-surface" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" -dependencies = [ - "bitflags 2.13.1", - "objc2", - "objc2-core-foundation", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -6006,15 +5641,6 @@ dependencies = [ "thiserror 2.0.19", ] -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "num-traits", -] - [[package]] name = "p256" version = "0.13.2" @@ -6110,7 +5736,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6155,38 +5781,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "pest_derive" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "pest_meta" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" -dependencies = [ - "pest", -] - [[package]] name = "pharos" version = "0.5.3" @@ -6197,23 +5791,13 @@ dependencies = [ "rustc_version 0.4.1", ] -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros 0.11.3", - "phf_shared 0.11.3", -] - [[package]] name = "phf" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros 0.13.1", + "phf_macros", "phf_shared 0.13.1", "serde", ] @@ -6227,26 +5811,6 @@ dependencies = [ "phf_shared 0.14.0", ] -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.7", -] - [[package]] name = "phf_generator" version = "0.13.1" @@ -6257,39 +5821,17 @@ dependencies = [ "phf_shared 0.13.1", ] -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "phf_macros" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator 0.13.1", - "phf_shared 0.13.1", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", + "phf_generator", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -6367,19 +5909,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "png" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" -dependencies = [ - "bitflags 2.13.1", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "polyval" version = "0.6.2" @@ -6540,7 +6069,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.13.1", + "bitflags", "chrono", "flate2", "procfs-core", @@ -6553,7 +6082,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.13.1", + "bitflags", "chrono", "hex", ] @@ -6564,9 +6093,9 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", - "bitflags 2.13.1", + "bit-set", + "bit-vec", + "bitflags", "num-traits", "rand 0.9.5", "rand_chacha 0.9.0", @@ -6641,12 +6170,6 @@ dependencies = [ "prost", ] -[[package]] -name = "pxfm" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" - [[package]] name = "quanta" version = "0.12.6" @@ -6668,12 +6191,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quinn" version = "0.11.11" @@ -6883,9 +6400,6 @@ dependencies = [ "instability", "ratatui-core", "ratatui-crossterm", - "ratatui-macros", - "ratatui-termina", - "ratatui-termwiz", "ratatui-widgets", "serde", ] @@ -6896,9 +6410,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c" dependencies = [ - "bitflags 2.13.1", + "bitflags", "compact_str", - "critical-section", "hashbrown 0.17.1", "itertools 0.14.0", "kasuari", @@ -6924,44 +6437,13 @@ dependencies = [ "ratatui-core", ] -[[package]] -name = "ratatui-macros" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7dc68daa7498a43e4d68e0eb078427e10c38fbcfbb1e42d955f1fa2140d814" -dependencies = [ - "ratatui-core", - "ratatui-widgets", -] - -[[package]] -name = "ratatui-termina" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0bf912d9e66f057a759d92e386a280ea886b352ab757d6ac4d653c7ed2c43c2" -dependencies = [ - "instability", - "ratatui-core", - "termina", -] - -[[package]] -name = "ratatui-termwiz" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf03e0380b7744054d6cb74224fe3adf062a029754933f575ca1e3b4c2ce977" -dependencies = [ - "ratatui-core", - "termwiz", -] - [[package]] name = "ratatui-widgets" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1" dependencies = [ - "bitflags 2.13.1", + "bitflags", "hashbrown 0.17.1", "indoc", "instability", @@ -6981,7 +6463,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -7016,7 +6498,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -7342,7 +6824,7 @@ dependencies = [ "tar", "tokio", "tokio-stream", - "toml 0.9.12+spec-1.1.0", + "toml", "tracing", "url", "zstd", @@ -7423,7 +6905,7 @@ dependencies = [ "reth-stages-types", "reth-static-file-types", "serde", - "toml 0.9.12+spec-1.1.0", + "toml", "url", ] @@ -8399,7 +7881,7 @@ name = "reth-libmdbx" version = "2.4.1" source = "git+https://github.com/paradigmxyz/reth.git?tag=v2.4.1#8eb210175687c9f0c889a3b6795c16781d830e3a" dependencies = [ - "bitflags 2.13.1", + "bitflags", "byteorder", "crossbeam-queue", "dashmap", @@ -8749,7 +8231,7 @@ dependencies = [ "serde", "strum 0.27.2", "thiserror 2.0.19", - "toml 0.9.12+spec-1.1.0", + "toml", "tracing", "url", "vergen", @@ -9705,7 +9187,7 @@ dependencies = [ "alloy-rlp", "aquamarine", "auto_impl", - "bitflags 2.13.1", + "bitflags", "futures-util", "imbl", "metrics", @@ -10071,7 +9553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ef2fffc28acc41e8ef7525f2b1d16288a10f80579a1b33e7e62abc9892314a" dependencies = [ "alloy-eip7928", - "bitflags 2.13.1", + "bitflags", "nonmax", "revm-bytecode", "revm-primitives", @@ -10271,7 +9753,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.1", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -10334,7 +9816,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10389,7 +9871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", "wait-timeout", ] @@ -10536,7 +10018,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.1", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -11065,7 +10547,7 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows", + "windows 0.62.2", ] [[package]] @@ -11074,7 +10556,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.13.1", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -11125,82 +10607,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "termina" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9048a889effe34a5cddee0af7f53285198b16dca3be510858d38dfdb3e62a04e" -dependencies = [ - "bitflags 2.13.1", - "parking_lot", - "rustix", - "signal-hook", - "windows-sys 0.61.2", -] - -[[package]] -name = "terminfo" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" -dependencies = [ - "fnv", - "nom", - "phf 0.11.3", - "phf_codegen", -] - -[[package]] -name = "termios" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" -dependencies = [ - "libc", -] - -[[package]] -name = "termwiz" -version = "0.23.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" -dependencies = [ - "anyhow", - "base64 0.22.1", - "bitflags 2.13.1", - "fancy-regex", - "filedescriptor", - "finl_unicode", - "fixedbitset", - "hex", - "lazy_static", - "libc", - "log", - "memmem", - "nix", - "num-derive", - "num-traits", - "ordered-float", - "pest", - "pest_derive", - "phf 0.11.3", - "sha2", - "signal-hook", - "siphasher", - "terminfo", - "termios", - "thiserror 1.0.69", - "ucd-trie", - "unicode-segmentation", - "vtparse", - "wezterm-bidi", - "wezterm-blob-leases", - "wezterm-color-types", - "wezterm-dynamic", - "wezterm-input-types", - "winapi", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -11247,12 +10653,12 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d2e834949be5111506bb252643498af1514f600d9e1dceedaa42afae155b67f" dependencies = [ - "bitflags 2.13.1", + "bitflags", "cfg-if", "libc", "log", "rustversion", - "windows", + "windows 0.61.3", ] [[package]] @@ -11273,20 +10679,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "tiff" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error 2.0.1", - "weezl", - "zune-jpeg", -] - [[package]] name = "tikv-jemalloc-ctl" version = "0.6.1" @@ -11473,21 +10865,6 @@ dependencies = [ "winnow 0.7.15", ] -[[package]] -name = "toml" -version = "1.1.3+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" -dependencies = [ - "indexmap 2.14.0", - "serde_core", - "serde_spanned", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 1.0.4", -] - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -11609,7 +10986,7 @@ checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", "base64 0.22.1", - "bitflags 2.13.1", + "bitflags", "bytes", "futures-core", "futures-util", @@ -12053,7 +11430,6 @@ version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ - "atomic", "getrandom 0.4.3", "js-sys", "wasm-bindgen", @@ -12127,15 +11503,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "vtparse" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" -dependencies = [ - "utf8parse", -] - [[package]] name = "wait-timeout" version = "0.2.1" @@ -12172,18 +11539,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.4+wasi-0.2.12" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -12194,9 +11561,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -12204,9 +11571,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12214,9 +11581,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -12227,9 +11594,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -12263,9 +11630,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -12317,84 +11684,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "weezl" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" - -[[package]] -name = "wezterm-bidi" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" -dependencies = [ - "log", - "wezterm-dynamic", -] - -[[package]] -name = "wezterm-blob-leases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" -dependencies = [ - "getrandom 0.3.4", - "mac_address", - "sha2", - "thiserror 1.0.69", - "uuid", -] - -[[package]] -name = "wezterm-color-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" -dependencies = [ - "csscolorparser", - "deltae", - "lazy_static", - "wezterm-dynamic", -] - -[[package]] -name = "wezterm-dynamic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" -dependencies = [ - "log", - "ordered-float", - "strsim", - "thiserror 1.0.69", - "wezterm-dynamic-derive", -] - -[[package]] -name = "wezterm-dynamic-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "wezterm-input-types" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" -dependencies = [ - "bitflags 1.3.2", - "euclid", - "lazy_static", - "serde", - "wezterm-dynamic", -] - [[package]] name = "wide" version = "0.7.33" @@ -12442,16 +11731,38 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + [[package]] name = "windows" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-numerics", + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", ] [[package]] @@ -12460,7 +11771,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core", + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -12471,9 +11795,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", ] [[package]] @@ -12482,9 +11817,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core", - "windows-link", - "windows-threading", + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -12509,20 +11844,36 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + [[package]] name = "windows-numerics" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.62.2", + "windows-link 0.2.1", ] [[package]] @@ -12531,9 +11882,18 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -12542,7 +11902,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -12551,7 +11920,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -12596,7 +11965,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -12636,7 +12005,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -12647,13 +12016,22 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-threading" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -12849,23 +12227,6 @@ dependencies = [ "tap", ] -[[package]] -name = "x11rb" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" -dependencies = [ - "gethostname", - "rustix", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" - [[package]] name = "xattr" version = "1.6.1" @@ -13032,18 +12393,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/Cargo.toml b/Cargo.toml index 671e8bb8..0f8a3e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] resolver = "2" members = [ - "bin/ev-deployer", - "bin/ev-dev", "bin/ev-reth", "crates/common", "crates/ev-primitives", diff --git a/README.md b/README.md index 88bf1e54..cc03200c 100644 --- a/README.md +++ b/README.md @@ -534,17 +534,10 @@ All standard Reth configuration options are supported. Key options for Evolve in ``` ev-reth/ ├── bin/ -│ ├── ev-reth/ # Main binary -│ │ ├── Cargo.toml -│ │ └── src/ -│ │ └── main.rs # Binary with Engine API integration -│ └── ev-dev/ # Local dev chain (like Hardhat Node / Anvil) +│ └── ev-reth/ # Main binary │ ├── Cargo.toml -│ ├── README.md # Usage guide with tool integrations -│ ├── assets/ -│ │ └── devnet-genesis.json │ └── src/ -│ └── main.rs +│ └── main.rs # Binary with Engine API integration ├── crates/ │ ├── common/ # Shared utilities and constants │ │ ├── Cargo.toml diff --git a/bin/ev-deployer/Cargo.toml b/bin/ev-deployer/Cargo.toml deleted file mode 100644 index 741684ec..00000000 --- a/bin/ev-deployer/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "ev-deployer" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true -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 = "1.1" -eyre = { workspace = true } -rand = { workspace = true } - -[dev-dependencies] -tempfile = { workspace = true } - -[lints] -workspace = true diff --git a/bin/ev-deployer/README.md b/bin/ev-deployer/README.md deleted file mode 100644 index 3d457509..00000000 --- a/bin/ev-deployer/README.md +++ /dev/null @@ -1,207 +0,0 @@ -# EV Deployer - -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 -# 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 - -# 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 \ - --private-key 0x... \ - --state deploy-state.json -``` - -## Building - -```bash -just build-deployer -``` - -The binary is output to `target/release/ev-deployer`. - -## Commands - -### `init genesis` - -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 genesis - -# 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 genesis \ - --chain-id 42170 \ - --permit2 \ - --deterministic-deployer \ - --admin-proxy-owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ - --output genesis.toml -``` - -| Flag | Description | -|------|-------------| -| `--output ` | Write to file instead of stdout | -| `--chain-id ` | Set the chain ID (defaults to 0) | -| `--permit2` | Enable Permit2 with its canonical address | -| `--deterministic-deployer` | Enable the deterministic deployer (Nick's factory) | -| `--admin-proxy-owner ` | 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 ` | Write to file instead of stdout | -| `--chain-id ` | Set the chain ID (defaults to 0) | -| `--permit2` | Enable Permit2 | -| `--admin-proxy-owner ` | Enable AdminProxy with the given owner | - -### `genesis` - -Generate genesis alloc JSON from a config. - -```bash -# Print alloc to stdout -ev-deployer genesis --config deploy.toml - -# Write to file -ev-deployer genesis --config deploy.toml --output alloc.json - -# Merge into an existing genesis file -ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json - -# Overwrite existing addresses when merging -ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json --force - -# Also export an address manifest -ev-deployer genesis --config deploy.toml --addresses-out addresses.json -``` - -In genesis mode, every configured contract must have an `address` field. - -### `deploy` - -Deploy contracts to a live chain via CREATE2. - -```bash -ev-deployer deploy \ - --config deploy.toml \ - --rpc-url http://localhost:8545 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ - --state deploy-state.json \ - --addresses-out addresses.json -``` - -| Flag | Env var | Description | -|------|---------|-------------| -| `--config ` | | Path to the TOML config | -| `--rpc-url ` | `EV_DEPLOYER_RPC_URL` | RPC endpoint of the target chain | -| `--private-key ` | `EV_DEPLOYER_PRIVATE_KEY` | Hex-encoded private key for signing | -| `--state ` | | Path to the state file (created if absent) | -| `--addresses-out ` | | Write a JSON address manifest | - -The deploy pipeline: - -1. Connects to the RPC and verifies the chain ID matches the config. -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). - -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 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: - -- 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. - -### `compute-address` - -Look up the configured address for a contract. - -```bash -ev-deployer compute-address --config deploy.toml --contract permit2 -``` - -## Config Reference - -### `[chain]` - -| Field | Type | Description | -|-------|------|-------------| -| `chain_id` | u64 | Chain ID | - -### `[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` | 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. - -## Testing - -```bash -just test-deployer -``` diff --git a/bin/ev-deployer/examples/devnet.toml b/bin/ev-deployer/examples/devnet.toml deleted file mode 100644 index 5e3d9096..00000000 --- a/bin/ev-deployer/examples/devnet.toml +++ /dev/null @@ -1,11 +0,0 @@ -[chain] -chain_id = 1234 - -[contracts.admin_proxy] -address = "0x000000000000000000000000000000000000Ad00" -owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - -[contracts.permit2] -# Canonical Uniswap Permit2 address (same on all chains via CREATE2). -# Using it here so frontends, SDKs and routers work out of the box. -address = "0x000000000022D473030F116dDEE9F6B43aC78BA3" diff --git a/bin/ev-deployer/src/config.rs b/bin/ev-deployer/src/config.rs deleted file mode 100644 index a251d9ac..00000000 --- a/bin/ev-deployer/src/config.rs +++ /dev/null @@ -1,372 +0,0 @@ -//! TOML config types, parsing, and validation. - -use alloy_primitives::Address; -use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, path::Path}; - -/// Top-level deploy configuration. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct DeployConfig { - /// Chain configuration. - pub chain: ChainConfig, - /// Contract configurations. - #[serde(default)] - pub contracts: ContractsConfig, -} - -/// Chain-level settings. -#[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, Eq)] -pub struct ContractsConfig { - /// `AdminProxy` contract config (optional). - pub admin_proxy: Option, - /// `Permit2` contract config (optional). - pub permit2: Option, - /// Deterministic deployer (Nick's factory) config (optional). - pub deterministic_deployer: Option, -} - -impl ContractsConfig { - /// Collect all configured deploy addresses. - fn all_addresses(&self) -> Vec
{ - let mut addrs = Vec::new(); - if let Some(ref ap) = self.admin_proxy { - if let Some(addr) = ap.address { - addrs.push(addr); - } - } - if let Some(ref p2) = self.permit2 { - if let Some(addr) = p2.address { - addrs.push(addr); - } - } - if let Some(ref dd) = self.deterministic_deployer { - if let Some(addr) = dd.address { - addrs.push(addr); - } - } - addrs - } -} - -/// `AdminProxy` configuration. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct AdminProxyConfig { - /// Address to deploy at (required for genesis, ignored for deploy). - pub address: Option
, - /// Owner address. - pub owner: Address, -} - -/// `Permit2` configuration (Uniswap token approval manager). -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct Permit2Config { - /// Address to deploy at (required for genesis, ignored for deploy). - pub address: Option
, -} - -/// 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, Eq)] -pub struct DeterministicDeployerConfig { - /// Address (defaults to the canonical `0x4e59b44847b379578588920ca78fbf26c0b4956c`). - pub address: Option
, -} - -impl DeployConfig { - /// Load and validate config from a TOML file. - pub fn load(path: &Path) -> eyre::Result { - let content = std::fs::read_to_string(path)?; - let config: Self = toml::from_str(&content)?; - config.validate()?; - Ok(config) - } - - /// Validate config values. - pub(crate) fn validate(&self) -> eyre::Result<()> { - if let Some(ref ap) = self.contracts.admin_proxy { - eyre::ensure!( - !ap.owner.is_zero(), - "admin_proxy.owner must not be the zero address" - ); - } - - if let Some(ref p2) = self.contracts.permit2 { - if let Some(addr) = p2.address { - eyre::ensure!( - !addr.is_zero(), - "permit2.address must not be the zero address" - ); - } - } - - if let Some(ref dd) = self.contracts.deterministic_deployer { - if let Some(addr) = dd.address { - eyre::ensure!( - !addr.is_zero(), - "deterministic_deployer.address must not be the zero address" - ); - } - } - - // Detect duplicate deploy addresses across all contracts. - let mut seen = HashSet::new(); - for addr in self.contracts.all_addresses() { - eyre::ensure!(seen.insert(addr), "duplicate deploy address: {addr}"); - } - - Ok(()) - } - - /// Additional validation for genesis mode: all addresses must be specified. - pub fn validate_for_genesis(&self) -> eyre::Result<()> { - if let Some(ref ap) = self.contracts.admin_proxy { - eyre::ensure!( - ap.address.is_some(), - "admin_proxy.address is required for genesis mode" - ); - } - if let Some(ref p2) = self.contracts.permit2 { - eyre::ensure!( - p2.address.is_some(), - "permit2.address is required for genesis mode" - ); - } - if let Some(ref dd) = self.contracts.deterministic_deployer { - eyre::ensure!( - dd.address.is_some(), - "deterministic_deployer.address is required for genesis mode" - ); - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_full_config() { - let toml = r#" -[chain] -chain_id = 1234 - -[contracts.admin_proxy] -address = "0x000000000000000000000000000000000000Ad00" -owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - assert_eq!(config.chain.chain_id, 1234); - assert!(config.contracts.admin_proxy.is_some()); - config.validate().unwrap(); - } - - #[test] - fn reject_zero_owner() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.admin_proxy] -address = "0x000000000000000000000000000000000000Ad00" -owner = "0x0000000000000000000000000000000000000000" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - assert!(config.validate().is_err()); - } - - #[test] - fn no_contracts_section() { - let toml = r#" -[chain] -chain_id = 1 -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.contracts.admin_proxy.is_none()); - } - - #[test] - fn reject_zero_permit2_address() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.permit2] -address = "0x0000000000000000000000000000000000000000" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - assert!(config.validate().is_err()); - } - - #[test] - fn reject_duplicate_deploy_address() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.admin_proxy] -address = "0x000000000000000000000000000000000000Ad00" -owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - -[contracts.permit2] -address = "0x000000000000000000000000000000000000Ad00" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - let err = config.validate().unwrap_err().to_string(); - assert!(err.contains("duplicate deploy address"), "{err}"); - } - - #[test] - fn permit2_only() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.permit2] -address = "0x000000000022D473030F116dDEE9F6B43aC78BA3" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.contracts.permit2.is_some()); - assert!(config.contracts.admin_proxy.is_none()); - } - - #[test] - fn both_contracts() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.admin_proxy] -address = "0x000000000000000000000000000000000000Ad00" -owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - -[contracts.permit2] -address = "0x000000000022D473030F116dDEE9F6B43aC78BA3" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.contracts.admin_proxy.is_some()); - assert!(config.contracts.permit2.is_some()); - } - - #[test] - fn reject_missing_address_for_genesis() { - use alloy_primitives::address; - - let config = DeployConfig { - chain: ChainConfig { chain_id: 1 }, - contracts: ContractsConfig { - admin_proxy: Some(AdminProxyConfig { - address: None, - owner: address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), - }), - permit2: None, - deterministic_deployer: None, - }, - }; - config.validate().unwrap(); // base validation passes - assert!(config.validate_for_genesis().is_err()); - } - - #[test] - fn admin_proxy_only() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.admin_proxy] -address = "0x000000000000000000000000000000000000Ad00" -owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.contracts.admin_proxy.is_some()); - } - - #[test] - fn deterministic_deployer_only() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.deterministic_deployer] -address = "0x4e59b44847b379578588920cA78FbF26c0B4956C" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.contracts.deterministic_deployer.is_some()); - assert!(config.contracts.admin_proxy.is_none()); - assert!(config.contracts.permit2.is_none()); - } - - #[test] - fn deterministic_deployer_without_address() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.deterministic_deployer] -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.contracts.deterministic_deployer.is_some()); - assert!(config - .contracts - .deterministic_deployer - .unwrap() - .address - .is_none()); - } - - #[test] - fn reject_zero_deterministic_deployer_address() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.deterministic_deployer] -address = "0x0000000000000000000000000000000000000000" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - assert!(config.validate().is_err()); - } - - #[test] - fn reject_duplicate_deterministic_deployer_address() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.permit2] -address = "0x4e59b44847b379578588920cA78FbF26c0B4956C" - -[contracts.deterministic_deployer] -address = "0x4e59b44847b379578588920cA78FbF26c0B4956C" -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - let err = config.validate().unwrap_err().to_string(); - assert!(err.contains("duplicate deploy address"), "{err}"); - } - - #[test] - fn reject_missing_deterministic_deployer_address_for_genesis() { - let toml = r#" -[chain] -chain_id = 1 - -[contracts.deterministic_deployer] -"#; - let config: DeployConfig = toml::from_str(toml).unwrap(); - config.validate().unwrap(); - assert!(config.validate_for_genesis().is_err()); - } -} diff --git a/bin/ev-deployer/src/contracts/admin_proxy.rs b/bin/ev-deployer/src/contracts/admin_proxy.rs deleted file mode 100644 index defa9c28..00000000 --- a/bin/ev-deployer/src/contracts/admin_proxy.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! `AdminProxy` bytecode and storage encoding. - -use crate::{config::AdminProxyConfig, contracts::GenesisContract}; -use alloy_primitives::{hex, Bytes, B256, U256}; -use std::collections::BTreeMap; - -/// `AdminProxy` runtime bytecode compiled with solc 0.8.33 (`cbor_metadata=false`). -/// Regenerate with: `cd contracts && forge inspect AdminProxy deployedBytecode` -pub(crate) const ADMIN_PROXY_BYTECODE: &[u8] = &hex!("60806040526004361061007e575f3560e01c80638da5cb5b1161004d5780638da5cb5b1461012d578063e30c397814610157578063f2fde38b14610181578063fa4bb79d146101a957610085565b806318dfb3c7146100895780631cff79cd146100c557806379ba5097146101015780638b5298541461011757610085565b3661008557005b5f5ffd5b348015610094575f5ffd5b506100af60048036038101906100aa9190610cf8565b6101e5565b6040516100bc9190610ea1565b60405180910390f35b3480156100d0575f5ffd5b506100eb60048036038101906100e69190610f70565b6104d9565b6040516100f89190611015565b60405180910390f35b34801561010c575f5ffd5b5061011561066c565b005b348015610122575f5ffd5b5061012b6107ed565b005b348015610138575f5ffd5b506101416108b4565b60405161014e9190611044565b60405180910390f35b348015610162575f5ffd5b5061016b6108d8565b6040516101789190611044565b60405180910390f35b34801561018c575f5ffd5b506101a760048036038101906101a2919061105d565b6108fd565b005b3480156101b4575f5ffd5b506101cf60048036038101906101ca91906110bb565b610aa4565b6040516101dc9190611015565b60405180910390f35b60605f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461026c576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282905085859050146102ab576040517fff633a3800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484905067ffffffffffffffff8111156102c8576102c761112c565b5b6040519080825280602002602001820160405280156102fb57816020015b60608152602001906001900390816102e65790505b5090505f5f90505b858590508110156104d0575f5f87878481811061032357610322611159565b5b9050602002016020810190610338919061105d565b73ffffffffffffffffffffffffffffffffffffffff1686868581811061036157610360611159565b5b90506020028101906103739190611192565b604051610381929190611230565b5f604051808303815f865af19150503d805f81146103ba576040519150601f19603f3d011682016040523d82523d5f602084013e6103bf565b606091505b50915091508161040657806040517fa5fa8d2b0000000000000000000000000000000000000000000000000000000081526004016103fd9190611015565b60405180910390fd5b87878481811061041957610418611159565b5b905060200201602081019061042e919061105d565b73ffffffffffffffffffffffffffffffffffffffff167fc96720f35dd524e76ea92971ce13d08e9a17816bf3b0008a7083e6032354ebb587878681811061047857610477611159565b5b905060200281019061048a9190611192565b8460405161049a93929190611274565b60405180910390a2808484815181106104b6576104b5611159565b5b602002602001018190525050508080600101915050610303565b50949350505050565b60605f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610560576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5f8573ffffffffffffffffffffffffffffffffffffffff168585604051610589929190611230565b5f604051808303815f865af19150503d805f81146105c2576040519150601f19603f3d011682016040523d82523d5f602084013e6105c7565b606091505b50915091508161060e57806040517fa5fa8d2b0000000000000000000000000000000000000000000000000000000081526004016106059190611015565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff167fc96720f35dd524e76ea92971ce13d08e9a17816bf3b0008a7083e6032354ebb586868460405161065893929190611274565b60405180910390a280925050509392505050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106f2576040517f1853971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff165f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3335f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f60015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610872576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610982576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036109e7576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff165f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60605f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b2b576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5f8673ffffffffffffffffffffffffffffffffffffffff16848787604051610b55929190611230565b5f6040518083038185875af1925050503d805f8114610b8f576040519150601f19603f3d011682016040523d82523d5f602084013e610b94565b606091505b509150915081610bdb57806040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152600401610bd29190611015565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff167fc96720f35dd524e76ea92971ce13d08e9a17816bf3b0008a7083e6032354ebb5878784604051610c2593929190611274565b60405180910390a28092505050949350505050565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f840112610c6357610c62610c42565b5b8235905067ffffffffffffffff811115610c8057610c7f610c46565b5b602083019150836020820283011115610c9c57610c9b610c4a565b5b9250929050565b5f5f83601f840112610cb857610cb7610c42565b5b8235905067ffffffffffffffff811115610cd557610cd4610c46565b5b602083019150836020820283011115610cf157610cf0610c4a565b5b9250929050565b5f5f5f5f60408587031215610d1057610d0f610c3a565b5b5f85013567ffffffffffffffff811115610d2d57610d2c610c3e565b5b610d3987828801610c4e565b9450945050602085013567ffffffffffffffff811115610d5c57610d5b610c3e565b5b610d6887828801610ca3565b925092505092959194509250565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610de182610d9f565b610deb8185610da9565b9350610dfb818560208601610db9565b610e0481610dc7565b840191505092915050565b5f610e1a8383610dd7565b905092915050565b5f602082019050919050565b5f610e3882610d76565b610e428185610d80565b935083602082028501610e5485610d90565b805f5b85811015610e8f5784840389528151610e708582610e0f565b9450610e7b83610e22565b925060208a01995050600181019050610e57565b50829750879550505050505092915050565b5f6020820190508181035f830152610eb98184610e2e565b905092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610eea82610ec1565b9050919050565b610efa81610ee0565b8114610f04575f5ffd5b50565b5f81359050610f1581610ef1565b92915050565b5f5f83601f840112610f3057610f2f610c42565b5b8235905067ffffffffffffffff811115610f4d57610f4c610c46565b5b602083019150836001820283011115610f6957610f68610c4a565b5b9250929050565b5f5f5f60408486031215610f8757610f86610c3a565b5b5f610f9486828701610f07565b935050602084013567ffffffffffffffff811115610fb557610fb4610c3e565b5b610fc186828701610f1b565b92509250509250925092565b5f82825260208201905092915050565b5f610fe782610d9f565b610ff18185610fcd565b9350611001818560208601610db9565b61100a81610dc7565b840191505092915050565b5f6020820190508181035f83015261102d8184610fdd565b905092915050565b61103e81610ee0565b82525050565b5f6020820190506110575f830184611035565b92915050565b5f6020828403121561107257611071610c3a565b5b5f61107f84828501610f07565b91505092915050565b5f819050919050565b61109a81611088565b81146110a4575f5ffd5b50565b5f813590506110b581611091565b92915050565b5f5f5f5f606085870312156110d3576110d2610c3a565b5b5f6110e087828801610f07565b945050602085013567ffffffffffffffff81111561110157611100610c3e565b5b61110d87828801610f1b565b93509350506040611120878288016110a7565b91505092959194509250565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f833560016020038436030381126111ae576111ad611186565b5b80840192508235915067ffffffffffffffff8211156111d0576111cf61118a565b5b6020830192506001820236038313156111ec576111eb61118e565b5b509250929050565b5f81905092915050565b828183375f83830152505050565b5f61121783856111f4565b93506112248385846111fe565b82840190509392505050565b5f61123c82848661120c565b91508190509392505050565b5f6112538385610fcd565b93506112608385846111fe565b61126983610dc7565b840190509392505050565b5f6040820190508181035f83015261128d818587611248565b905081810360208301526112a18184610fdd565b905094935050505056"); -/// Build a genesis alloc entry for `AdminProxy`. -pub(crate) fn build(config: &AdminProxyConfig) -> GenesisContract { - let address = config.address.expect("address required for genesis"); - let mut storage = BTreeMap::new(); - - // Slot 0: owner (address left-padded to 32 bytes) - let owner_value = B256::from(U256::from_be_bytes(config.owner.into_word().0)); - storage.insert(B256::ZERO, owner_value); - - GenesisContract { - address, - code: Bytes::from_static(ADMIN_PROXY_BYTECODE), - storage, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::address; - use std::{path::PathBuf, process::Command}; - - #[test] - fn golden_admin_proxy_storage() { - let config = AdminProxyConfig { - address: Some(address!("000000000000000000000000000000000000Ad00")), - owner: address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), - }; - let contract = build(&config); - - let expected_slot0: B256 = - "0x000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - .parse() - .unwrap(); - assert_eq!(contract.storage[&B256::ZERO], expected_slot0); - } - - #[test] - #[ignore = "requires forge CLI"] - fn admin_proxy_bytecode_matches_solidity_source() { - let contracts_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .ancestors() - .nth(2) - .unwrap() - .join("contracts"); - - let output = Command::new("forge") - .args(["inspect", "AdminProxy", "deployedBytecode", "--root"]) - .arg(&contracts_root) - .output() - .expect("forge not found"); - - assert!( - output.status.success(), - "forge inspect failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - - let forge_hex = String::from_utf8(output.stdout) - .unwrap() - .trim() - .trim_start_matches("0x") - .to_lowercase(); - - let hardcoded_hex = hex::encode(ADMIN_PROXY_BYTECODE); - - assert_eq!( - forge_hex, hardcoded_hex, - "AdminProxy bytecode mismatch! Update the constant with: cd contracts && forge inspect AdminProxy deployedBytecode" - ); - } -} diff --git a/bin/ev-deployer/src/contracts/deterministic_deployer.rs b/bin/ev-deployer/src/contracts/deterministic_deployer.rs deleted file mode 100644 index 0432ac4f..00000000 --- a/bin/ev-deployer/src/contracts/deterministic_deployer.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Deterministic deployer (Nick's factory) bytecode for genesis injection. - -use crate::{config::DeterministicDeployerConfig, contracts::GenesisContract}; -use alloy_primitives::{hex, Bytes}; -use std::collections::BTreeMap; - -/// Runtime bytecode of the deterministic deployer factory from Ethereum mainnet. -/// See: -pub(crate) const DETERMINISTIC_DEPLOYER_BYTECODE: &[u8] = &hex!( - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b80825250506014600cf3" -); - -/// Build a genesis alloc entry for the deterministic deployer. -pub(crate) const fn build(config: &DeterministicDeployerConfig) -> GenesisContract { - let address = config.address.expect("address required for genesis"); - - GenesisContract { - address, - code: Bytes::from_static(DETERMINISTIC_DEPLOYER_BYTECODE), - storage: BTreeMap::new(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::address; - - #[test] - fn build_produces_correct_bytecode() { - let config = DeterministicDeployerConfig { - address: Some(address!("4e59b44847b379578588920ca78fbf26c0b4956c")), - }; - let contract = build(&config); - - assert_eq!( - contract.address, - address!("4e59b44847b379578588920ca78fbf26c0b4956c") - ); - assert_eq!(contract.code.as_ref(), DETERMINISTIC_DEPLOYER_BYTECODE); - assert!(contract.storage.is_empty()); - } -} diff --git a/bin/ev-deployer/src/contracts/immutables.rs b/bin/ev-deployer/src/contracts/immutables.rs deleted file mode 100644 index 09341047..00000000 --- a/bin/ev-deployer/src/contracts/immutables.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Bytecode patching for Solidity immutable variables. -//! -//! Solidity `immutable` values are embedded in the **runtime bytecode** by the -//! compiler, not in storage. When compiling with placeholder values (e.g. -//! `address(0)`, `uint32(0)`), the compiler leaves zero-filled regions at known -//! byte offsets. This module replaces those regions with the actual values from -//! the deploy config at genesis-generation time. - -use alloy_primitives::{B256, U256}; - -/// A single immutable reference inside a bytecode blob. -#[derive(Debug, Clone, Copy)] -pub(crate) struct ImmutableRef { - /// Byte offset into the **runtime** bytecode. - pub start: usize, - /// Number of bytes (always 32 for EVM words). - pub length: usize, -} - -/// Patch a mutable bytecode slice, writing `value` at every listed offset. -/// -/// # Panics -/// -/// Panics if any reference extends past the end of `bytecode`. -pub(crate) fn patch_bytes(bytecode: &mut [u8], refs: &[ImmutableRef], value: &[u8; 32]) { - for r in refs { - assert!( - r.start + r.length <= bytecode.len(), - "immutable ref out of bounds: start={} length={} bytecode_len={}", - r.start, - r.length, - bytecode.len() - ); - bytecode[r.start..r.start + r.length].copy_from_slice(value); - } -} - -/// Convenience: patch with an ABI-encoded `uint256`. -pub(crate) fn patch_u256(bytecode: &mut [u8], refs: &[ImmutableRef], val: U256) { - let word = B256::from(val); - patch_bytes(bytecode, refs, &word.0); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn patch_single_ref() { - let mut bytecode = vec![0u8; 64]; - let refs = [ImmutableRef { - start: 10, - length: 32, - }]; - let value = B256::from(U256::from(42u64)); - patch_bytes(&mut bytecode, &refs, &value.0); - - assert_eq!(bytecode[41], 42); - // bytes before are untouched - assert_eq!(bytecode[9], 0); - // bytes after are untouched - assert_eq!(bytecode[42], 0); - } - - #[test] - #[should_panic(expected = "immutable ref out of bounds")] - fn patch_out_of_bounds_panics() { - let mut bytecode = vec![0u8; 16]; - let refs = [ImmutableRef { - start: 0, - length: 32, - }]; - let value = [0u8; 32]; - patch_bytes(&mut bytecode, &refs, &value); - } -} diff --git a/bin/ev-deployer/src/contracts/mod.rs b/bin/ev-deployer/src/contracts/mod.rs deleted file mode 100644 index 37a47478..00000000 --- a/bin/ev-deployer/src/contracts/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Contract bytecode and storage encoding. - -pub(crate) mod admin_proxy; -pub(crate) mod deterministic_deployer; -pub(crate) mod immutables; -pub(crate) mod permit2; - -use alloy_primitives::{Address, Bytes, B256}; -use std::collections::BTreeMap; - -/// A contract ready to be placed in genesis alloc. -#[derive(Debug)] -pub struct GenesisContract { - /// The address to deploy at. - pub address: Address, - /// Runtime bytecode. - pub code: Bytes, - /// Storage slot values. - pub storage: BTreeMap, -} diff --git a/bin/ev-deployer/src/contracts/permit2.rs b/bin/ev-deployer/src/contracts/permit2.rs deleted file mode 100644 index 9e66e6ba..00000000 --- a/bin/ev-deployer/src/contracts/permit2.rs +++ /dev/null @@ -1,253 +0,0 @@ -//! `Permit2` bytecode and immutable encoding. -//! -//! Uniswap's `Permit2` provides gas-efficient token approval management -//! via signature-based permits (EIP-2612 style) for any ERC-20 token. -//! -//! ## Immutables (in bytecode, not storage) -//! -//! | Variable | Type | Offset | -//! |-----------------------------|---------|--------| -//! | `_CACHED_CHAIN_ID` | uint256 | \[6945\] | -//! | `_CACHED_DOMAIN_SEPARATOR` | bytes32 | \[6983\] | -//! -//! Both come from the EIP-712 base contract (`src/EIP712.sol`). The -//! constructor normally caches `block.chainid` and the resulting domain -//! separator so that `DOMAIN_SEPARATOR()` can skip recomputation when the -//! chain ID hasn't changed. For a genesis deploy we patch the correct -//! values directly into the bytecode. -//! -//! ## Storage layout -//! -//! Permit2 has no storage that needs initialization at genesis. All state -//! (allowances, nonces, …) starts at zero. - -use crate::{ - config::Permit2Config, - contracts::{ - immutables::{patch_bytes, patch_u256, ImmutableRef}, - GenesisContract, - }, -}; -use alloy_primitives::{hex, keccak256, Address, Bytes, B256, U256}; -use std::collections::BTreeMap; - -/// `Permit2` runtime bytecode compiled from Uniswap/permit2 (commit cc56ad0) -/// with solc 0.8.17 (via-ir, optimizer `1_000_000` runs, `bytecode_hash="none"`). -/// -/// Compiled with placeholder immutables (all zeros). Actual values are patched -/// at genesis time via [`build`]. -/// -/// Regenerate with: -/// ```sh -/// cd contracts/lib/permit2 && forge inspect Permit2 deployedBytecode -/// ``` -pub(crate) const PERMIT2_BYTECODE: &[u8] = &hex!("6040608081526004908136101561001557600080fd5b600090813560e01c80630d58b1db1461126c578063137c29fe146110755780632a2d80d114610db75780632b67b57014610bde57806330f28b7a14610ade5780633644e51514610a9d57806336c7851614610a285780633ff9dcb1146109a85780634fe02b441461093f57806365d9723c146107ac57806387517c451461067a578063927da105146105c3578063cc53287f146104a3578063edd9444b1461033a5763fe8ec1a7146100c657600080fd5b346103365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff833581811161033257610114903690860161164b565b60243582811161032e5761012b903690870161161a565b6101336114e6565b9160843585811161032a5761014b9036908a016115c1565b98909560a43590811161032657610164913691016115c1565b969095815190610173826113ff565b606b82527f5065726d697442617463685769746e6573735472616e7366657246726f6d285460208301527f6f6b656e5065726d697373696f6e735b5d207065726d69747465642c61646472838301527f657373207370656e6465722c75696e74323536206e6f6e63652c75696e74323560608301527f3620646561646c696e652c000000000000000000000000000000000000000000608083015282519a8b9181610222602085018096611f93565b918237018a8152039961025b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09b8c8101835282611437565b5190209085515161026b81611ebb565b908a5b8181106102f95750506102f6999a6102ed9183516102a081610294602082018095611f66565b03848101835282611437565b519020602089810151858b015195519182019687526040820192909252336060820152608081019190915260a081019390935260643560c08401528260e081015b03908101835282611437565b51902093611cf7565b80f35b8061031161030b610321938c5161175e565b51612054565b61031b828661175e565b52611f0a565b61026e565b8880fd5b8780fd5b8480fd5b8380fd5b5080fd5b5091346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff9080358281116103325761038b903690830161164b565b60243583811161032e576103a2903690840161161a565b9390926103ad6114e6565b9160643590811161049f576103c4913691016115c1565b949093835151976103d489611ebb565b98885b81811061047d5750506102f697988151610425816103f9602082018095611f66565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611437565b5190206020860151828701519083519260208401947ffcf35f5ac6a2c28868dc44c302166470266239195f02b0ee408334829333b7668652840152336060840152608083015260a082015260a081526102ed8161141b565b808b61031b8261049461030b61049a968d5161175e565b9261175e565b6103d7565b8680fd5b5082346105bf57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103325780359067ffffffffffffffff821161032e576104f49136910161161a565b929091845b848110610504578580f35b8061051a610515600193888861196c565b61197c565b61052f84610529848a8a61196c565b0161197c565b3389528385528589209173ffffffffffffffffffffffffffffffffffffffff80911692838b528652868a20911690818a5285528589207fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558551918252848201527f89b1add15eff56b3dfe299ad94e01f2b52fbcb80ae1a3baea6ae8c04cb2b98a4853392a2016104f9565b8280fd5b50346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610676816105ff6114a0565b936106086114c3565b6106106114e6565b73ffffffffffffffffffffffffffffffffffffffff968716835260016020908152848420928816845291825283832090871683528152919020549251938316845260a083901c65ffffffffffff169084015260d09190911c604083015281906060820190565b0390f35b50346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336576106b26114a0565b906106bb6114c3565b916106c46114e6565b65ffffffffffff926064358481169081810361032a5779ffffffffffff0000000000000000000000000000000000000000947fda9fa7c1b00402c17d0161b249b1ab8bbec047c5a52207b9c112deffd817036b94338a5260016020527fffffffffffff0000000000000000000000000000000000000000000000000000858b209873ffffffffffffffffffffffffffffffffffffffff809416998a8d5260205283878d209b169a8b8d52602052868c209486156000146107a457504216925b8454921697889360a01b16911617179055815193845260208401523392a480f35b905092610783565b5082346105bf5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576107e56114a0565b906107ee6114c3565b9265ffffffffffff604435818116939084810361032a57338852602091600183528489209673ffffffffffffffffffffffffffffffffffffffff80911697888b528452858a20981697888a5283528489205460d01c93848711156109175761ffff9085840316116108f05750907f55eb90d810e1700b35a8e7e25395ff7f2b2259abd7415ca2284dfb1c246418f393929133895260018252838920878a528252838920888a5282528389209079ffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffff000000000000000000000000000000000000000000000000000083549260d01b16911617905582519485528401523392a480f35b84517f24d35a26000000000000000000000000000000000000000000000000000000008152fd5b5084517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b503461033657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336578060209273ffffffffffffffffffffffffffffffffffffffff61098f6114a0565b1681528084528181206024358252845220549051908152f35b5082346105bf57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf577f3704902f963766a4e561bbaab6e6cdc1b1dd12f6e9e99648da8843b3f46b918d90359160243533855284602052818520848652602052818520818154179055815193845260208401523392a280f35b8234610a9a5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a9a57610a606114a0565b610a686114c3565b610a706114e6565b6064359173ffffffffffffffffffffffffffffffffffffffff8316830361032e576102f6936117a1565b80fd5b503461033657817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657602090610ad7611b1e565b9051908152f35b508290346105bf576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf57610b1a3661152a565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261033257610b4c611478565b9160e43567ffffffffffffffff8111610bda576102f694610b6f913691016115c1565b939092610b7c8351612054565b6020840151828501519083519260208401947f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068652840152336060840152608083015260a082015260a08152610bd18161141b565b51902091611c25565b8580fd5b509134610336576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610c186114a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360160c08112610332576080855191610c51836113e3565b1261033257845190610c6282611398565b73ffffffffffffffffffffffffffffffffffffffff91602435838116810361049f578152604435838116810361049f57602082015265ffffffffffff606435818116810361032a5788830152608435908116810361049f576060820152815260a435938285168503610bda576020820194855260c4359087830182815260e43567ffffffffffffffff811161032657610cfe90369084016115c1565b929093804211610d88575050918591610d786102f6999a610d7e95610d238851611fbe565b90898c511690519083519260208401947ff3841cd1ff0085026a6327b620b67997ce40f282c88a8e905a7a5626e310f3d086528401526060830152608082015260808152610d70816113ff565b519020611bd9565b916120c7565b519251169161199d565b602492508a51917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b5091346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc93818536011261033257610df36114a0565b9260249081359267ffffffffffffffff9788851161032a578590853603011261049f578051978589018981108282111761104a578252848301358181116103265785019036602383011215610326578382013591610e50836115ef565b90610e5d85519283611437565b838252602093878584019160071b83010191368311611046578801905b828210610fe9575050508a526044610e93868801611509565b96838c01978852013594838b0191868352604435908111610fe557610ebb90369087016115c1565b959096804211610fba575050508998995151610ed681611ebb565b908b5b818110610f9757505092889492610d7892610f6497958351610f02816103f98682018095611f66565b5190209073ffffffffffffffffffffffffffffffffffffffff9a8b8b51169151928551948501957faf1b0d30d2cab0380e68f0689007e3254993c596f2fdd0aaa7f4d04f794408638752850152830152608082015260808152610d70816113ff565b51169082515192845b848110610f78578580f35b80610f918585610f8b600195875161175e565b5161199d565b01610f6d565b80610311610fac8e9f9e93610fb2945161175e565b51611fbe565b9b9a9b610ed9565b8551917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b8a80fd5b6080823603126110465785608091885161100281611398565b61100b85611509565b8152611018838601611509565b838201526110278a8601611607565b8a8201528d611037818701611607565b90820152815201910190610e7a565b8c80fd5b84896041867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5082346105bf576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576110b03661152a565b91807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c360112610332576110e2611478565b67ffffffffffffffff93906101043585811161049f5761110590369086016115c1565b90936101243596871161032a57611125610bd1966102f6983691016115c1565b969095825190611134826113ff565b606482527f5065726d69745769746e6573735472616e7366657246726f6d28546f6b656e5060208301527f65726d697373696f6e73207065726d69747465642c6164647265737320737065848301527f6e6465722c75696e74323536206e6f6e63652c75696e7432353620646561646c60608301527f696e652c0000000000000000000000000000000000000000000000000000000060808301528351948591816111e3602085018096611f93565b918237018b8152039361121c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868101835282611437565b5190209261122a8651612054565b6020878101518589015195519182019687526040820192909252336060820152608081019190915260a081019390935260e43560c08401528260e081016102e1565b5082346105bf576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033257813567ffffffffffffffff92838211610bda5736602383011215610bda5781013592831161032e576024906007368386831b8401011161049f57865b8581106112e5578780f35b80821b83019060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83360301126103265761139288876001946060835161132c81611398565b611368608461133c8d8601611509565b9485845261134c60448201611509565b809785015261135d60648201611509565b809885015201611509565b918291015273ffffffffffffffffffffffffffffffffffffffff80808093169516931691166117a1565b016112da565b6080810190811067ffffffffffffffff8211176113b457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176113b457604052565b60a0810190811067ffffffffffffffff8211176113b457604052565b60c0810190811067ffffffffffffffff8211176113b457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113b457604052565b60c4359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01906080821261149b576040805190611563826113e3565b8082941261149b57805181810181811067ffffffffffffffff8211176113b457825260043573ffffffffffffffffffffffffffffffffffffffff8116810361149b578152602435602082015282526044356020830152606435910152565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b67ffffffffffffffff81116113b45760051b60200190565b359065ffffffffffff8216820361149b57565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020808501948460061b01011161149b57565b91909160608184031261149b576040805191611666836113e3565b8294813567ffffffffffffffff9081811161149b57830182601f8201121561149b578035611693816115ef565b926116a087519485611437565b818452602094858086019360061b8501019381851161149b579086899897969594939201925b8484106116e3575050505050855280820135908501520135910152565b90919293949596978483031261149b578851908982019082821085831117611730578a928992845261171487611509565b81528287013583820152815201930191908897969594936116c6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80518210156117725760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b92919273ffffffffffffffffffffffffffffffffffffffff604060008284168152600160205282828220961695868252602052818120338252602052209485549565ffffffffffff8760a01c16804211611884575082871696838803611812575b5050611810955016926118b5565b565b878484161160001461184f57602488604051907ff96fb0710000000000000000000000000000000000000000000000000000000082526004820152fd5b7fffffffffffffffffffffffff000000000000000000000000000000000000000084846118109a031691161790553880611802565b602490604051907fd81b2f2e0000000000000000000000000000000000000000000000000000000082526004820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d116001600051141617161561190e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b91908110156117725760061b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361149b5790565b9065ffffffffffff908160608401511673ffffffffffffffffffffffffffffffffffffffff908185511694826020820151169280866040809401511695169560009187835260016020528383208984526020528383209916988983526020528282209184835460d01c03611af5579185611ace94927fc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec98979694508715600014611ad35779ffffffffffff00000000000000000000000000000000000000009042165b60a01b167fffffffffffff00000000000000000000000000000000000000000000000000006001860160d01b1617179055519384938491604091949373ffffffffffffffffffffffffffffffffffffffff606085019616845265ffffffffffff809216602085015216910152565b0390a4565b5079ffffffffffff000000000000000000000000000000000000000087611a60565b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b467f000000000000000000000000000000000000000000000000000000000000000003611b69577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86682527f9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a604082015246606082015230608082015260808152611bd3816113ff565b51902090565b611be1611b1e565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611bd381611398565b9192909360a435936040840151804211611cc65750602084510151808611611c955750918591610d78611c6594611c60602088015186611e47565b611bd9565b73ffffffffffffffffffffffffffffffffffffffff809151511692608435918216820361149b57611810936118b5565b602490604051907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b602490604051907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b959093958051519560409283830151804211611e175750848803611dee57611d2e918691610d7860209b611c608d88015186611e47565b60005b868110611d42575050505050505050565b611d4d81835161175e565b5188611d5a83878a61196c565b01359089810151808311611dbe575091818888886001968596611d84575b50505050505001611d31565b611db395611dad9273ffffffffffffffffffffffffffffffffffffffff6105159351169561196c565b916118b5565b803888888883611d78565b6024908651907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b600484517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6024908551907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b9073ffffffffffffffffffffffffffffffffffffffff600160ff83161b9216600052600060205260406000209060081c6000526020526040600020818154188091551615611e9157565b60046040517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b90611ec5826115ef565b611ed26040519182611437565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611f0082946115ef565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611f375760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160208092019160005b828110611f7f575050505090565b835185529381019392810192600101611f71565b9081519160005b838110611fab575050016000815290565b8060208092840101518185015201611f9a565b60405160208101917f65626cad6cb96493bf6f5ebea28756c966f023ab9e8a83a7101849d5573b3678835273ffffffffffffffffffffffffffffffffffffffff8082511660408401526020820151166060830152606065ffffffffffff9182604082015116608085015201511660a082015260a0815260c0810181811067ffffffffffffffff8211176113b45760405251902090565b6040516020808201927f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1845273ffffffffffffffffffffffffffffffffffffffff81511660408401520151606082015260608152611bd381611398565b919082604091031261149b576020823592013590565b6000843b61222e5750604182036121ac576120e4828201826120b1565b939092604010156117725760209360009360ff6040608095013560f81c5b60405194855216868401526040830152606082015282805260015afa156121a05773ffffffffffffffffffffffffffffffffffffffff806000511691821561217657160361214c57565b60046040517f815e1d64000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6040513d6000823e3d90fd5b60408203612204576121c0918101906120b1565b91601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84169360ff1c019060ff8211611f375760209360009360ff608094612102565b60046040517f4be6321b000000000000000000000000000000000000000000000000000000008152fd5b929391601f928173ffffffffffffffffffffffffffffffffffffffff60646020957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051988997889687947f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8752600487015260406024870152816044870152868601378b85828601015201168101030192165afa9081156123a857829161232a575b507fffffffff000000000000000000000000000000000000000000000000000000009150160361230057565b60046040517fb0669cbc000000000000000000000000000000000000000000000000000000008152fd5b90506020813d82116123a0575b8161234460209383611437565b810103126103365751907fffffffff0000000000000000000000000000000000000000000000000000000082168203610a9a57507fffffffff0000000000000000000000000000000000000000000000000000000090386122d4565b3d9150612337565b6040513d84823e3d90fdfea164736f6c6343000811000a"); - -/// `Permit2` creation bytecode (initcode) compiled from Uniswap/permit2 (commit cc56ad0) -/// with solc 0.8.17 (via-ir, optimizer `1_000_000` runs, `bytecode_hash="none"`). -/// No constructor arguments needed. -/// Regenerate with: `cd contracts/lib/permit2 && forge inspect Permit2 bytecode` -pub(crate) const PERMIT2_INITCODE: &[u8] = &hex!("60c0346100bb574660a052602081017f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681527f9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a60408301524660608301523060808301526080825260a082019180831060018060401b038411176100a557826040525190206080526123c090816100c1823960805181611b47015260a05181611b210152f35b634e487b7160e01b600052604160045260246000fd5b600080fdfe6040608081526004908136101561001557600080fd5b600090813560e01c80630d58b1db1461126c578063137c29fe146110755780632a2d80d114610db75780632b67b57014610bde57806330f28b7a14610ade5780633644e51514610a9d57806336c7851614610a285780633ff9dcb1146109a85780634fe02b441461093f57806365d9723c146107ac57806387517c451461067a578063927da105146105c3578063cc53287f146104a3578063edd9444b1461033a5763fe8ec1a7146100c657600080fd5b346103365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff833581811161033257610114903690860161164b565b60243582811161032e5761012b903690870161161a565b6101336114e6565b9160843585811161032a5761014b9036908a016115c1565b98909560a43590811161032657610164913691016115c1565b969095815190610173826113ff565b606b82527f5065726d697442617463685769746e6573735472616e7366657246726f6d285460208301527f6f6b656e5065726d697373696f6e735b5d207065726d69747465642c61646472838301527f657373207370656e6465722c75696e74323536206e6f6e63652c75696e74323560608301527f3620646561646c696e652c000000000000000000000000000000000000000000608083015282519a8b9181610222602085018096611f93565b918237018a8152039961025b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09b8c8101835282611437565b5190209085515161026b81611ebb565b908a5b8181106102f95750506102f6999a6102ed9183516102a081610294602082018095611f66565b03848101835282611437565b519020602089810151858b015195519182019687526040820192909252336060820152608081019190915260a081019390935260643560c08401528260e081015b03908101835282611437565b51902093611cf7565b80f35b8061031161030b610321938c5161175e565b51612054565b61031b828661175e565b52611f0a565b61026e565b8880fd5b8780fd5b8480fd5b8380fd5b5080fd5b5091346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff9080358281116103325761038b903690830161164b565b60243583811161032e576103a2903690840161161a565b9390926103ad6114e6565b9160643590811161049f576103c4913691016115c1565b949093835151976103d489611ebb565b98885b81811061047d5750506102f697988151610425816103f9602082018095611f66565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611437565b5190206020860151828701519083519260208401947ffcf35f5ac6a2c28868dc44c302166470266239195f02b0ee408334829333b7668652840152336060840152608083015260a082015260a081526102ed8161141b565b808b61031b8261049461030b61049a968d5161175e565b9261175e565b6103d7565b8680fd5b5082346105bf57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103325780359067ffffffffffffffff821161032e576104f49136910161161a565b929091845b848110610504578580f35b8061051a610515600193888861196c565b61197c565b61052f84610529848a8a61196c565b0161197c565b3389528385528589209173ffffffffffffffffffffffffffffffffffffffff80911692838b528652868a20911690818a5285528589207fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558551918252848201527f89b1add15eff56b3dfe299ad94e01f2b52fbcb80ae1a3baea6ae8c04cb2b98a4853392a2016104f9565b8280fd5b50346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610676816105ff6114a0565b936106086114c3565b6106106114e6565b73ffffffffffffffffffffffffffffffffffffffff968716835260016020908152848420928816845291825283832090871683528152919020549251938316845260a083901c65ffffffffffff169084015260d09190911c604083015281906060820190565b0390f35b50346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336576106b26114a0565b906106bb6114c3565b916106c46114e6565b65ffffffffffff926064358481169081810361032a5779ffffffffffff0000000000000000000000000000000000000000947fda9fa7c1b00402c17d0161b249b1ab8bbec047c5a52207b9c112deffd817036b94338a5260016020527fffffffffffff0000000000000000000000000000000000000000000000000000858b209873ffffffffffffffffffffffffffffffffffffffff809416998a8d5260205283878d209b169a8b8d52602052868c209486156000146107a457504216925b8454921697889360a01b16911617179055815193845260208401523392a480f35b905092610783565b5082346105bf5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576107e56114a0565b906107ee6114c3565b9265ffffffffffff604435818116939084810361032a57338852602091600183528489209673ffffffffffffffffffffffffffffffffffffffff80911697888b528452858a20981697888a5283528489205460d01c93848711156109175761ffff9085840316116108f05750907f55eb90d810e1700b35a8e7e25395ff7f2b2259abd7415ca2284dfb1c246418f393929133895260018252838920878a528252838920888a5282528389209079ffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffff000000000000000000000000000000000000000000000000000083549260d01b16911617905582519485528401523392a480f35b84517f24d35a26000000000000000000000000000000000000000000000000000000008152fd5b5084517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b503461033657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336578060209273ffffffffffffffffffffffffffffffffffffffff61098f6114a0565b1681528084528181206024358252845220549051908152f35b5082346105bf57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf577f3704902f963766a4e561bbaab6e6cdc1b1dd12f6e9e99648da8843b3f46b918d90359160243533855284602052818520848652602052818520818154179055815193845260208401523392a280f35b8234610a9a5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a9a57610a606114a0565b610a686114c3565b610a706114e6565b6064359173ffffffffffffffffffffffffffffffffffffffff8316830361032e576102f6936117a1565b80fd5b503461033657817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657602090610ad7611b1e565b9051908152f35b508290346105bf576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf57610b1a3661152a565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261033257610b4c611478565b9160e43567ffffffffffffffff8111610bda576102f694610b6f913691016115c1565b939092610b7c8351612054565b6020840151828501519083519260208401947f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068652840152336060840152608083015260a082015260a08152610bd18161141b565b51902091611c25565b8580fd5b509134610336576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610c186114a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360160c08112610332576080855191610c51836113e3565b1261033257845190610c6282611398565b73ffffffffffffffffffffffffffffffffffffffff91602435838116810361049f578152604435838116810361049f57602082015265ffffffffffff606435818116810361032a5788830152608435908116810361049f576060820152815260a435938285168503610bda576020820194855260c4359087830182815260e43567ffffffffffffffff811161032657610cfe90369084016115c1565b929093804211610d88575050918591610d786102f6999a610d7e95610d238851611fbe565b90898c511690519083519260208401947ff3841cd1ff0085026a6327b620b67997ce40f282c88a8e905a7a5626e310f3d086528401526060830152608082015260808152610d70816113ff565b519020611bd9565b916120c7565b519251169161199d565b602492508a51917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b5091346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc93818536011261033257610df36114a0565b9260249081359267ffffffffffffffff9788851161032a578590853603011261049f578051978589018981108282111761104a578252848301358181116103265785019036602383011215610326578382013591610e50836115ef565b90610e5d85519283611437565b838252602093878584019160071b83010191368311611046578801905b828210610fe9575050508a526044610e93868801611509565b96838c01978852013594838b0191868352604435908111610fe557610ebb90369087016115c1565b959096804211610fba575050508998995151610ed681611ebb565b908b5b818110610f9757505092889492610d7892610f6497958351610f02816103f98682018095611f66565b5190209073ffffffffffffffffffffffffffffffffffffffff9a8b8b51169151928551948501957faf1b0d30d2cab0380e68f0689007e3254993c596f2fdd0aaa7f4d04f794408638752850152830152608082015260808152610d70816113ff565b51169082515192845b848110610f78578580f35b80610f918585610f8b600195875161175e565b5161199d565b01610f6d565b80610311610fac8e9f9e93610fb2945161175e565b51611fbe565b9b9a9b610ed9565b8551917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b8a80fd5b6080823603126110465785608091885161100281611398565b61100b85611509565b8152611018838601611509565b838201526110278a8601611607565b8a8201528d611037818701611607565b90820152815201910190610e7a565b8c80fd5b84896041867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5082346105bf576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576110b03661152a565b91807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c360112610332576110e2611478565b67ffffffffffffffff93906101043585811161049f5761110590369086016115c1565b90936101243596871161032a57611125610bd1966102f6983691016115c1565b969095825190611134826113ff565b606482527f5065726d69745769746e6573735472616e7366657246726f6d28546f6b656e5060208301527f65726d697373696f6e73207065726d69747465642c6164647265737320737065848301527f6e6465722c75696e74323536206e6f6e63652c75696e7432353620646561646c60608301527f696e652c0000000000000000000000000000000000000000000000000000000060808301528351948591816111e3602085018096611f93565b918237018b8152039361121c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868101835282611437565b5190209261122a8651612054565b6020878101518589015195519182019687526040820192909252336060820152608081019190915260a081019390935260e43560c08401528260e081016102e1565b5082346105bf576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033257813567ffffffffffffffff92838211610bda5736602383011215610bda5781013592831161032e576024906007368386831b8401011161049f57865b8581106112e5578780f35b80821b83019060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83360301126103265761139288876001946060835161132c81611398565b611368608461133c8d8601611509565b9485845261134c60448201611509565b809785015261135d60648201611509565b809885015201611509565b918291015273ffffffffffffffffffffffffffffffffffffffff80808093169516931691166117a1565b016112da565b6080810190811067ffffffffffffffff8211176113b457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176113b457604052565b60a0810190811067ffffffffffffffff8211176113b457604052565b60c0810190811067ffffffffffffffff8211176113b457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113b457604052565b60c4359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01906080821261149b576040805190611563826113e3565b8082941261149b57805181810181811067ffffffffffffffff8211176113b457825260043573ffffffffffffffffffffffffffffffffffffffff8116810361149b578152602435602082015282526044356020830152606435910152565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b67ffffffffffffffff81116113b45760051b60200190565b359065ffffffffffff8216820361149b57565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020808501948460061b01011161149b57565b91909160608184031261149b576040805191611666836113e3565b8294813567ffffffffffffffff9081811161149b57830182601f8201121561149b578035611693816115ef565b926116a087519485611437565b818452602094858086019360061b8501019381851161149b579086899897969594939201925b8484106116e3575050505050855280820135908501520135910152565b90919293949596978483031261149b578851908982019082821085831117611730578a928992845261171487611509565b81528287013583820152815201930191908897969594936116c6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80518210156117725760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b92919273ffffffffffffffffffffffffffffffffffffffff604060008284168152600160205282828220961695868252602052818120338252602052209485549565ffffffffffff8760a01c16804211611884575082871696838803611812575b5050611810955016926118b5565b565b878484161160001461184f57602488604051907ff96fb0710000000000000000000000000000000000000000000000000000000082526004820152fd5b7fffffffffffffffffffffffff000000000000000000000000000000000000000084846118109a031691161790553880611802565b602490604051907fd81b2f2e0000000000000000000000000000000000000000000000000000000082526004820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d116001600051141617161561190e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b91908110156117725760061b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361149b5790565b9065ffffffffffff908160608401511673ffffffffffffffffffffffffffffffffffffffff908185511694826020820151169280866040809401511695169560009187835260016020528383208984526020528383209916988983526020528282209184835460d01c03611af5579185611ace94927fc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec98979694508715600014611ad35779ffffffffffff00000000000000000000000000000000000000009042165b60a01b167fffffffffffff00000000000000000000000000000000000000000000000000006001860160d01b1617179055519384938491604091949373ffffffffffffffffffffffffffffffffffffffff606085019616845265ffffffffffff809216602085015216910152565b0390a4565b5079ffffffffffff000000000000000000000000000000000000000087611a60565b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b467f000000000000000000000000000000000000000000000000000000000000000003611b69577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86682527f9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a604082015246606082015230608082015260808152611bd3816113ff565b51902090565b611be1611b1e565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611bd381611398565b9192909360a435936040840151804211611cc65750602084510151808611611c955750918591610d78611c6594611c60602088015186611e47565b611bd9565b73ffffffffffffffffffffffffffffffffffffffff809151511692608435918216820361149b57611810936118b5565b602490604051907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b602490604051907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b959093958051519560409283830151804211611e175750848803611dee57611d2e918691610d7860209b611c608d88015186611e47565b60005b868110611d42575050505050505050565b611d4d81835161175e565b5188611d5a83878a61196c565b01359089810151808311611dbe575091818888886001968596611d84575b50505050505001611d31565b611db395611dad9273ffffffffffffffffffffffffffffffffffffffff6105159351169561196c565b916118b5565b803888888883611d78565b6024908651907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b600484517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6024908551907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b9073ffffffffffffffffffffffffffffffffffffffff600160ff83161b9216600052600060205260406000209060081c6000526020526040600020818154188091551615611e9157565b60046040517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b90611ec5826115ef565b611ed26040519182611437565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611f0082946115ef565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611f375760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160208092019160005b828110611f7f575050505090565b835185529381019392810192600101611f71565b9081519160005b838110611fab575050016000815290565b8060208092840101518185015201611f9a565b60405160208101917f65626cad6cb96493bf6f5ebea28756c966f023ab9e8a83a7101849d5573b3678835273ffffffffffffffffffffffffffffffffffffffff8082511660408401526020820151166060830152606065ffffffffffff9182604082015116608085015201511660a082015260a0815260c0810181811067ffffffffffffffff8211176113b45760405251902090565b6040516020808201927f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1845273ffffffffffffffffffffffffffffffffffffffff81511660408401520151606082015260608152611bd381611398565b919082604091031261149b576020823592013590565b6000843b61222e5750604182036121ac576120e4828201826120b1565b939092604010156117725760209360009360ff6040608095013560f81c5b60405194855216868401526040830152606082015282805260015afa156121a05773ffffffffffffffffffffffffffffffffffffffff806000511691821561217657160361214c57565b60046040517f815e1d64000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6040513d6000823e3d90fd5b60408203612204576121c0918101906120b1565b91601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84169360ff1c019060ff8211611f375760209360009360ff608094612102565b60046040517f4be6321b000000000000000000000000000000000000000000000000000000008152fd5b929391601f928173ffffffffffffffffffffffffffffffffffffffff60646020957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051988997889687947f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8752600487015260406024870152816044870152868601378b85828601015201168101030192165afa9081156123a857829161232a575b507fffffffff000000000000000000000000000000000000000000000000000000009150160361230057565b60046040517fb0669cbc000000000000000000000000000000000000000000000000000000008152fd5b90506020813d82116123a0575b8161234460209383611437565b810103126103365751907fffffffff0000000000000000000000000000000000000000000000000000000082168203610a9a57507fffffffff0000000000000000000000000000000000000000000000000000000090386122d4565b3d9150612337565b6040513d84823e3d90fdfea164736f6c6343000811000a"); - -// ── Immutable reference offsets (from compiled artifact `immutableReferences`) ── - -/// `_CACHED_CHAIN_ID` (uint256) — from `EIP712.sol`. -pub(crate) const CACHED_CHAIN_ID_REFS: &[ImmutableRef] = &[ImmutableRef { - start: 6945, - length: 32, -}]; - -/// `_CACHED_DOMAIN_SEPARATOR` (bytes32) — from `EIP712.sol`. -pub(crate) const CACHED_DOMAIN_SEPARATOR_REFS: &[ImmutableRef] = &[ImmutableRef { - start: 6983, - length: 32, -}]; - -/// EIP-712 type hash: `keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)")` -pub(crate) const EIP712_TYPE_HASH: B256 = B256::new(hex!( - "8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866" -)); - -/// `keccak256("Permit2")` -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: -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 { - let mut bytecode = PERMIT2_BYTECODE.to_vec(); - - let chain_id_u256 = U256::from(chain_id); - patch_u256(&mut bytecode, CACHED_CHAIN_ID_REFS, chain_id_u256); - - let mut buf = [0u8; 128]; - buf[0..32].copy_from_slice(EIP712_TYPE_HASH.as_slice()); - buf[32..64].copy_from_slice(HASHED_NAME.as_slice()); - buf[64..96].copy_from_slice(&B256::from(chain_id_u256).0); - buf[96..128].copy_from_slice(address.into_word().as_slice()); - let domain_separator = keccak256(buf); - patch_bytes( - &mut bytecode, - CACHED_DOMAIN_SEPARATOR_REFS, - &domain_separator.0, - ); - - bytecode -} - -/// Build a genesis alloc entry for `Permit2`. -pub(crate) fn build(config: &Permit2Config, chain_id: u64) -> GenesisContract { - let address = config.address.expect("address required for genesis"); - let bytecode = expected_runtime_bytecode(chain_id, address); - - GenesisContract { - address, - code: Bytes::from(bytecode), - storage: BTreeMap::new(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::{address, Address}; - use std::{path::PathBuf, process::Command}; - - fn test_config() -> Permit2Config { - Permit2Config { - address: Some(address!("000000000022D473030F116dDEE9F6B43aC78BA3")), - } - } - - #[test] - fn no_storage_entries() { - let contract = build(&test_config(), 1234); - assert!( - contract.storage.is_empty(), - "Permit2 should have no storage at genesis" - ); - } - - #[test] - fn bytecode_is_patched_with_chain_id() { - let contract = build(&test_config(), 42); - let code = contract.code.to_vec(); - let word = &code[6945..6945 + 32]; - assert_eq!(word[31], 42); - assert_eq!(word[0..31], [0u8; 31]); - } - - #[test] - fn bytecode_is_patched_with_domain_separator() { - let config = test_config(); - let chain_id: u64 = 1234; - let contract = build(&config, chain_id); - let code = contract.code.to_vec(); - - // Compute expected domain separator - let mut buf = [0u8; 128]; - buf[0..32].copy_from_slice(EIP712_TYPE_HASH.as_slice()); - buf[32..64].copy_from_slice(HASHED_NAME.as_slice()); - buf[64..96].copy_from_slice(&B256::from(U256::from(chain_id)).0); - buf[96..128].copy_from_slice(config.address.unwrap().into_word().as_slice()); - let expected = keccak256(buf); - - let word = &code[6983..6983 + 32]; - assert_eq!(word, expected.as_slice()); - } - - #[test] - fn domain_separator_changes_with_chain_id() { - let config = test_config(); - let c1 = build(&config, 1); - let c2 = build(&config, 2); - - let ds1 = &c1.code[6983..6983 + 32]; - let ds2 = &c2.code[6983..6983 + 32]; - assert_ne!( - ds1, ds2, - "different chain IDs should produce different domain separators" - ); - } - - #[test] - fn domain_separator_changes_with_address() { - let c1 = build( - &Permit2Config { - address: Some(Address::repeat_byte(0x01)), - }, - 1234, - ); - let c2 = build( - &Permit2Config { - address: Some(Address::repeat_byte(0x02)), - }, - 1234, - ); - - let ds1 = &c1.code[6983..6983 + 32]; - let ds2 = &c2.code[6983..6983 + 32]; - assert_ne!( - ds1, ds2, - "different addresses should produce different domain separators" - ); - } - - #[test] - fn eip712_constants_are_correct() { - // Verify the hardcoded constants match the expected values - assert_eq!( - EIP712_TYPE_HASH, - keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"), - ); - assert_eq!(HASHED_NAME, keccak256("Permit2")); - } - - #[test] - #[ignore = "requires forge CLI"] - fn permit2_bytecode_matches_solidity_source() { - let contracts_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .ancestors() - .nth(2) - .unwrap() - .join("contracts") - .join("lib") - .join("permit2"); - - let output = Command::new("forge") - .args(["inspect", "Permit2", "deployedBytecode", "--root"]) - .arg(&contracts_root) - .output() - .expect("forge not found"); - - assert!( - output.status.success(), - "forge inspect failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - - let forge_hex = String::from_utf8(output.stdout) - .unwrap() - .trim() - .strip_prefix("0x") - .unwrap() - .to_lowercase(); - - let hardcoded_hex = hex::encode(PERMIT2_BYTECODE); - - assert_eq!( - forge_hex, hardcoded_hex, - "Permit2 bytecode mismatch! Regenerate with: \ - cd contracts/lib/permit2 && forge inspect Permit2 deployedBytecode" - ); - } -} diff --git a/bin/ev-deployer/src/deploy/create2.rs b/bin/ev-deployer/src/deploy/create2.rs deleted file mode 100644 index 32b6d24f..00000000 --- a/bin/ev-deployer/src/deploy/create2.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! CREATE2 address computation. - -use alloy_primitives::{keccak256, Address, Bytes, B256}; - -/// The deterministic deployer factory address (Nick's factory). -/// See: -pub(crate) const DETERMINISTIC_DEPLOYER: Address = Address::new(alloy_primitives::hex!( - "4e59b44847b379578588920ca78fbf26c0b4956c" -)); - -/// Compute the CREATE2 address for a contract deployed via the deterministic deployer. -/// -/// The factory expects calldata `salt ++ initcode` and deploys via: -/// `CREATE2(value=0, offset, size, salt)` -/// -/// The resulting address is: -/// `keccak256(0xff ++ factory ++ salt ++ keccak256(initcode))[12..]` -pub(crate) fn compute_address(salt: B256, initcode: &[u8]) -> Address { - let init_code_hash = keccak256(initcode); - DETERMINISTIC_DEPLOYER.create2(salt, init_code_hash) -} - -/// Build the calldata to send to the deterministic deployer factory. -/// Format: `salt (32 bytes) ++ initcode` -pub(crate) fn build_factory_calldata(salt: B256, initcode: &[u8]) -> Bytes { - let mut data = Vec::with_capacity(32 + initcode.len()); - data.extend_from_slice(salt.as_slice()); - data.extend_from_slice(initcode); - Bytes::from(data) -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::hex; - - #[test] - fn known_create2_address() { - let salt = B256::ZERO; - // Minimal initcode: PUSH1 0x00 PUSH1 0x00 RETURN (returns empty code) - let initcode = hex!("60006000f3"); - let addr = compute_address(salt, &initcode); - - let init_hash = keccak256(initcode); - let expected = DETERMINISTIC_DEPLOYER.create2(salt, init_hash); - assert_eq!(addr, expected); - } - - #[test] - fn different_salts_different_addresses() { - let initcode = hex!("60006000f3"); - let addr1 = compute_address(B256::ZERO, &initcode); - let addr2 = compute_address(B256::with_last_byte(1), &initcode); - assert_ne!(addr1, addr2); - } - - #[test] - fn different_initcode_different_addresses() { - let salt = B256::ZERO; - let addr1 = compute_address(salt, &hex!("60006000f3")); - let addr2 = compute_address(salt, &hex!("60016000f3")); - 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); - let initcode = hex!("aabbcc"); - let calldata = build_factory_calldata(salt, &initcode); - - assert_eq!(calldata.len(), 32 + 3); - assert_eq!(&calldata[..32], salt.as_slice()); - assert_eq!(&calldata[32..], &hex!("aabbcc")); - } -} diff --git a/bin/ev-deployer/src/deploy/deployer.rs b/bin/ev-deployer/src/deploy/deployer.rs deleted file mode 100644 index 8a2812ab..00000000 --- a/bin/ev-deployer/src/deploy/deployer.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! `ChainDeployer` trait and `LiveDeployer` implementation. - -use crate::deploy::create2::{build_factory_calldata, DETERMINISTIC_DEPLOYER}; -use alloy::{ - network::EthereumWallet, - providers::{Provider, ProviderBuilder}, -}; -use alloy_primitives::{Address, Bytes, B256}; -use alloy_rpc_types_eth::TransactionRequest; -use alloy_signer_local::PrivateKeySigner; -use async_trait::async_trait; - -/// Receipt from a confirmed transaction. -#[derive(Debug)] -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 trait ChainDeployer: Send + Sync { - /// Get the chain ID of the connected chain. - async fn chain_id(&self) -> eyre::Result; - - /// Read the bytecode at an address. Returns empty bytes if no code. - async fn get_code(&self, address: Address) -> eyre::Result; - - /// Send a CREATE2 deployment transaction via the deterministic deployer. - /// Returns the tx hash once the tx is confirmed. - async fn deploy_create2(&self, salt: B256, initcode: &[u8]) -> eyre::Result; -} - -/// Live deployer using alloy provider + signer. -pub struct LiveDeployer { - provider: Box, -} - -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 fn new(rpc_url: &str, private_key_hex: &str) -> eyre::Result { - let key_hex = private_key_hex - .strip_prefix("0x") - .unwrap_or(private_key_hex); - let signer: PrivateKeySigner = key_hex.parse()?; - let wallet = EthereumWallet::from(signer); - - let provider = ProviderBuilder::new() - .wallet(wallet) - .connect_http(rpc_url.parse()?); - - Ok(Self { - provider: Box::new(provider), - }) - } -} - -#[async_trait] -impl ChainDeployer for LiveDeployer { - async fn chain_id(&self) -> eyre::Result { - Ok(self.provider.get_chain_id().await?) - } - - async fn get_code(&self, address: Address) -> eyre::Result { - Ok(self.provider.get_code_at(address).await?) - } - - async fn deploy_create2(&self, salt: B256, initcode: &[u8]) -> eyre::Result { - let calldata = build_factory_calldata(salt, initcode); - - let tx = TransactionRequest::default() - .to(DETERMINISTIC_DEPLOYER) - .input(calldata.into()); - - let pending = self.provider.send_transaction(tx).await?; - let receipt = pending.get_receipt().await?; - - Ok(TxReceipt { - tx_hash: receipt.transaction_hash, - success: receipt.status(), - }) - } -} diff --git a/bin/ev-deployer/src/deploy/mod.rs b/bin/ev-deployer/src/deploy/mod.rs deleted file mode 100644 index e32bf5a4..00000000 --- a/bin/ev-deployer/src/deploy/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! On-chain deployment pipeline: CREATE2 addressing, state tracking, and orchestration. - -pub mod create2; -pub mod deployer; -pub mod pipeline; -pub mod state; diff --git a/bin/ev-deployer/src/deploy/pipeline.rs b/bin/ev-deployer/src/deploy/pipeline.rs deleted file mode 100644 index 2b190f55..00000000 --- a/bin/ev-deployer/src/deploy/pipeline.rs +++ /dev/null @@ -1,397 +0,0 @@ -//! Deploy pipeline: orchestrates the full deployment flow. - -use crate::{ - config::DeployConfig, - contracts, - deploy::{ - create2::{compute_address, DETERMINISTIC_DEPLOYER}, - deployer::ChainDeployer, - state::{ContractState, ContractStatus, DeployState}, - }, -}; -use alloy_primitives::{Address, B256}; -use std::path::{Path, PathBuf}; - -/// Configuration for the deploy pipeline. -#[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. - /// If the file exists, the pipeline resumes from it and reuses its CREATE2 salt. - /// If it does not exist, the pipeline creates it on first run. - pub state_path: PathBuf, - /// Optional path for writing a JSON address manifest after a successful deploy. - /// This output is informational only and does not affect deployment behavior. - pub addresses_out: Option, -} - -/// Run the full deploy pipeline. -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?; - eprintln!(" chain_id={chain_id}"); - - eyre::ensure!( - chain_id == pipeline_cfg.config.chain.chain_id, - "chain_id mismatch: config says {}, RPC reports {}", - pipeline_cfg.config.chain.chain_id, - chain_id - ); - - eprintln!("[2/4] Verifying deterministic deployer..."); - let deployer_code = deployer.get_code(DETERMINISTIC_DEPLOYER).await?; - eyre::ensure!( - !deployer_code.is_empty(), - "deterministic deployer not found at {} -- deploy it before running ev-deployer deploy", - DETERMINISTIC_DEPLOYER - ); - eprintln!(" OK"); - - // Load or create state - let mut state = if pipeline_cfg.state_path.exists() { - let state = DeployState::load(&pipeline_cfg.state_path)?; - state.validate_immutability(&pipeline_cfg.config)?; - state - } else { - let state = DeployState::new(&pipeline_cfg.config); - state.save(&pipeline_cfg.state_path)?; - state - }; - - // ── Step 2: Deploy Permit2 ── - if let Some(ref p2_config) = pipeline_cfg.config.contracts.permit2 { - eprintln!("[3/4] Deploying Permit2..."); - - if p2_config.address.is_some() { - 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(permit2_salt, &initcode); - - let expected_runtime = contracts::permit2::expected_runtime_bytecode(chain_id, address); - - deploy_contract( - deployer, - &mut state, - &DeployContractParams { - name: "permit2", - address, - salt: permit2_salt, - initcode: &initcode, - expected_runtime: &expected_runtime, - state_path: &pipeline_cfg.state_path, - }, - ) - .await?; - } else { - eprintln!("[3/4] Permit2 not configured, skipping"); - } - - // ── Step 3: Verify ── - eprintln!("[4/4] Verifying bytecodes..."); - verify_all(deployer, &mut state, &pipeline_cfg.config, chain_id).await?; - state.save(&pipeline_cfg.state_path)?; - eprintln!(" OK"); - - // ── Step 5: Output ── - eprintln!(); - eprintln!( - "Deploy complete. State saved to {}", - pipeline_cfg.state_path.display() - ); - - if let Some(ref addr_path) = pipeline_cfg.addresses_out { - let manifest = build_deploy_manifest(&state); - let json = serde_json::to_string_pretty(&manifest)?; - std::fs::write(addr_path, &json)?; - eprintln!("Wrote address manifest to {}", addr_path.display()); - } - - Ok(()) -} - -/// Parameters for deploying a single contract. -struct DeployContractParams<'a> { - name: &'a str, - address: Address, - salt: B256, - initcode: &'a [u8], - expected_runtime: &'a [u8], - state_path: &'a Path, -} - -/// Deploy a single contract via CREATE2 with idempotency. -async fn deploy_contract( - deployer: &dyn ChainDeployer, - state: &mut DeployState, - params: &DeployContractParams<'_>, -) -> eyre::Result<()> { - let DeployContractParams { - name, - address, - salt, - initcode, - expected_runtime, - state_path, - } = params; - // Check if already deployed or verified in state - let current_status = get_contract_status(state, name); - if current_status >= Some(ContractStatus::Deployed) { - eprintln!(" already deployed at {address}, skipping"); - return Ok(()); - } - - // Idempotency: check if code already exists on-chain - let existing_code = deployer.get_code(*address).await?; - if !existing_code.is_empty() { - if existing_code.as_ref() == *expected_runtime { - eprintln!(" found matching bytecode at {address}, marking as deployed"); - set_contract_state( - state, - name, - ContractState { - status: ContractStatus::Deployed, - address: *address, - deploy_tx: None, - }, - ); - state.save(state_path)?; - return Ok(()); - } - eyre::bail!( - "unexpected bytecode at {address}: expected {} bytes, found {} bytes", - expected_runtime.len(), - existing_code.len() - ); - } - - // Deploy - let receipt = deployer.deploy_create2(*salt, initcode).await?; - eyre::ensure!( - receipt.success, - "CREATE2 deploy tx reverted for {name}: tx={}", - receipt.tx_hash - ); - - eprintln!(" tx={} address={address}", receipt.tx_hash); - - set_contract_state( - state, - name, - ContractState { - status: ContractStatus::Deployed, - address: *address, - deploy_tx: Some(receipt.tx_hash), - }, - ); - state.save(state_path)?; - - Ok(()) -} - -/// Verify all deployed contracts have matching on-chain bytecode. -async fn verify_all( - deployer: &dyn ChainDeployer, - state: &mut DeployState, - _config: &DeployConfig, - chain_id: u64, -) -> eyre::Result<()> { - if let Some(ref cs) = state.contracts.permit2 { - if cs.status == ContractStatus::Deployed { - let on_chain = deployer.get_code(cs.address).await?; - let expected = contracts::permit2::expected_runtime_bytecode(chain_id, cs.address); - eyre::ensure!( - on_chain.as_ref() == expected.as_slice(), - "bytecode mismatch at {}: expected {} bytes, got {} bytes", - cs.address, - expected.len(), - on_chain.len() - ); - let mut updated = cs.clone(); - updated.status = ContractStatus::Verified; - state.contracts.permit2 = Some(updated); - } - } - - Ok(()) -} - -fn get_contract_status(state: &DeployState, name: &str) -> Option { - if name == "permit2" { - state.contracts.permit2.as_ref().map(|c| c.status) - } else { - None - } -} - -fn set_contract_state(state: &mut DeployState, name: &str, cs: ContractState) { - if name == "permit2" { - state.contracts.permit2 = Some(cs); - } -} - -fn build_deploy_manifest(state: &DeployState) -> serde_json::Value { - let mut manifest = serde_json::Map::new(); - if let Some(ref cs) = state.contracts.permit2 { - manifest.insert( - "permit2".to_string(), - serde_json::Value::String(format!("{}", cs.address)), - ); - } - serde_json::Value::Object(manifest) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{config::*, deploy::deployer::TxReceipt}; - use alloy_primitives::Bytes; - use async_trait::async_trait; - use std::{collections::HashMap, sync::Mutex}; - - /// Mock deployer for testing the pipeline without a live chain. - struct MockDeployer { - chain_id: u64, - code: Mutex>, - deploys: Mutex)>>, - } - - impl MockDeployer { - fn new(chain_id: u64) -> Self { - let mut code = HashMap::new(); - code.insert(DETERMINISTIC_DEPLOYER, Bytes::from_static(&[0x01])); - Self { - chain_id, - code: Mutex::new(code), - deploys: Mutex::new(Vec::new()), - } - } - } - - #[async_trait] - impl ChainDeployer for MockDeployer { - async fn chain_id(&self) -> eyre::Result { - Ok(self.chain_id) - } - - async fn get_code(&self, address: Address) -> eyre::Result { - Ok(self - .code - .lock() - .unwrap() - .get(&address) - .cloned() - .unwrap_or_default()) - } - - async fn deploy_create2(&self, salt: B256, initcode: &[u8]) -> eyre::Result { - self.deploys.lock().unwrap().push((salt, initcode.to_vec())); - - // Simulate: place the expected runtime bytecode at the computed address - let address = compute_address(salt, initcode); - - let runtime = contracts::permit2::expected_runtime_bytecode(self.chain_id, address); - self.code - .lock() - .unwrap() - .insert(address, Bytes::from(runtime)); - - Ok(TxReceipt { - tx_hash: B256::with_last_byte(0x01), - success: true, - }) - } - } - - fn test_config() -> DeployConfig { - DeployConfig { - chain: ChainConfig { chain_id: 1234 }, - contracts: ContractsConfig { - admin_proxy: None, - permit2: Some(Permit2Config { address: None }), - deterministic_deployer: None, - }, - } - } - - #[tokio::test] - async fn pipeline_deploys_permit2() { - let mock = MockDeployer::new(1234); - let tmp_state = tempfile::NamedTempFile::new().unwrap(); - std::fs::remove_file(tmp_state.path()).unwrap(); - - let cfg = PipelineConfig { - config: test_config(), - state_path: tmp_state.path().to_path_buf(), - addresses_out: None, - }; - - run(&cfg, &mock).await.unwrap(); - - let state = DeployState::load(tmp_state.path()).unwrap(); - assert_eq!( - state.contracts.permit2.as_ref().unwrap().status, - ContractStatus::Verified - ); - assert_eq!(mock.deploys.lock().unwrap().len(), 1); - } - - #[tokio::test] - async fn pipeline_skips_already_deployed() { - let mock = MockDeployer::new(1234); - let tmp_state = tempfile::NamedTempFile::new().unwrap(); - std::fs::remove_file(tmp_state.path()).unwrap(); - - let cfg = PipelineConfig { - config: test_config(), - state_path: tmp_state.path().to_path_buf(), - addresses_out: None, - }; - - // First run - run(&cfg, &mock).await.unwrap(); - assert_eq!(mock.deploys.lock().unwrap().len(), 1); - - // Second run — should skip - run(&cfg, &mock).await.unwrap(); - assert_eq!(mock.deploys.lock().unwrap().len(), 1); // no new deploys - } - - #[tokio::test] - async fn pipeline_rejects_chain_id_mismatch() { - let mock = MockDeployer::new(9999); - let tmp_state = tempfile::NamedTempFile::new().unwrap(); - std::fs::remove_file(tmp_state.path()).unwrap(); - - let cfg = PipelineConfig { - config: test_config(), // chain_id = 1234 - state_path: tmp_state.path().to_path_buf(), - addresses_out: None, - }; - - let err = run(&cfg, &mock).await.unwrap_err().to_string(); - assert!(err.contains("chain_id mismatch"), "{err}"); - } - - #[tokio::test] - async fn pipeline_rejects_missing_deployer() { - let mock = MockDeployer::new(1234); - mock.code.lock().unwrap().remove(&DETERMINISTIC_DEPLOYER); - - let tmp_state = tempfile::NamedTempFile::new().unwrap(); - std::fs::remove_file(tmp_state.path()).unwrap(); - - let cfg = PipelineConfig { - config: test_config(), - state_path: tmp_state.path().to_path_buf(), - addresses_out: None, - }; - - let err = run(&cfg, &mock).await.unwrap_err().to_string(); - assert!(err.contains("deterministic deployer not found"), "{err}"); - } -} diff --git a/bin/ev-deployer/src/deploy/state.rs b/bin/ev-deployer/src/deploy/state.rs deleted file mode 100644 index d71d8bde..00000000 --- a/bin/ev-deployer/src/deploy/state.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! Deploy state file: tracks deployment progress with resumability. - -use crate::config::DeployConfig; -use alloy_primitives::{Address, B256}; -use rand::RngExt; -use serde::{Deserialize, Serialize}; -use std::path::Path; - -/// Current state file schema version. -const STATE_VERSION: u32 = 1; - -/// Overall deployment state, persisted to JSON. -#[derive(Debug, Serialize, Deserialize)] -pub(crate) struct DeployState { - /// Schema version. - pub version: u32, - /// Random salt for CREATE2 deployments. - pub create2_salt: B256, - /// Snapshot of the config at first run — used for immutability checks. - pub applied_intent: AppliedIntent, - /// Per-contract deployment state. - #[serde(default)] - pub contracts: ContractStates, -} - -/// Snapshot of the config that was used for the first deployment. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub(crate) struct AppliedIntent { - pub chain_id: u64, - pub permit2: Option, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub(crate) struct AppliedPermit2 {} - -/// Per-contract deployment states. -#[derive(Debug, Default, Serialize, Deserialize)] -pub(crate) struct ContractStates { - pub permit2: Option, -} - -/// State of a single contract deployment. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct ContractState { - pub status: ContractStatus, - pub address: Address, - #[serde(skip_serializing_if = "Option::is_none")] - pub deploy_tx: Option, -} - -/// Contract deployment status progression. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub(crate) enum ContractStatus { - Pending, - Deployed, - Verified, -} - -impl DeployState { - /// Create a new state from config, generating a random salt. - pub(crate) fn new(config: &DeployConfig) -> Self { - let mut salt_bytes = [0u8; 32]; - let mut rng = rand::rng(); - rng.fill(&mut salt_bytes); - let salt = B256::from(salt_bytes); - - Self { - version: STATE_VERSION, - create2_salt: salt, - applied_intent: AppliedIntent::from_config(config), - contracts: ContractStates::default(), - } - } - - /// Load state from a JSON file. - pub(crate) fn load(path: &Path) -> eyre::Result { - let content = std::fs::read_to_string(path)?; - let state: Self = serde_json::from_str(&content)?; - eyre::ensure!( - state.version == STATE_VERSION, - "unsupported state version: {} (expected {})", - state.version, - STATE_VERSION - ); - Ok(state) - } - - /// Save state to a JSON file atomically (write to tmp, then rename). - pub(crate) fn save(&self, path: &Path) -> eyre::Result<()> { - let json = serde_json::to_string_pretty(self)?; - let tmp = path.with_extension("tmp"); - std::fs::write(&tmp, &json)?; - std::fs::rename(&tmp, path)?; - Ok(()) - } - - /// Validate that the current config is compatible with the applied intent. - /// Immutable fields cannot change. New contracts can be added. - pub(crate) fn validate_immutability(&self, config: &DeployConfig) -> eyre::Result<()> { - let current = &self.applied_intent; - - eyre::ensure!( - config.chain.chain_id == current.chain_id, - "immutability violation: chain_id changed from {} to {}", - current.chain_id, - config.chain.chain_id - ); - - // If permit2 was in the original intent, it must still be present - if current.permit2.is_some() { - eyre::ensure!( - config.contracts.permit2.is_some(), - "immutability violation: permit2 was configured but is now missing" - ); - } - - Ok(()) - } -} - -impl AppliedIntent { - fn from_config(config: &DeployConfig) -> Self { - Self { - chain_id: config.chain.chain_id, - permit2: config.contracts.permit2.as_ref().map(|_| AppliedPermit2 {}), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::*; - - fn test_config() -> DeployConfig { - DeployConfig { - chain: ChainConfig { chain_id: 1234 }, - contracts: ContractsConfig { - admin_proxy: None, - permit2: Some(Permit2Config { address: None }), - deterministic_deployer: None, - }, - } - } - - #[test] - fn new_state_has_random_salt() { - let s1 = DeployState::new(&test_config()); - let s2 = DeployState::new(&test_config()); - assert_ne!(s1.create2_salt, s2.create2_salt); - } - - #[test] - fn new_state_snapshots_intent() { - let state = DeployState::new(&test_config()); - assert_eq!(state.applied_intent.chain_id, 1234); - assert!(state.applied_intent.permit2.is_some()); - } - - #[test] - fn roundtrip_save_load() { - let state = DeployState::new(&test_config()); - let tmp = tempfile::NamedTempFile::new().unwrap(); - state.save(tmp.path()).unwrap(); - let loaded = DeployState::load(tmp.path()).unwrap(); - assert_eq!(loaded.create2_salt, state.create2_salt); - assert_eq!(loaded.applied_intent, state.applied_intent); - } - - #[test] - fn immutability_ok_same_config() { - let config = test_config(); - let state = DeployState::new(&config); - assert!(state.validate_immutability(&config).is_ok()); - } - - #[test] - fn immutability_rejects_chain_id_change() { - let mut changed = test_config(); - let state = DeployState::new(&changed); - changed.chain.chain_id = 9999; - let err = state - .validate_immutability(&changed) - .unwrap_err() - .to_string(); - assert!(err.contains("chain_id changed"), "{err}"); - } - - #[test] - fn immutability_allows_adding_permit2() { - let config = DeployConfig { - chain: ChainConfig { chain_id: 1234 }, - contracts: ContractsConfig { - admin_proxy: None, - permit2: None, - deterministic_deployer: None, - }, - }; - let state = DeployState::new(&config); - - let mut extended = config; - extended.contracts.permit2 = Some(Permit2Config { address: None }); - assert!(state.validate_immutability(&extended).is_ok()); - } - - #[test] - fn immutability_rejects_removing_permit2() { - let mut changed = test_config(); - let state = DeployState::new(&changed); - changed.contracts.permit2 = None; - let err = state - .validate_immutability(&changed) - .unwrap_err() - .to_string(); - assert!( - err.contains("permit2 was configured but is now missing"), - "{err}" - ); - } - - #[test] - fn contract_status_ordering() { - assert!(ContractStatus::Pending < ContractStatus::Deployed); - assert!(ContractStatus::Deployed < ContractStatus::Verified); - } -} diff --git a/bin/ev-deployer/src/genesis.rs b/bin/ev-deployer/src/genesis.rs deleted file mode 100644 index 984dfef3..00000000 --- a/bin/ev-deployer/src/genesis.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! Genesis alloc JSON builder. - -use crate::{ - config::DeployConfig, - contracts::{self, GenesisContract}, -}; -use alloy_primitives::B256; -use serde_json::{Map, Value}; -use std::path::Path; - -/// Build the alloc JSON from config. -pub fn build_alloc(config: &DeployConfig) -> Value { - let mut alloc = Map::new(); - - if let Some(ref ap_config) = config.contracts.admin_proxy { - let contract = contracts::admin_proxy::build(ap_config); - insert_contract(&mut alloc, &contract); - } - - if let Some(ref p2_config) = config.contracts.permit2 { - let contract = contracts::permit2::build(p2_config, config.chain.chain_id); - insert_contract(&mut alloc, &contract); - } - - if let Some(ref dd_config) = config.contracts.deterministic_deployer { - let contract = contracts::deterministic_deployer::build(dd_config); - insert_contract(&mut alloc, &contract); - } - - Value::Object(alloc) -} - -/// Build alloc and merge into an existing genesis JSON file. -pub fn merge_into(config: &DeployConfig, genesis_path: &Path, force: bool) -> eyre::Result { - 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 - .get_mut("alloc") - .and_then(|v| v.as_object_mut()) - .ok_or_else(|| eyre::eyre!("genesis JSON missing 'alloc' object"))?; - - let new_alloc = alloc.as_object().unwrap(); - for (addr, entry) in new_alloc { - let canonical = normalize_addr(addr); - let existing_key = genesis_alloc - .keys() - .find(|k| normalize_addr(k) == canonical) - .cloned(); - if existing_key.is_some() && !force { - eyre::bail!("address collision at {addr}; use --force to overwrite"); - } - if let Some(key) = existing_key { - genesis_alloc.remove(&key); - } - genesis_alloc.insert(canonical, entry.clone()); - } - - Ok(()) -} - -fn normalize_addr(addr: &str) -> String { - addr.strip_prefix("0x").unwrap_or(addr).to_lowercase() -} - -fn insert_contract(alloc: &mut Map, contract: &GenesisContract) { - let addr_key = normalize_addr(&format!("{}", contract.address)); - - let mut storage_map = Map::new(); - for (slot, value) in &contract.storage { - let slot_key = format_slot_key(slot); - storage_map.insert(slot_key, Value::String(format!("{value}"))); - } - - let mut entry = Map::new(); - entry.insert("balance".to_string(), Value::String("0x0".to_string())); - entry.insert( - "code".to_string(), - Value::String(format!( - "0x{}", - alloy_primitives::hex::encode(&contract.code) - )), - ); - entry.insert("storage".to_string(), Value::Object(storage_map)); - - alloc.insert(addr_key, Value::Object(entry)); -} - -/// Format a storage slot key as a full 32-byte hex string. -/// `B256::ZERO` -> "0x0000000000000000000000000000000000000000000000000000000000000000" -fn format_slot_key(slot: &B256) -> String { - format!("{slot}") -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::*; - use alloy_primitives::address; - - fn test_config() -> DeployConfig { - DeployConfig { - chain: ChainConfig { chain_id: 1234 }, - contracts: ContractsConfig { - admin_proxy: Some(AdminProxyConfig { - address: Some(address!("000000000000000000000000000000000000ad00")), - owner: address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), - }), - permit2: None, - deterministic_deployer: None, - }, - } - } - - #[test] - fn alloc_json_structure() { - let alloc = build_alloc(&test_config()); - let obj = alloc.as_object().unwrap(); - assert!(obj.contains_key("000000000000000000000000000000000000ad00")); - - let entry = obj - .get("000000000000000000000000000000000000ad00") - .unwrap() - .as_object() - .unwrap(); - assert_eq!(entry["balance"], "0x0"); - assert!(entry["code"].as_str().unwrap().starts_with("0x")); - assert!(entry.contains_key("storage")); - } - - #[test] - fn alloc_golden_value() { - let alloc = build_alloc(&test_config()); - let storage = alloc - .as_object() - .unwrap() - .get("000000000000000000000000000000000000ad00") - .unwrap() - .get("storage") - .unwrap() - .as_object() - .unwrap(); - - assert_eq!( - storage["0x0000000000000000000000000000000000000000000000000000000000000000"], - "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" - ); - } - - #[test] - fn slot_key_formatting() { - assert_eq!( - format_slot_key(&B256::ZERO), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format_slot_key(&B256::with_last_byte(1)), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format_slot_key(&B256::with_last_byte(6)), - "0x0000000000000000000000000000000000000000000000000000000000000006" - ); - } - - #[test] - fn merge_detects_collision() { - let genesis = r#"{"alloc":{"000000000000000000000000000000000000ad00":{"balance":"0x0"}}}"#; - let tmp = tempfile::NamedTempFile::new().unwrap(); - std::fs::write(tmp.path(), genesis).unwrap(); - - let result = merge_into(&test_config(), tmp.path(), false); - assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("address collision")); - } - - #[test] - fn merge_force_overwrites() { - let genesis = r#"{"alloc":{"000000000000000000000000000000000000ad00":{"balance":"0x0"}}}"#; - let tmp = tempfile::NamedTempFile::new().unwrap(); - std::fs::write(tmp.path(), genesis).unwrap(); - - let result = merge_into(&test_config(), tmp.path(), true); - assert!(result.is_ok()); - } - - #[test] - fn merge_detects_collision_with_0x_prefix() { - let genesis = - r#"{"alloc":{"0x000000000000000000000000000000000000ad00":{"balance":"0x0"}}}"#; - let tmp = tempfile::NamedTempFile::new().unwrap(); - std::fs::write(tmp.path(), genesis).unwrap(); - - let result = merge_into(&test_config(), tmp.path(), false); - assert!(result.is_err()); - } - - #[test] - fn merge_detects_collision_with_mixed_case() { - let genesis = r#"{"alloc":{"000000000000000000000000000000000000AD00":{"balance":"0x0"}}}"#; - let tmp = tempfile::NamedTempFile::new().unwrap(); - std::fs::write(tmp.path(), genesis).unwrap(); - - let result = merge_into(&test_config(), tmp.path(), false); - assert!(result.is_err()); - } - - #[test] - fn deterministic_deployer_in_alloc() { - let config = DeployConfig { - chain: ChainConfig { chain_id: 1234 }, - contracts: ContractsConfig { - admin_proxy: None, - permit2: None, - deterministic_deployer: Some(DeterministicDeployerConfig { - address: Some(address!("4e59b44847b379578588920cA78FbF26c0B4956C")), - }), - }, - }; - let alloc = build_alloc(&config); - let obj = alloc.as_object().unwrap(); - let key = "4e59b44847b379578588920ca78fbf26c0b4956c"; - assert!(obj.contains_key(key), "missing key {key} in {obj:?}"); - - let entry = obj.get(key).unwrap().as_object().unwrap(); - assert_eq!(entry["balance"], "0x0"); - assert!(entry["code"].as_str().unwrap().starts_with("0x")); - assert!(entry["storage"].as_object().unwrap().is_empty()); - } -} diff --git a/bin/ev-deployer/src/init.rs b/bin/ev-deployer/src/init.rs deleted file mode 100644 index 52634c66..00000000 --- a/bin/ev-deployer/src/init.rs +++ /dev/null @@ -1,316 +0,0 @@ -//! Dynamic config template generation for the `init` command. - -/// Whether the config is for genesis injection or live CREATE2 deployment. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum InitMode { - /// Config for genesis injection. - Genesis, - /// Config for live CREATE2 deployment. - Deploy, -} - -/// Parameters for generating the init template. -#[derive(Debug)] -pub struct InitParams { - /// Genesis or deploy mode. - pub mode: InitMode, - /// Target chain ID (written to `[chain]` section). - pub chain_id: u64, - /// Whether to enable the `Permit2` contract section. - pub permit2: bool, - /// Whether to include the deterministic deployer (Nick's factory). - pub deterministic_deployer: bool, - /// If set, enables `AdminProxy` with this owner address. - pub admin_proxy_owner: Option, -} - -/// Generate a TOML config template based on the given parameters. -pub fn generate_template(params: &InitParams) -> String { - let mut out = String::new(); - - let is_genesis = params.mode == InitMode::Genesis; - - // In deploy mode, the deterministic deployer must already exist on-chain - // (verified by the pipeline) — it cannot be deployed via CREATE2 itself. - let deterministic_deployer = params.deterministic_deployer && is_genesis; - - // Header - let mode_label = if is_genesis { "genesis" } else { "deploy" }; - out.push_str(&format!( - "# EV Deployer configuration ({mode_label} mode)\n" - )); - out.push_str("# See: bin/ev-deployer/README.md\n"); - out.push('\n'); - - // Chain - out.push_str("[chain]\n"); - out.push_str("# The chain ID for the target network.\n"); - out.push_str(&format!("chain_id = {}\n", params.chain_id)); - - // Contracts section header - out.push('\n'); - out.push_str("# ── Contracts ────────────────────────────────────────────\n"); - if is_genesis { - out.push_str("# Uncomment and configure the contracts you want in genesis.\n"); - out.push_str("# The `address` field is required for genesis mode.\n"); - } else { - out.push_str("# Uncomment the contracts you want to deploy via CREATE2.\n"); - out.push_str("# Addresses are computed deterministically; no `address` field needed.\n"); - } - - // AdminProxy - out.push('\n'); - out.push_str("# AdminProxy: transparent proxy with owner-based access control.\n"); - out.push_str("# The owner address is stored in slot 0.\n"); - if let Some(ref owner) = params.admin_proxy_owner { - out.push_str("[contracts.admin_proxy]\n"); - if is_genesis { - out.push_str("address = \"0x000000000000000000000000000000000000Ad00\"\n"); - } - out.push_str(&format!("owner = \"{owner}\"\n")); - } else { - out.push_str("# [contracts.admin_proxy]\n"); - if is_genesis { - out.push_str("# address = \"0x000000000000000000000000000000000000Ad00\"\n"); - } - out.push_str("# owner = \"0x...\"\n"); - } - - // Permit2 - out.push('\n'); - out.push_str("# Permit2: Uniswap canonical token approval manager.\n"); - if params.permit2 { - out.push_str("[contracts.permit2]\n"); - if is_genesis { - out.push_str("address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\"\n"); - } - } else { - out.push_str("# [contracts.permit2]\n"); - if is_genesis { - out.push_str("# address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\"\n"); - } - } - - // Deterministic deployer (only relevant for genesis mode — in deploy mode - // the pipeline verifies it exists on-chain, it cannot be deployed via CREATE2). - if is_genesis { - out.push('\n'); - out.push_str( - "# Deterministic deployer (Nick's factory): CREATE2 factory for deploy mode.\n", - ); - out.push_str( - "# Required in genesis for post-merge chains where the keyless tx cannot land.\n", - ); - if deterministic_deployer { - out.push_str("[contracts.deterministic_deployer]\n"); - out.push_str("address = \"0x4e59b44847b379578588920cA78FbF26c0B4956C\"\n"); - } else { - out.push_str("# [contracts.deterministic_deployer]\n"); - out.push_str("# address = \"0x4e59b44847b379578588920cA78FbF26c0B4956C\"\n"); - } - } - - out -} - -#[cfg(test)] -mod tests { - use super::*; - - const LEGACY_GENESIS_TEMPLATE: &str = include_str!("init_template.toml"); - - #[test] - fn genesis_default_matches_legacy_template() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 0, - permit2: false, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert_eq!(output, LEGACY_GENESIS_TEMPLATE); - } - - #[test] - fn genesis_custom_chain_id() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 42170, - permit2: false, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!(output.contains("chain_id = 42170"), "{output}"); - assert!(output.contains("# [contracts.permit2]"), "{output}"); - assert!(output.contains("# [contracts.admin_proxy]"), "{output}"); - } - - #[test] - fn genesis_permit2_includes_address() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 0, - permit2: true, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!(output.contains("[contracts.permit2]\n"), "{output}"); - assert!( - output.contains("address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\""), - "{output}" - ); - } - - #[test] - fn genesis_admin_proxy_with_owner() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 0, - permit2: false, - deterministic_deployer: false, - admin_proxy_owner: Some("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string()), - }; - let output = generate_template(¶ms); - assert!(output.contains("[contracts.admin_proxy]\n"), "{output}"); - assert!( - output.contains("address = \"0x000000000000000000000000000000000000Ad00\""), - "{output}" - ); - assert!( - output.contains("owner = \"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\""), - "{output}" - ); - } - - #[test] - fn genesis_all_flags() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 1234, - permit2: true, - deterministic_deployer: true, - admin_proxy_owner: Some("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string()), - }; - let output = generate_template(¶ms); - assert!(output.contains("(genesis mode)"), "{output}"); - assert!(output.contains("chain_id = 1234"), "{output}"); - assert!(output.contains("[contracts.permit2]\n"), "{output}"); - assert!(output.contains("[contracts.admin_proxy]\n"), "{output}"); - assert!( - output.contains("[contracts.deterministic_deployer]\n"), - "{output}" - ); - } - - #[test] - fn genesis_deterministic_deployer_includes_address() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 0, - permit2: false, - deterministic_deployer: true, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!( - output.contains("[contracts.deterministic_deployer]\n"), - "{output}" - ); - assert!( - output.contains("address = \"0x4e59b44847b379578588920cA78FbF26c0B4956C\""), - "{output}" - ); - } - - #[test] - fn genesis_deterministic_deployer_disabled() { - let params = InitParams { - mode: InitMode::Genesis, - chain_id: 0, - permit2: false, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!( - output.contains("# [contracts.deterministic_deployer]"), - "{output}" - ); - } - - // ── Deploy mode tests ── - - #[test] - fn deploy_header() { - let params = InitParams { - mode: InitMode::Deploy, - chain_id: 1234, - permit2: false, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!(output.contains("(deploy mode)"), "{output}"); - assert!( - output.contains("Addresses are computed deterministically"), - "{output}" - ); - } - - #[test] - fn deploy_permit2_no_address() { - let params = InitParams { - mode: InitMode::Deploy, - chain_id: 1234, - permit2: true, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!(output.contains("[contracts.permit2]\n"), "{output}"); - assert!( - !output.contains("address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\""), - "deploy mode should not include address for permit2\n{output}" - ); - } - - #[test] - fn deploy_excludes_deterministic_deployer() { - let params = InitParams { - mode: InitMode::Deploy, - chain_id: 1234, - permit2: true, - deterministic_deployer: false, - admin_proxy_owner: None, - }; - let output = generate_template(¶ms); - assert!( - !output.contains("deterministic_deployer"), - "deploy mode should not include deterministic deployer section\n{output}" - ); - } - - #[test] - fn deploy_admin_proxy_no_address() { - let params = InitParams { - mode: InitMode::Deploy, - chain_id: 1234, - permit2: false, - deterministic_deployer: false, - admin_proxy_owner: Some("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string()), - }; - let output = generate_template(¶ms); - assert!(output.contains("[contracts.admin_proxy]\n"), "{output}"); - assert!( - !output.contains("address = \"0x000000000000000000000000000000000000Ad00\""), - "deploy mode should not include address for admin_proxy\n{output}" - ); - assert!( - output.contains("owner = \"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\""), - "{output}" - ); - } -} diff --git a/bin/ev-deployer/src/init_template.toml b/bin/ev-deployer/src/init_template.toml deleted file mode 100644 index e1686d58..00000000 --- a/bin/ev-deployer/src/init_template.toml +++ /dev/null @@ -1,25 +0,0 @@ -# EV Deployer configuration (genesis mode) -# See: bin/ev-deployer/README.md - -[chain] -# The chain ID for the target network. -chain_id = 0 - -# ── Contracts ──────────────────────────────────────────── -# Uncomment and configure the contracts you want in genesis. -# The `address` field is required for genesis mode. - -# AdminProxy: transparent proxy with owner-based access control. -# The owner address is stored in slot 0. -# [contracts.admin_proxy] -# address = "0x000000000000000000000000000000000000Ad00" -# owner = "0x..." - -# Permit2: Uniswap canonical token approval manager. -# [contracts.permit2] -# address = "0x000000000022D473030F116dDEE9F6B43aC78BA3" - -# Deterministic deployer (Nick's factory): CREATE2 factory for deploy mode. -# Required in genesis for post-merge chains where the keyless tx cannot land. -# [contracts.deterministic_deployer] -# address = "0x4e59b44847b379578588920cA78FbF26c0B4956C" diff --git a/bin/ev-deployer/src/lib.rs b/bin/ev-deployer/src/lib.rs deleted file mode 100644 index 8df95412..00000000 --- a/bin/ev-deployer/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! 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; diff --git a/bin/ev-deployer/src/main.rs b/bin/ev-deployer/src/main.rs deleted file mode 100644 index 44f29bcc..00000000 --- a/bin/ev-deployer/src/main.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! EV Deployer — genesis alloc generator and live deployer for ev-reth contracts. - -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. -#[derive(Parser)] -#[command( - name = "ev-deployer", - about = "Generate genesis alloc or deploy ev-reth contracts" -)] -struct Cli { - #[command(subcommand)] - command: Command, -} - -#[derive(Subcommand)] -enum Command { - /// Generate a starter config file for genesis or deploy mode. - Init { - #[command(subcommand)] - subcommand: InitSubcommand, - }, - /// Generate genesis alloc JSON from a deploy config. - Genesis { - /// Path to the deploy TOML config. - #[arg(long)] - config: PathBuf, - - /// Write alloc JSON to this file instead of stdout. - #[arg(long)] - output: Option, - - /// Merge alloc entries into an existing genesis JSON file. - #[arg(long)] - merge_into: Option, - - /// Allow overwriting existing addresses when merging. - #[arg(long, default_value_t = false)] - force: bool, - - /// Write an address manifest to this file. - #[arg(long)] - addresses_out: Option, - }, - /// Deploy contracts to a live chain via CREATE2. - Deploy { - /// Path to the deploy TOML config. - #[arg(long)] - config: PathBuf, - - /// RPC URL of the target chain. - #[arg(long, env = "EV_DEPLOYER_RPC_URL")] - rpc_url: String, - - /// Hex-encoded private key for signing transactions. - #[arg(long, env = "EV_DEPLOYER_PRIVATE_KEY")] - private_key: String, - - /// Path to the state file (created if absent, resumed if present). - #[arg(long)] - state: PathBuf, - - /// Write an address manifest to this file. - #[arg(long)] - addresses_out: Option, - }, - /// Compute the address for a configured contract. - ComputeAddress { - /// Path to the deploy TOML config. - #[arg(long)] - config: PathBuf, - - /// Contract name (e.g. `admin_proxy`). - #[arg(long)] - contract: String, - }, -} - -#[derive(Subcommand)] -enum InitSubcommand { - /// Generate config for genesis injection (contracts embedded at chain start). - Genesis { - /// Write config to this file instead of stdout. - #[arg(long)] - output: Option, - - /// Set the chain ID (defaults to 0). - #[arg(long)] - chain_id: Option, - - /// Include `Permit2` with its canonical address. - #[arg(long)] - permit2: bool, - - /// Include the deterministic deployer (Nick's factory) with its canonical address. - #[arg(long)] - deterministic_deployer: bool, - - /// Include `AdminProxy` with the given owner address. - #[arg(long)] - admin_proxy_owner: Option
, - }, - /// Generate config for live CREATE2 deployment. - Deploy { - /// Write config to this file instead of stdout. - #[arg(long)] - output: Option, - - /// Set the chain ID (defaults to 0). - #[arg(long)] - chain_id: Option, - - /// Include `Permit2`. - #[arg(long)] - permit2: bool, - - /// Include `AdminProxy` with the given owner address. - #[arg(long)] - admin_proxy_owner: Option
, - }, -} - -fn main() -> eyre::Result<()> { - let cli = Cli::parse(); - - match cli.command { - Command::Genesis { - config: config_path, - output, - merge_into, - force, - addresses_out, - } => { - let cfg = config::DeployConfig::load(&config_path)?; - cfg.validate_for_genesis()?; - - let result = if let Some(ref genesis_path) = merge_into { - genesis::merge_into(&cfg, genesis_path, force)? - } else { - genesis::build_alloc(&cfg) - }; - - let json = serde_json::to_string_pretty(&result)?; - - 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(ref addr_path) = addresses_out { - let manifest = output::build_manifest(&cfg); - let manifest_json = serde_json::to_string_pretty(&manifest)?; - std::fs::write(addr_path, &manifest_json)?; - eprintln!("Wrote address manifest to {}", addr_path.display()); - } - } - Command::Deploy { - config: config_path, - rpc_url, - private_key, - state: state_path, - addresses_out, - } => { - let cfg = config::DeployConfig::load(&config_path)?; - let deployer = deploy::deployer::LiveDeployer::new(&rpc_url, &private_key)?; - let pipeline_cfg = deploy::pipeline::PipelineConfig { - config: cfg, - state_path, - addresses_out, - }; - - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()? - .block_on(deploy::pipeline::run(&pipeline_cfg, &deployer))?; - } - Command::Init { subcommand } => { - let (params, output) = match subcommand { - InitSubcommand::Genesis { - output, - chain_id, - permit2, - deterministic_deployer, - admin_proxy_owner, - } => ( - init::InitParams { - mode: init::InitMode::Genesis, - chain_id: chain_id.unwrap_or(0), - permit2, - deterministic_deployer, - admin_proxy_owner: admin_proxy_owner.map(|a| format!("{a}")), - }, - output, - ), - InitSubcommand::Deploy { - output, - chain_id, - permit2, - admin_proxy_owner, - } => ( - init::InitParams { - mode: init::InitMode::Deploy, - chain_id: chain_id.unwrap_or(0), - permit2, - deterministic_deployer: false, - admin_proxy_owner: admin_proxy_owner.map(|a| format!("{a}")), - }, - output, - ), - }; - - let template = init::generate_template(¶ms); - - if let Some(ref out_path) = output { - std::fs::write(out_path, &template)?; - eprintln!("Wrote config to {}", out_path.display()); - } else { - print!("{template}"); - } - } - Command::ComputeAddress { - config: config_path, - contract, - } => { - let cfg = config::DeployConfig::load(&config_path)?; - - let address = match contract.as_str() { - "admin_proxy" => cfg - .contracts - .admin_proxy - .as_ref() - .and_then(|c| c.address) - .ok_or_else(|| eyre::eyre!("admin_proxy not configured or address not set"))?, - "permit2" => cfg - .contracts - .permit2 - .as_ref() - .and_then(|c| c.address) - .ok_or_else(|| eyre::eyre!("permit2 not configured or address not set"))?, - "deterministic_deployer" => cfg - .contracts - .deterministic_deployer - .as_ref() - .and_then(|c| c.address) - .ok_or_else(|| { - eyre::eyre!("deterministic_deployer not configured or address not set") - })?, - other => { - eyre::bail!("unknown contract: {other}"); - } - }; - - println!("{address}"); - } - } - - Ok(()) -} diff --git a/bin/ev-deployer/src/output.rs b/bin/ev-deployer/src/output.rs deleted file mode 100644 index 20c4fc41..00000000 --- a/bin/ev-deployer/src/output.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Address manifest output. - -use crate::config::DeployConfig; -use serde_json::{Map, Value}; - -/// Build an address manifest JSON from config. -pub fn build_manifest(config: &DeployConfig) -> Value { - let mut manifest = Map::new(); - - if let Some(ref ap) = config.contracts.admin_proxy { - if let Some(addr) = ap.address { - manifest.insert( - "admin_proxy".to_string(), - Value::String(format!("{}", addr)), - ); - } - } - - if let Some(ref p2) = config.contracts.permit2 { - if let Some(addr) = p2.address { - manifest.insert("permit2".to_string(), Value::String(format!("{}", addr))); - } - } - - if let Some(ref dd) = config.contracts.deterministic_deployer { - if let Some(addr) = dd.address { - manifest.insert( - "deterministic_deployer".to_string(), - Value::String(format!("{}", addr)), - ); - } - } - - Value::Object(manifest) -} diff --git a/bin/ev-deployer/tests/e2e_genesis.sh b/bin/ev-deployer/tests/e2e_genesis.sh deleted file mode 100755 index b7c085ae..00000000 --- a/bin/ev-deployer/tests/e2e_genesis.sh +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env bash -# End-to-end test: generate genesis with ev-deployer, boot ev-reth, verify contracts via RPC. -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" -DEPLOYER="$REPO_ROOT/target/release/ev-deployer" -EV_RETH="$REPO_ROOT/target/release/ev-reth" -CONFIG="$REPO_ROOT/bin/ev-deployer/examples/devnet.toml" -BASE_GENESIS="$REPO_ROOT/bin/ev-dev/assets/devnet-genesis.json" - -RPC_PORT=18545 -RPC_URL="http://127.0.0.1:$RPC_PORT" -NODE_PID="" -TMPDIR_PATH="" - -cleanup() { - if [[ -n "$NODE_PID" ]]; then - kill "$NODE_PID" 2>/dev/null || true - wait "$NODE_PID" 2>/dev/null || true - fi - if [[ -n "$TMPDIR_PATH" ]]; then - rm -rf "$TMPDIR_PATH" - fi -} -trap cleanup EXIT - -# ── Helpers ────────────────────────────────────────────── - -fail() { echo "FAIL: $1" >&2; exit 1; } -pass() { echo "PASS: $1"; } - -rpc_call() { - local method="$1" - local params="$2" - curl -s --connect-timeout 5 --max-time 10 -X POST "$RPC_URL" \ - -H "Content-Type: application/json" \ - -d "{\"jsonrpc\":\"2.0\",\"method\":\"$method\",\"params\":$params,\"id\":1}" \ - | python3 -c "import sys,json; print(json.load(sys.stdin)['result'])" -} - -wait_for_rpc() { - local max_attempts=30 - for i in $(seq 1 $max_attempts); do - if curl -s --connect-timeout 1 --max-time 2 -X POST "$RPC_URL" \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ - 2>/dev/null | grep -q result; then - return 0 - fi - sleep 1 - done - fail "node did not become ready after ${max_attempts}s" -} - -# ── Step 1: Build ──────────────────────────────────────── - -echo "=== Building ev-deployer and ev-reth ===" -cargo build --release --bin ev-deployer --bin ev-reth --manifest-path "$REPO_ROOT/Cargo.toml" \ - 2>&1 | tail -3 - -[[ -x "$DEPLOYER" ]] || fail "ev-deployer binary not found" -[[ -x "$EV_RETH" ]] || fail "ev-reth binary not found" - -# ── Step 2: Generate genesis ───────────────────────────── - -TMPDIR_PATH="$(mktemp -d)" -GENESIS="$TMPDIR_PATH/genesis.json" -DATADIR="$TMPDIR_PATH/data" - -echo "=== Generating genesis with ev-deployer ===" -"$DEPLOYER" genesis \ - --config "$CONFIG" \ - --merge-into "$BASE_GENESIS" \ - --output "$GENESIS" \ - --force - -echo "Genesis written to $GENESIS" - -# Quick sanity: address should be in the alloc -grep -qi "000000000000000000000000000000000000Ad00" "$GENESIS" \ - || fail "AdminProxy address not found in genesis" -grep -qi "000000000022D473030F116dDEE9F6B43aC78BA3" "$GENESIS" \ - || fail "Permit2 address not found in genesis" - -pass "genesis contains all contract addresses" - -# ── Step 3: Start ev-reth ──────────────────────────────── - -echo "=== Starting ev-reth node ===" -"$EV_RETH" node \ - --dev \ - --chain "$GENESIS" \ - --datadir "$DATADIR" \ - --http \ - --http.addr 127.0.0.1 \ - --http.port "$RPC_PORT" \ - --http.api eth,net,web3 \ - --disable-discovery \ - --no-persist-peers \ - --port 0 \ - --log.stdout.filter error \ - & -NODE_PID=$! - -echo "Node PID: $NODE_PID, waiting for RPC..." -wait_for_rpc -pass "node is up and responding to RPC" - -# ── Step 4: Verify AdminProxy ──────────────────────────── - -ADMIN_PROXY="0x000000000000000000000000000000000000Ad00" -ADMIN_OWNER="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - -echo "=== Verifying AdminProxy at $ADMIN_PROXY ===" - -# Check code is present -admin_code=$(rpc_call "eth_getCode" "[\"$ADMIN_PROXY\", \"latest\"]") -[[ "$admin_code" != "0x" && "$admin_code" != "0x0" && ${#admin_code} -gt 10 ]] \ - || fail "AdminProxy has no bytecode (got: $admin_code)" -pass "AdminProxy has bytecode (${#admin_code} hex chars)" - -# Check owner in slot 0 -admin_slot0=$(rpc_call "eth_getStorageAt" "[\"$ADMIN_PROXY\", \"0x0\", \"latest\"]") -# Owner should be in the lower 20 bytes, left-padded to 32 bytes -expected_owner_slot="0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" -[[ "$(echo "$admin_slot0" | tr '[:upper:]' '[:lower:]')" == "$(echo "$expected_owner_slot" | tr '[:upper:]' '[:lower:]')" ]] \ - || fail "AdminProxy slot 0 (owner) mismatch: got $admin_slot0, expected $expected_owner_slot" -pass "AdminProxy owner slot 0 = $ADMIN_OWNER" - -# ── Step 5: Verify Permit2 ────────────────────────────── - -PERMIT2="0x000000000022D473030F116dDEE9F6B43aC78BA3" - -echo "=== Verifying Permit2 at $PERMIT2 ===" - -# Check code is present -p2_code=$(rpc_call "eth_getCode" "[\"$PERMIT2\", \"latest\"]") -[[ "$p2_code" != "0x" && "$p2_code" != "0x0" && ${#p2_code} -gt 10 ]] \ - || fail "Permit2 has no bytecode (got: $p2_code)" -pass "Permit2 has bytecode (${#p2_code} hex chars)" - -# Call DOMAIN_SEPARATOR() — selector 0x3644e515 -# Should return the cached domain separator matching chain_id=1234 and the contract address -p2_domain_sep=$(rpc_call "eth_call" "[{\"to\":\"$PERMIT2\",\"data\":\"0x3644e515\"}, \"latest\"]") -expected_domain_sep="0x6cda538cafce36292a6ef27740629597f85f6716f5694d26d5c59fc1d07cfd95" -[[ "$(echo "$p2_domain_sep" | tr '[:upper:]' '[:lower:]')" == "$(echo "$expected_domain_sep" | tr '[:upper:]' '[:lower:]')" ]] \ - || fail "Permit2 DOMAIN_SEPARATOR() mismatch: got $p2_domain_sep, expected $expected_domain_sep" -pass "Permit2 DOMAIN_SEPARATOR() correct for chain_id=1234" - -# ── Done ───────────────────────────────────────────────── - -echo "" -echo "=== All checks passed ===" diff --git a/bin/ev-dev/Cargo.toml b/bin/ev-dev/Cargo.toml deleted file mode 100644 index db274636..00000000 --- a/bin/ev-dev/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "ev-dev" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true -description = "One-command local dev chain for ev-reth" - -[[bin]] -name = "ev-dev" -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 -reth-cli-util.workspace = true -reth-ethereum-cli.workspace = true - -# Alloy dependencies -alloy-signer-local.workspace = true -alloy-primitives.workspace = true -alloy-provider.workspace = true -alloy-rpc-types.workspace = true -alloy-network.workspace = true - -# Reth tracing (for Layers type) -reth-tracing.workspace = true - -# Core dependencies -eyre.workspace = true -tracing.workspace = true -tokio = { workspace = true, features = ["full"] } -clap = { workspace = true, features = ["derive", "env"] } -tempfile.workspace = true -serde_json.workspace = true -futures.workspace = true - -# TUI -ratatui = "0.30" -crossterm = { version = "0.29", features = ["event-stream"] } -tracing-subscriber = { version = "0.3", features = ["env-filter", "registry"] } -arboard = "3" - -[lints] -workspace = true - -[features] -default = ["jemalloc"] - -jemalloc = ["reth-cli-util/jemalloc", "reth-ethereum-cli/jemalloc"] -jemalloc-prof = ["reth-cli-util/jemalloc-prof"] -tracy-allocator = ["reth-cli-util/tracy-allocator"] - -asm-keccak = ["reth-ethereum-cli/asm-keccak"] - -dev = ["reth-ethereum-cli/dev"] diff --git a/bin/ev-dev/README.md b/bin/ev-dev/README.md deleted file mode 100644 index 698a32fa..00000000 --- a/bin/ev-dev/README.md +++ /dev/null @@ -1,300 +0,0 @@ -# ev-dev - -One-command local development chain for Evolve. Think of it as the Evolve equivalent of [Hardhat Node](https://hardhat.org/hardhat-network/docs/overview) or [Anvil](https://book.getfoundry.sh/reference/anvil/). - -## Installation - -```bash -# Install to ~/.cargo/bin -just install-ev-dev - -# Or build without installing -just build-ev-dev -``` - -## Quick Start - -```bash -# Build and run -just dev-chain - -# Or run directly after installing -ev-dev -``` - -The chain starts immediately with 10 pre-funded accounts, each holding 1,000,000 ETH. - -## CLI Options - -``` -ev-dev [OPTIONS] -``` - -| Flag | Default | Description | -|------|---------|-------------| -| `--host` | `127.0.0.1` | Host to bind HTTP/WS RPC server | -| `--port` | `8545` | Port for HTTP/WS RPC server | -| `--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) | -| `--genesis-config` | — | Path to an ev-deployer TOML config to deploy contracts at genesis | -| `--tui` | `false` | Launch with an interactive terminal UI instead of plain log output | - -### TUI Mode - -Pass `--tui` to launch an interactive terminal dashboard: - -```bash -ev-dev --tui -``` - -The TUI shows: - -- **Chain info** — chain ID, RPC URL, block time -- **Accounts** — addresses, private keys, and real-time balances (polled every 2s) -- **Deployed contracts** — when using `--genesis-config` -- **Logs** — live node logs with scrollback - -Keyboard shortcuts: - -| Key | Action | -|-----|--------| -| `Tab` | Cycle between panels | -| `↑` / `↓` | Scroll within the active panel | -| `q` / `Esc` / `Ctrl+C` | Quit | - -### Examples - -```bash -# Mine blocks only when transactions arrive -ev-dev --block-time 0 - -# Listen on all interfaces (useful inside Docker/VMs) -ev-dev --host 0.0.0.0 - -# Custom port, faster blocks -ev-dev --port 9545 --block-time 2 - -# Start with genesis contracts deployed -ev-deployer init genesis --permit2 --deterministic-deployer --chain-id 1234 --output genesis.toml -ev-dev --genesis-config genesis.toml -``` - -## Genesis Contract Deployment - -You can deploy contracts into the genesis state by passing a `--genesis-config` flag pointing to an [ev-deployer](../ev-deployer/README.md) TOML config file. - -```bash -ev-dev --genesis-config path/to/deploy.toml -``` - -When a genesis 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. - -## Live Contract Deployment (CREATE2) - -You can also deploy contracts to a running ev-dev chain using `ev-deployer deploy`. This uses the [deterministic deployer](https://github.com/Arachnid/deterministic-deployment-proxy) (Nick's CREATE2 factory at `0x4e59b44847b379578588920ca78fbf26c0b4956c`), which must be included in the genesis via `--genesis-config`. - -```bash -# Terminal 1: start the chain with Nick's factory in genesis -ev-deployer init genesis --deterministic-deployer --chain-id 1234 --output genesis.toml -ev-dev --genesis-config genesis.toml - -# Terminal 2: generate a deploy config and deploy Permit2 via CREATE2 -ev-deployer init deploy --permit2 --chain-id 1234 --output deploy.toml -ev-deployer deploy \ - --config deploy.toml \ - --rpc-url http://127.0.0.1:8545 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ - --state /tmp/deploy-state.json -``` - -Permit2 deploys to its canonical address (`0x000000000022D473030F116dDEE9F6B43aC78BA3`) using the original Uniswap CREATE2 salt. The `--state` file tracks progress and allows resuming interrupted deployments. - -**Genesis vs Deploy mode**: Use `--genesis-config` (genesis mode) when you want contracts available from block 0 with exact addresses. Use `ev-deployer deploy` when you want to simulate a real deployment pipeline — contracts land at their canonical CREATE2 addresses. - -## Chain Details - -| Property | Value | -|----------|-------| -| Chain ID | `1234` | -| Gas limit | 30,000,000 | -| Base fee | 1 Gwei | -| Contract size limit | 128 KB | -| Hardforks | All enabled at genesis (through Cancun) | - -## Pre-funded Accounts - -Accounts are derived from the standard Hardhat mnemonic: - -``` -test test test test test test test test test test test junk -``` - -Derivation path: `m/44'/60'/0'/0/{index}` - -| # | Address | Private Key | -|---|---------|-------------| -| 0 | `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` | `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` | -| 1 | `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` | `0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d` | -| 2 | `0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC` | `0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a` | -| 3 | `0x90F79bf6EB2c4f870365E785982E1f101E93b906` | `0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6` | -| 4 | `0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65` | `0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a` | -| 5 | `0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc` | `0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba` | -| 6 | `0x976EA74026E726554dB657fA54763abd0C3a0aa9` | `0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e` | -| 7 | `0x14dC79964da2C08dba798bBb5d93A585CAa97F90` | `0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356` | -| 8 | `0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f` | `0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97` | -| 9 | `0xa0Ee7A142d267C1f36714E4a8F75612F20a79720` | `0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6` | - -> **WARNING**: These accounts and their private keys are publicly known. Any funds sent to them on a real network **will be lost**. - -## Using with Common Tools - -### Foundry (cast / forge) - -```bash -# Check balance -cast balance 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --rpc-url http://127.0.0.1:8545 - -# Send ETH -cast send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ - --value 1ether \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ - --rpc-url http://127.0.0.1:8545 - -# Deploy a contract -forge create src/MyContract.sol:MyContract \ - --rpc-url http://127.0.0.1:8545 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - -# Run Forge tests against ev-dev -forge test --fork-url http://127.0.0.1:8545 -``` - -### Hardhat - -In `hardhat.config.js`: - -```js -module.exports = { - networks: { - evdev: { - url: "http://127.0.0.1:8545", - chainId: 1234, - accounts: [ - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", - ], - }, - }, -}; -``` - -```bash -npx hardhat run scripts/deploy.js --network evdev -``` - -### ethers.js / viem - -```js -// ethers.js v6 -import { JsonRpcProvider, Wallet } from "ethers"; - -const provider = new JsonRpcProvider("http://127.0.0.1:8545"); -const wallet = new Wallet( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - provider -); -``` - -```js -// viem -import { createWalletClient, http } from "viem"; -import { privateKeyToAccount } from "viem/accounts"; -import { defineChain } from "viem"; - -const evdev = defineChain({ - id: 1234, - name: "ev-dev", - nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, - rpcUrls: { - default: { http: ["http://127.0.0.1:8545"] }, - }, -}); - -const account = privateKeyToAccount( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" -); -const client = createWalletClient({ account, chain: evdev, transport: http() }); -``` - -### curl (raw JSON-RPC) - -```bash -# Get block number -curl -s http://127.0.0.1:8545 \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' - -# Get chain ID -curl -s http://127.0.0.1:8545 \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' - -# Get pending transactions from txpool -curl -s http://127.0.0.1:8545 \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"txpoolExt_getTxs","params":[],"id":1}' -``` - -## Available RPC Namespaces - -The following namespaces are enabled by default over both HTTP and WebSocket: - -- `eth` — standard Ethereum JSON-RPC -- `net` — network info -- `web3` — client version, SHA3 -- `txpool` — transaction pool inspection -- `debug` — debug namespace (traceCall, traceTransaction, etc.) -- `trace` — OpenEthereum-compatible trace namespace - -Additionally, the custom `txpoolExt` namespace is available: - -- `txpoolExt_getTxs` — returns pending transactions as RLP-encoded bytes - -## Evolve-specific Features - -ev-dev includes all Evolve customizations out of the box: - -- **Base fee redirect**: Base fees are sent to `0x00...00fe` instead of being burned -- **128 KB contract size limit**: Deploy contracts up to 128 KB (vs Ethereum's 24 KB) -- **Mint precompile**: Native minting precompile is active, admin is account `0` (`0xf39F...2266`) -- **EvNode transactions (type 0x76)**: Batch calls and sponsored transactions are supported - -## How It Works - -ev-dev is a thin wrapper around the full `ev-reth` node. On startup it: - -1. If `--genesis-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. diff --git a/bin/ev-dev/assets/devnet-genesis.json b/bin/ev-dev/assets/devnet-genesis.json deleted file mode 100644 index 1e39739a..00000000 --- a/bin/ev-dev/assets/devnet-genesis.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "config": { - "chainId": 1234, - "homesteadBlock": 0, - "daoForkSupport": true, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "muirGlacierBlock": 0, - "berlinBlock": 0, - "londonBlock": 0, - "arrowGlacierBlock": 0, - "grayGlacierBlock": 0, - "shanghaiTime": 0, - "cancunTime": 0, - "terminalTotalDifficulty": "0x0", - "terminalTotalDifficultyPassed": true, - "evolve": { - "baseFeeSink": "0x00000000000000000000000000000000000000fe", - "baseFeeRedirectActivationHeight": 0, - "baseFeeMaxChangeDenominator": 5000, - "baseFeeElasticityMultiplier": 10, - "initialBaseFeePerGas": 1000000000, - "mintAdmin": "0x000000000000000000000000000000000000Ad00", - "mintPrecompileActivationHeight": 0, - "contractSizeLimit": 131072, - "contractSizeLimitActivationHeight": 0 - } - }, - "nonce": "0x0", - "timestamp": "0x0", - "extraData": "0x00", - "gasLimit": "0x1c9c380", - "difficulty": "0x0", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "baseFeePerGas": "0x3b9aca00", - "alloc": { - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x90f79bf6eb2c4f870365e785982e1f101e93b906": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x976ea74026e726554db657fa54763abd0c3a0aa9": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x14dc79964da2c08b23698b3d3cc7ca32193d9955": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xbcd4042de499d14e55001ccbb24a551f3b954096": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x71be63f3384f5fb98995898a86b02fb2426c5788": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xfabb0ac9d68b0b445fb7357272ff202c5651694a": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x1cbd3b2770909d4e10f157cabc84c7264073c9ec": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xcd3b766ccdd6ae721141f452c550ca635964ce71": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x2546bcd3c84621e976d8185a91a922ae77ecec30": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xbda5747bfd65f08deb54cb465eb87d40e51b197e": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xdd2fd4581271e230360230f9337d5c0430bf44c0": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x000000000000000000000000000000000000Ad00": { - "balance": "0x0", - "code": "0x60806040526004361061007e575f3560e01c80638da5cb5b1161004d5780638da5cb5b1461012d578063e30c397814610157578063f2fde38b14610181578063fa4bb79d146101a957610085565b806318dfb3c7146100895780631cff79cd146100c557806379ba5097146101015780638b5298541461011757610085565b3661008557005b5f5ffd5b348015610094575f5ffd5b506100af60048036038101906100aa9190610cf8565b6101e5565b6040516100bc9190610ea1565b60405180910390f35b3480156100d0575f5ffd5b506100eb60048036038101906100e69190610f70565b6104d9565b6040516100f89190611015565b60405180910390f35b34801561010c575f5ffd5b5061011561066c565b005b348015610122575f5ffd5b5061012b6107ed565b005b348015610138575f5ffd5b506101416108b4565b60405161014e9190611044565b60405180910390f35b348015610162575f5ffd5b5061016b6108d8565b6040516101789190611044565b60405180910390f35b34801561018c575f5ffd5b506101a760048036038101906101a2919061105d565b6108fd565b005b3480156101b4575f5ffd5b506101cf60048036038101906101ca91906110bb565b610aa4565b6040516101dc9190611015565b60405180910390f35b60605f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461026c576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282905085859050146102ab576040517fff633a3800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484905067ffffffffffffffff8111156102c8576102c761112c565b5b6040519080825280602002602001820160405280156102fb57816020015b60608152602001906001900390816102e65790505b5090505f5f90505b858590508110156104d0575f5f87878481811061032357610322611159565b5b9050602002016020810190610338919061105d565b73ffffffffffffffffffffffffffffffffffffffff1686868581811061036157610360611159565b5b90506020028101906103739190611192565b604051610381929190611230565b5f604051808303815f865af19150503d805f81146103ba576040519150601f19603f3d011682016040523d82523d5f602084013e6103bf565b606091505b50915091508161040657806040517fa5fa8d2b0000000000000000000000000000000000000000000000000000000081526004016103fd9190611015565b60405180910390fd5b87878481811061041957610418611159565b5b905060200201602081019061042e919061105d565b73ffffffffffffffffffffffffffffffffffffffff167fc96720f35dd524e76ea92971ce13d08e9a17816bf3b0008a7083e6032354ebb587878681811061047857610477611159565b5b905060200281019061048a9190611192565b8460405161049a93929190611274565b60405180910390a2808484815181106104b6576104b5611159565b5b602002602001018190525050508080600101915050610303565b50949350505050565b60605f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610560576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5f8573ffffffffffffffffffffffffffffffffffffffff168585604051610589929190611230565b5f604051808303815f865af19150503d805f81146105c2576040519150601f19603f3d011682016040523d82523d5f602084013e6105c7565b606091505b50915091508161060e57806040517fa5fa8d2b0000000000000000000000000000000000000000000000000000000081526004016106059190611015565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff167fc96720f35dd524e76ea92971ce13d08e9a17816bf3b0008a7083e6032354ebb586868460405161065893929190611274565b60405180910390a280925050509392505050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106f2576040517f1853971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff165f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3335f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f60015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610872576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610982576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036109e7576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff165f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60605f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b2b576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5f8673ffffffffffffffffffffffffffffffffffffffff16848787604051610b55929190611230565b5f6040518083038185875af1925050503d805f8114610b8f576040519150601f19603f3d011682016040523d82523d5f602084013e610b94565b606091505b509150915081610bdb57806040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152600401610bd29190611015565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff167fc96720f35dd524e76ea92971ce13d08e9a17816bf3b0008a7083e6032354ebb5878784604051610c2593929190611274565b60405180910390a28092505050949350505050565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f840112610c6357610c62610c42565b5b8235905067ffffffffffffffff811115610c8057610c7f610c46565b5b602083019150836020820283011115610c9c57610c9b610c4a565b5b9250929050565b5f5f83601f840112610cb857610cb7610c42565b5b8235905067ffffffffffffffff811115610cd557610cd4610c46565b5b602083019150836020820283011115610cf157610cf0610c4a565b5b9250929050565b5f5f5f5f60408587031215610d1057610d0f610c3a565b5b5f85013567ffffffffffffffff811115610d2d57610d2c610c3e565b5b610d3987828801610c4e565b9450945050602085013567ffffffffffffffff811115610d5c57610d5b610c3e565b5b610d6887828801610ca3565b925092505092959194509250565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610de182610d9f565b610deb8185610da9565b9350610dfb818560208601610db9565b610e0481610dc7565b840191505092915050565b5f610e1a8383610dd7565b905092915050565b5f602082019050919050565b5f610e3882610d76565b610e428185610d80565b935083602082028501610e5485610d90565b805f5b85811015610e8f5784840389528151610e708582610e0f565b9450610e7b83610e22565b925060208a01995050600181019050610e57565b50829750879550505050505092915050565b5f6020820190508181035f830152610eb98184610e2e565b905092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610eea82610ec1565b9050919050565b610efa81610ee0565b8114610f04575f5ffd5b50565b5f81359050610f1581610ef1565b92915050565b5f5f83601f840112610f3057610f2f610c42565b5b8235905067ffffffffffffffff811115610f4d57610f4c610c46565b5b602083019150836001820283011115610f6957610f68610c4a565b5b9250929050565b5f5f5f60408486031215610f8757610f86610c3a565b5b5f610f9486828701610f07565b935050602084013567ffffffffffffffff811115610fb557610fb4610c3e565b5b610fc186828701610f1b565b92509250509250925092565b5f82825260208201905092915050565b5f610fe782610d9f565b610ff18185610fcd565b9350611001818560208601610db9565b61100a81610dc7565b840191505092915050565b5f6020820190508181035f83015261102d8184610fdd565b905092915050565b61103e81610ee0565b82525050565b5f6020820190506110575f830184611035565b92915050565b5f6020828403121561107257611071610c3a565b5b5f61107f84828501610f07565b91505092915050565b5f819050919050565b61109a81611088565b81146110a4575f5ffd5b50565b5f813590506110b581611091565b92915050565b5f5f5f5f606085870312156110d3576110d2610c3a565b5b5f6110e087828801610f07565b945050602085013567ffffffffffffffff81111561110157611100610c3e565b5b61110d87828801610f1b565b93509350506040611120878288016110a7565b91505092959194509250565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f833560016020038436030381126111ae576111ad611186565b5b80840192508235915067ffffffffffffffff8211156111d0576111cf61118a565b5b6020830192506001820236038313156111ec576111eb61118e565b5b509250929050565b5f81905092915050565b828183375f83830152505050565b5f61121783856111f4565b93506112248385846111fe565b82840190509392505050565b5f61123c82848661120c565b91508190509392505050565b5f6112538385610fcd565b93506112608385846111fe565b61126983610dc7565b840190509392505050565b5f6040820190508181035f83015261128d818587611248565b905081810360208301526112a18184610fdd565b905094935050505056fea26469706673582212201029704c8e76cc8133cedd39a8adbebfe979b8809644c7f5e9cff417e23119d464736f6c634300081e0033", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - } - }, - "0x4e59b44847b379578588920ca78fbf26c0b4956c": { - "balance": "0x0", - "code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3" - }, - "0x0101010101010101010101010101010101010101": { - "balance": "0x1" - } - }, - "number": "0x0" -} diff --git a/bin/ev-dev/src/main.rs b/bin/ev-dev/src/main.rs deleted file mode 100644 index 715b1c53..00000000 --- a/bin/ev-dev/src/main.rs +++ /dev/null @@ -1,389 +0,0 @@ -//! ev-dev: one-command local dev chain for ev-reth. -//! -//! Spins up a fully functional Evolve chain with funded accounts, -//! similar to Hardhat Node or Anvil. - -#![allow(missing_docs, rustdoc::missing_crate_level_docs)] - -mod tui; - -use alloy_signer_local::{coins_bip39::English, MnemonicBuilder}; -use clap::Parser; -use ev_deployer::{config::DeployConfig, genesis::merge_alloc, output::build_manifest}; -use evolve_ev_reth::{ - config::EvolveConfig, - rpc::txpool::{EvolveTxpoolApiImpl, EvolveTxpoolApiServer}, -}; -use reth_ethereum_cli::Cli; -use std::{io::Write, path::PathBuf}; -use tracing::info; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; - -use ev_node::{EvolveArgs, EvolveChainSpecParser, EvolveNode}; - -#[global_allocator] -static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); - -const DEVNET_GENESIS: &str = include_str!("../assets/devnet-genesis.json"); -const HARDHAT_MNEMONIC: &str = "test test test test test test test test test test test junk"; - -fn parse_accounts(s: &str) -> Result { - let n: usize = s.parse().map_err(|e| format!("{e}"))?; - if (1..=20).contains(&n) { - Ok(n) - } else { - Err(format!("{n} is not in 1..=20")) - } -} - -/// Local dev chain for ev-reth with pre-funded accounts. -#[derive(Parser, Debug)] -#[command(name = "ev-dev", about = "One-command local Evolve dev chain")] -struct EvDevArgs { - /// Host to bind HTTP/WS RPC server - #[arg(long, default_value = "127.0.0.1")] - host: String, - - /// Port for HTTP/WS RPC server - #[arg(long, default_value_t = 8545)] - port: u16, - - /// Block time in seconds (0 = mine on tx) - #[arg(long, default_value_t = 1)] - block_time: u64, - - /// Suppress the startup banner - #[arg(long, default_value_t = false)] - silent: bool, - - /// Number of accounts to display (1..=20) - #[arg(long, default_value_t = 10, value_parser = parse_accounts)] - accounts: usize, - - /// Path to an ev-deployer TOML config to embed contracts in genesis. - #[arg(long, value_name = "PATH")] - genesis_config: Option, - - /// Launch with terminal UI instead of plain log output - #[arg(long, default_value_t = false)] - tui: bool, -} - -fn derive_keys(count: usize) -> Vec<(String, String)> { - (0..count) - .map(|i| { - let signer = MnemonicBuilder::::default() - .phrase(HARDHAT_MNEMONIC) - .index(i as u32) - .expect("valid derivation index") - .build() - .expect("valid key derivation"); - let address = signer.address(); - let key_bytes = signer.credential().to_bytes(); - ( - format!("{address}"), - format!("0x{}", alloy_primitives::hex::encode(key_bytes)), - ) - }) - .collect() -} - -fn chain_id_from_genesis() -> u64 { - let genesis: serde_json::Value = - serde_json::from_str(DEVNET_GENESIS).expect("valid genesis JSON"); - genesis["config"]["chainId"] - .as_u64() - .expect("genesis must have config.chainId") -} - -fn print_banner(args: &EvDevArgs, deploy_cfg: Option<&DeployConfig>) { - let accounts = derive_keys(args.accounts); - - println!(); - println!(r" _ "); - println!(r" | | "); - println!(r" _____ _____ __| | _____ __"); - println!(r" / _ \ \ / /___/ / _` |/ _ \ \ / /"); - println!(r" | __/\ V / | (_| | __/\ V / "); - println!(r" \___| \_/ \__,_|\___| \_/ "); - println!(); - println!(" Evolve Local Development Chain"); - println!(" =============================="); - println!(); - println!("Chain ID: {}", chain_id_from_genesis()); - println!("RPC URL: http://{}:{}", args.host, args.port); - println!( - "Block time: {}", - if args.block_time == 0 { - "auto (mine on tx)".to_string() - } else { - format!("{}s", args.block_time) - } - ); - println!(); - println!("Available Accounts"); - println!("=================="); - for (i, (addr, _)) in accounts.iter().enumerate() { - println!("({i}) {addr} (1000000 ETH)"); - } - println!(); - println!("Private Keys"); - println!("=================="); - for (i, (_, key)) in accounts.iter().enumerate() { - println!("({i}) {key}"); - } - println!(); - println!("Mnemonic: {HARDHAT_MNEMONIC}"); - println!("Derivation path: m/44'/60'/0'/0/{{index}}"); - println!(); - - if let Some(cfg) = deploy_cfg { - let config_path = args.genesis_config.as_ref().unwrap(); - println!("Genesis Contracts (from {})", config_path.display()); - println!("=================="); - let manifest = build_manifest(cfg); - if let Some(obj) = manifest.as_object() { - for (name, addr) in obj { - println!(" {name:20} {addr}"); - } - } - println!(); - } - - println!("WARNING: These accounts and keys are publicly known."); - println!("Any funds sent to them on mainnet WILL BE LOST."); - println!(); -} - -fn build_reth_args( - dev_args: &EvDevArgs, - genesis_path: String, - datadir_path: String, -) -> Vec { - let mut args = vec![ - "ev-dev".to_string(), - "node".to_string(), - "--dev".to_string(), - "--chain".to_string(), - genesis_path, - "--datadir".to_string(), - datadir_path, - "--http".to_string(), - "--http.addr".to_string(), - dev_args.host.clone(), - "--http.port".to_string(), - dev_args.port.to_string(), - "--http.api".to_string(), - "eth,net,web3,txpool,debug,trace".to_string(), - "--http.corsdomain".to_string(), - "*".to_string(), - "--ws".to_string(), - "--ws.addr".to_string(), - dev_args.host.clone(), - "--ws.port".to_string(), - dev_args.port.to_string(), - "--ws.api".to_string(), - "eth,net,web3,txpool,debug,trace".to_string(), - "--disable-discovery".to_string(), - "--no-persist-peers".to_string(), - "--port".to_string(), - "0".to_string(), - ]; - - if dev_args.block_time > 0 { - args.push("--dev.block-time".to_string()); - args.push(format!("{}s", dev_args.block_time)); - } - - args -} - -fn prepare_genesis( - deploy_cfg: &Option, -) -> (tempfile::NamedTempFile, tempfile::TempDir) { - let genesis_json = if let Some(ref cfg) = deploy_cfg { - let mut genesis: serde_json::Value = - serde_json::from_str(DEVNET_GENESIS).expect("valid genesis JSON"); - merge_alloc(cfg, &mut genesis, true).expect("failed to merge genesis config into genesis"); - serde_json::to_string(&genesis).expect("failed to serialize merged genesis") - } else { - DEVNET_GENESIS.to_string() - }; - - let mut genesis_file = - tempfile::NamedTempFile::new().expect("failed to create temp genesis file"); - genesis_file - .write_all(genesis_json.as_bytes()) - .expect("failed to write genesis"); - - let datadir = tempfile::TempDir::new().expect("failed to create temp data dir"); - - (genesis_file, datadir) -} - -fn load_genesis_config(dev_args: &EvDevArgs) -> Option { - dev_args.genesis_config.as_ref().map(|config_path| { - let mut cfg = DeployConfig::load(config_path) - .unwrap_or_else(|e| panic!("failed to load genesis config: {e}")); - - let genesis_chain_id = chain_id_from_genesis(); - if cfg.chain.chain_id != genesis_chain_id { - eprintln!( - "WARNING: genesis config chain_id ({}) differs from devnet genesis ({}), overriding to {}", - cfg.chain.chain_id, genesis_chain_id, genesis_chain_id - ); - cfg.chain.chain_id = genesis_chain_id; - } - cfg - }) -} - -fn deploy_contracts_list(deploy_cfg: &Option) -> Option> { - deploy_cfg.as_ref().map(|cfg| { - let manifest = build_manifest(cfg); - manifest - .as_object() - .map(|obj| { - obj.iter() - .map(|(name, addr)| (name.clone(), addr.as_str().unwrap_or("").to_string())) - .collect() - }) - .unwrap_or_default() - }) -} - -fn main() { - reth_cli_util::sigsegv_handler::install(); - - if std::env::var_os("RUST_BACKTRACE").is_none() { - std::env::set_var("RUST_BACKTRACE", "1"); - } - - let dev_args = EvDevArgs::parse(); - let deploy_cfg = load_genesis_config(&dev_args); - - if dev_args.tui { - run_with_tui(dev_args, deploy_cfg); - } else { - run_without_tui(dev_args, deploy_cfg); - } -} - -fn run_without_tui(dev_args: EvDevArgs, deploy_cfg: Option) { - if !dev_args.silent { - print_banner(&dev_args, deploy_cfg.as_ref()); - } - - let (genesis_file, datadir) = prepare_genesis(&deploy_cfg); - let genesis_path = genesis_file - .path() - .to_str() - .expect("valid path") - .to_string(); - let datadir_path = datadir.path().to_str().expect("valid path").to_string(); - let args = build_reth_args(&dev_args, genesis_path, datadir_path); - - let cli = match Cli::::try_parse_from(args) { - Ok(cli) => cli, - Err(err) => { - eprintln!("{err}"); - std::process::exit(2); - } - }; - - if let Err(err) = cli.run(|builder, _evolve_args| async move { - info!("=== EV-DEV: Starting local development chain ==="); - let handle = builder - .node(EvolveNode::new()) - .extend_rpc_modules(move |ctx| { - let evolve_cfg = EvolveConfig::default(); - let evolve_txpool = - EvolveTxpoolApiImpl::new(ctx.pool().clone(), evolve_cfg.max_txpool_bytes); - ctx.modules.merge_configured(evolve_txpool.into_rpc())?; - Ok(()) - }) - .launch_with_debug_capabilities() - .await?; - - info!("=== EV-DEV: Local chain running - RPC ready ==="); - handle.node_exit_future.await - }) { - eprintln!("Error: {err:?}"); - std::process::exit(1); - } -} - -fn run_with_tui(dev_args: EvDevArgs, deploy_cfg: Option) { - let (log_tx, log_rx) = tokio::sync::mpsc::channel(10_000); - - // Install our tracing subscriber with the TUI layer BEFORE cli.run(). - // When reth's internal init_tracing calls try_init(), it will find a - // subscriber already installed and silently skip its own setup. - let tui_layer = tui::TuiTracingLayer::new(log_tx); - let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()); - - tracing_subscriber::registry() - .with(filter) - .with(tui_layer) - .init(); - - let chain_id = chain_id_from_genesis(); - let rpc_url = format!("http://{}:{}", dev_args.host, dev_args.port); - let block_time = dev_args.block_time; - let accounts = derive_keys(dev_args.accounts); - let contracts = deploy_contracts_list(&deploy_cfg); - - let (balance_tx, balance_rx) = tokio::sync::mpsc::channel(16); - let app = tui::App::new( - chain_id, - rpc_url.clone(), - block_time, - accounts.clone(), - contracts, - log_rx, - balance_rx, - ); - - let (genesis_file, datadir) = prepare_genesis(&deploy_cfg); - let genesis_path = genesis_file - .path() - .to_str() - .expect("valid path") - .to_string(); - let datadir_path = datadir.path().to_str().expect("valid path").to_string(); - let args = build_reth_args(&dev_args, genesis_path, datadir_path); - - let cli = match Cli::::try_parse_from(args) { - Ok(cli) => cli, - Err(err) => { - eprintln!("{err}"); - std::process::exit(2); - } - }; - - if let Err(err) = cli.run(|builder, _evolve_args| async move { - info!("=== EV-DEV: Starting local development chain (TUI) ==="); - let _handle = builder - .node(EvolveNode::new()) - .extend_rpc_modules(move |ctx| { - let evolve_cfg = EvolveConfig::default(); - let evolve_txpool = - EvolveTxpoolApiImpl::new(ctx.pool().clone(), evolve_cfg.max_txpool_bytes); - ctx.modules.merge_configured(evolve_txpool.into_rpc())?; - Ok(()) - }) - .launch_with_debug_capabilities() - .await?; - - info!("=== EV-DEV: Local chain running - RPC ready ==="); - - tui::spawn_balance_poller(rpc_url, accounts, balance_tx); - tui::run(app).await?; - - Ok(()) - }) { - let _ = tui::restore_terminal(); - eprintln!("Error: {err:?}"); - std::process::exit(1); - } -} diff --git a/bin/ev-dev/src/tui/app.rs b/bin/ev-dev/src/tui/app.rs deleted file mode 100644 index 93596c14..00000000 --- a/bin/ev-dev/src/tui/app.rs +++ /dev/null @@ -1,354 +0,0 @@ -use std::{collections::VecDeque, time::Instant}; - -use alloy_primitives::{Address, U256}; -use tokio::sync::mpsc; - -const MAX_LOGS: usize = 1000; -const MAX_BLOCKS: usize = 200; - -#[derive(Debug, Clone)] -pub(crate) struct BlockInfo { - pub(crate) number: u64, - pub(crate) hash: String, - pub(crate) tx_count: u64, - pub(crate) gas_used: u64, -} - -#[derive(Debug, Clone)] -pub(crate) struct LogEntry { - pub(crate) level: tracing::Level, - pub(crate) target: String, - pub(crate) message: String, - pub(crate) fields: Vec<(String, String)>, - pub(crate) timestamp: Instant, -} - -#[derive(Debug, Clone)] -pub(crate) struct TxInfo { - pub(crate) hash: String, - pub(crate) from: String, - pub(crate) to: String, - pub(crate) value: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct BlockDetail { - pub(crate) number: u64, - pub(crate) txs: Vec, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum Panel { - Blocks, - Logs, - Accounts, -} - -pub(crate) struct App { - // Static - pub(crate) chain_id: u64, - pub(crate) rpc_url: String, - pub(crate) block_time: u64, - pub(crate) accounts: Vec<(String, String)>, - pub(crate) deploy_contracts: Option>, - - // Dynamic - pub(crate) blocks: VecDeque, - pub(crate) logs: VecDeque, - pub(crate) current_block: u64, - pub(crate) start_time: Instant, - pub(crate) balances: Vec, - - // UI state - pub(crate) active_panel: Panel, - pub(crate) log_scroll: usize, - pub(crate) block_selected: usize, - pub(crate) account_selected: usize, - pub(crate) clipboard_msg: Option<(String, Instant)>, - pub(crate) block_detail: Option, - pub(crate) should_quit: bool, - - // Channels - pub(crate) log_rx: mpsc::Receiver, - pub(crate) balance_rx: mpsc::Receiver>, - pub(crate) detail_tx: mpsc::Sender, - pub(crate) detail_rx: mpsc::Receiver, -} - -impl App { - pub(crate) fn new( - chain_id: u64, - rpc_url: String, - block_time: u64, - accounts: Vec<(String, String)>, - deploy_contracts: Option>, - log_rx: mpsc::Receiver, - balance_rx: mpsc::Receiver>, - ) -> Self { - let initial_balance = "1000000 ETH".to_string(); - let balances = vec![initial_balance; accounts.len()]; - let (detail_tx, detail_rx) = mpsc::channel(4); - Self { - chain_id, - rpc_url, - block_time, - accounts, - deploy_contracts, - blocks: VecDeque::new(), - logs: VecDeque::new(), - current_block: 0, - start_time: Instant::now(), - balances, - active_panel: Panel::Logs, - log_scroll: 0, - block_selected: 0, - account_selected: 0, - clipboard_msg: None, - block_detail: None, - should_quit: false, - log_rx, - balance_rx, - detail_tx, - detail_rx, - } - } - - pub(crate) fn drain_balances(&mut self) { - while let Ok(new_balances) = self.balance_rx.try_recv() { - self.balances = new_balances; - } - } - - pub(crate) fn drain_logs(&mut self) { - while let Ok(entry) = self.log_rx.try_recv() { - if entry.message == "built block" { - if let Some(block) = self.parse_block_from_fields(&entry.fields) { - self.current_block = block.number; - self.blocks.push_front(block); - if self.blocks.len() > MAX_BLOCKS { - self.blocks.pop_back(); - } - } - } - - self.logs.push_back(entry); - if self.logs.len() > MAX_LOGS { - self.logs.pop_front(); - } - } - } - - fn parse_block_from_fields(&self, fields: &[(String, String)]) -> Option { - let mut number = None; - let mut hash = String::new(); - let mut tx_count = 0; - let mut gas_used = 0; - - for (k, v) in fields { - match k.as_str() { - "block_number" => number = v.parse().ok(), - "block_hash" => { - let h = v.trim_matches('"'); - hash = if h.len() > 10 { - format!("{}..{}", &h[..6], &h[h.len() - 4..]) - } else { - h.to_string() - }; - } - "tx_count" => tx_count = v.parse().unwrap_or(0), - "gas_used" => gas_used = v.parse().unwrap_or(0), - _ => {} - } - } - - number.map(|n| BlockInfo { - number: n, - hash, - tx_count, - gas_used, - }) - } - - pub(crate) const fn next_panel(&mut self) { - self.active_panel = match self.active_panel { - Panel::Blocks => Panel::Logs, - Panel::Logs => Panel::Accounts, - Panel::Accounts => Panel::Blocks, - }; - } - - pub(crate) const fn scroll_up(&mut self) { - match self.active_panel { - Panel::Logs => self.log_scroll = self.log_scroll.saturating_add(1), - Panel::Blocks => { - self.block_selected = self.block_selected.saturating_sub(1); - } - Panel::Accounts => { - self.account_selected = self.account_selected.saturating_sub(1); - } - } - } - - pub(crate) fn scroll_down(&mut self) { - match self.active_panel { - Panel::Logs => self.log_scroll = self.log_scroll.saturating_sub(1), - Panel::Blocks => { - if !self.blocks.is_empty() { - self.block_selected = (self.block_selected + 1).min(self.blocks.len() - 1); - } - } - Panel::Accounts => { - if !self.accounts.is_empty() { - self.account_selected = - (self.account_selected + 1).min(self.accounts.len() - 1); - } - } - } - } - - pub(crate) fn copy_account_address(&mut self) { - if let Some((addr, _)) = self.accounts.get(self.account_selected) { - if let Ok(mut clipboard) = arboard::Clipboard::new() { - let _ = clipboard.set_text(addr.clone()); - let truncated = if addr.len() > 10 { - format!("{}..{}", &addr[..6], &addr[addr.len() - 4..]) - } else { - addr.clone() - }; - self.clipboard_msg = Some((format!("Copied address {truncated}"), Instant::now())); - } - } - } - - pub(crate) fn copy_account_key(&mut self) { - if let Some((_, key)) = self.accounts.get(self.account_selected) { - if let Ok(mut clipboard) = arboard::Clipboard::new() { - let _ = clipboard.set_text(key.clone()); - self.clipboard_msg = Some(("Copied private key".to_string(), Instant::now())); - } - } - } - - pub(crate) fn fetch_block_detail(&self) { - let Some(block_info) = self.blocks.get(self.block_selected) else { - return; - }; - let tx = self.detail_tx.clone(); - let rpc_url = self.rpc_url.clone(); - let block_num = block_info.number; - - tokio::spawn(async move { - use alloy_network::TransactionResponse; - use alloy_provider::{Provider, ProviderBuilder}; - use alloy_rpc_types::{BlockNumberOrTag, TransactionTrait}; - - let provider = - ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL")); - - let result = provider - .get_block_by_number(BlockNumberOrTag::Number(block_num)) - .full() - .await; - - let txs = match result { - Ok(Some(block)) => block - .transactions - .into_transactions() - .map(|t| { - let hash = format!("{}", t.tx_hash()); - let from = format!("{}", t.from()); - let to = t.to().map_or_else( - || "Contract Creation".into(), - |a| truncate_hex(&format!("{a}")), - ); - let value = format_ether(t.value()); - TxInfo { - hash: truncate_hex(&hash), - from: truncate_hex(&from), - to, - value, - } - }) - .collect(), - _ => vec![], - }; - - let _ = tx - .send(BlockDetail { - number: block_num, - txs, - }) - .await; - }); - } - - pub(crate) fn drain_block_detail(&mut self) { - if let Ok(detail) = self.detail_rx.try_recv() { - self.block_detail = Some(detail); - } - } - - pub(crate) fn close_block_detail(&mut self) { - self.block_detail = None; - } -} - -fn truncate_hex(s: &str) -> String { - if s.len() > 10 { - format!("{}..{}", &s[..6], &s[s.len() - 4..]) - } else { - s.to_string() - } -} - -fn format_ether(wei: U256) -> String { - let ether_unit = U256::from(10u64).pow(U256::from(18)); - let whole = wei / ether_unit; - let remainder = wei % ether_unit; - - let frac_digits = 4; - let frac_unit = U256::from(10u64).pow(U256::from(18 - frac_digits)); - let frac = remainder / frac_unit; - - let frac_val: u64 = frac.try_into().unwrap_or(0); - let formatted = format!("{whole}.{frac_val:0>4}"); - // Trim trailing zeros but keep at least one decimal - let trimmed = formatted.trim_end_matches('0'); - let trimmed = trimmed.trim_end_matches('.'); - format!("{trimmed} ETH") -} - -pub(crate) fn spawn_balance_poller( - rpc_url: String, - accounts: Vec<(String, String)>, - tx: mpsc::Sender>, -) { - let addresses: Vec
= accounts - .iter() - .filter_map(|(addr, _)| addr.parse().ok()) - .collect(); - - tokio::spawn(async move { - use alloy_provider::{Provider, ProviderBuilder}; - - let mut interval = tokio::time::interval(std::time::Duration::from_secs(2)); - loop { - interval.tick().await; - - let provider = - ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL")); - - let mut balances = Vec::with_capacity(addresses.len()); - for addr in &addresses { - match provider.get_balance(*addr).await { - Ok(bal) => balances.push(format_ether(bal)), - Err(_) => balances.push("? ETH".to_string()), - } - } - - if tx.send(balances).await.is_err() { - break; - } - } - }); -} diff --git a/bin/ev-dev/src/tui/events.rs b/bin/ev-dev/src/tui/events.rs deleted file mode 100644 index 8e23ea9a..00000000 --- a/bin/ev-dev/src/tui/events.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - -use super::app::{App, Panel}; - -pub(crate) fn handle_key(app: &mut App, key: KeyEvent) { - // If block detail overlay is open, handle it separately - if app.block_detail.is_some() { - match key.code { - KeyCode::Esc | KeyCode::Enter => app.close_block_detail(), - KeyCode::Char('q') => app.should_quit = true, - KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => { - app.should_quit = true; - } - _ => {} - } - return; - } - - match key.code { - KeyCode::Char('q') | KeyCode::Esc => app.should_quit = true, - KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => { - app.should_quit = true; - } - KeyCode::Tab => app.next_panel(), - KeyCode::Up => app.scroll_up(), - KeyCode::Down => app.scroll_down(), - KeyCode::Char('a') if app.active_panel == Panel::Accounts => { - app.copy_account_address(); - } - KeyCode::Char('k') if app.active_panel == Panel::Accounts => { - app.copy_account_key(); - } - KeyCode::Enter if app.active_panel == Panel::Blocks => { - app.fetch_block_detail(); - } - _ => {} - } -} diff --git a/bin/ev-dev/src/tui/mod.rs b/bin/ev-dev/src/tui/mod.rs deleted file mode 100644 index 10f09348..00000000 --- a/bin/ev-dev/src/tui/mod.rs +++ /dev/null @@ -1,74 +0,0 @@ -pub(crate) mod app; -mod events; -mod tracing_layer; -mod ui; - -pub(crate) use app::{spawn_balance_poller, App}; -pub(crate) use tracing_layer::TuiTracingLayer; - -use std::io::{self, stdout}; - -use crossterm::{ - event::{Event, EventStream}, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - ExecutableCommand, -}; -use futures::StreamExt; -use ratatui::prelude::CrosstermBackend; - -struct TerminalGuard; - -impl Drop for TerminalGuard { - fn drop(&mut self) { - let _ = disable_raw_mode(); - let _ = stdout().execute(LeaveAlternateScreen); - } -} - -pub(crate) async fn run(mut app: App) -> eyre::Result<()> { - let original_hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(move |info| { - let _ = disable_raw_mode(); - let _ = stdout().execute(LeaveAlternateScreen); - original_hook(info); - })); - - enable_raw_mode()?; - stdout().execute(EnterAlternateScreen)?; - let _guard = TerminalGuard; - - let backend = CrosstermBackend::new(stdout()); - let mut terminal = ratatui::Terminal::new(backend)?; - - let mut event_stream = EventStream::new(); - let mut tick = tokio::time::interval(std::time::Duration::from_millis(100)); - - loop { - if app.should_quit { - break; - } - - tokio::select! { - _ = tick.tick() => { - app.drain_logs(); - app.drain_balances(); - app.drain_block_detail(); - terminal.draw(|frame| ui::draw(frame, &app))?; - } - maybe_event = event_stream.next() => { - if let Some(Ok(Event::Key(key))) = maybe_event { - events::handle_key(&mut app, key); - } - } - } - } - - // Terminal restored by TerminalGuard drop - Ok(()) -} - -pub(crate) fn restore_terminal() -> io::Result<()> { - disable_raw_mode()?; - stdout().execute(LeaveAlternateScreen)?; - Ok(()) -} diff --git a/bin/ev-dev/src/tui/tracing_layer.rs b/bin/ev-dev/src/tui/tracing_layer.rs deleted file mode 100644 index 7ea78d5e..00000000 --- a/bin/ev-dev/src/tui/tracing_layer.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::time::Instant; - -use tokio::sync::mpsc; -use tracing::{ - field::{Field, Visit}, - Subscriber, -}; -use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer}; - -use super::app::LogEntry; - -struct FieldCollector { - fields: Vec<(String, String)>, -} - -impl FieldCollector { - const fn new() -> Self { - Self { fields: Vec::new() } - } - - fn take_message(&mut self) -> String { - if let Some(pos) = self.fields.iter().position(|(k, _)| k == "message") { - self.fields.remove(pos).1 - } else { - String::new() - } - } -} - -impl Visit for FieldCollector { - fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) { - self.fields - .push((field.name().to_string(), format!("{:?}", value))); - } - - fn record_str(&mut self, field: &Field, value: &str) { - self.fields - .push((field.name().to_string(), value.to_string())); - } - - fn record_u64(&mut self, field: &Field, value: u64) { - self.fields - .push((field.name().to_string(), value.to_string())); - } - - fn record_i64(&mut self, field: &Field, value: i64) { - self.fields - .push((field.name().to_string(), value.to_string())); - } -} - -pub(crate) struct TuiTracingLayer { - tx: mpsc::Sender, -} - -impl TuiTracingLayer { - pub(crate) const fn new(tx: mpsc::Sender) -> Self { - Self { tx } - } -} - -impl Layer for TuiTracingLayer -where - S: Subscriber + for<'lookup> LookupSpan<'lookup>, -{ - fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) { - let mut collector = FieldCollector::new(); - event.record(&mut collector); - - let message = collector.take_message(); - let metadata = event.metadata(); - - let entry = LogEntry { - level: *metadata.level(), - target: metadata.target().to_string(), - message, - fields: collector.fields, - timestamp: Instant::now(), - }; - - let _ = self.tx.try_send(entry); - } -} diff --git a/bin/ev-dev/src/tui/ui.rs b/bin/ev-dev/src/tui/ui.rs deleted file mode 100644 index 8bf4cff2..00000000 --- a/bin/ev-dev/src/tui/ui.rs +++ /dev/null @@ -1,448 +0,0 @@ -use ratatui::{ - layout::{Constraint, Layout, Rect}, - style::{Color, Modifier, Style}, - text::{Line, Span}, - widgets::{Block, Borders, Cell, Clear, List, ListItem, Paragraph, Row, Table}, - Frame, -}; - -use super::app::{App, BlockDetail, Panel}; - -fn border_style(app: &App, panel: Panel) -> Style { - if app.active_panel == panel { - Style::default().fg(Color::Cyan) - } else { - Style::default().fg(Color::DarkGray) - } -} - -const fn level_color(level: &tracing::Level) -> Color { - match *level { - tracing::Level::ERROR => Color::Red, - tracing::Level::WARN => Color::Yellow, - tracing::Level::INFO => Color::Green, - tracing::Level::DEBUG | tracing::Level::TRACE => Color::DarkGray, - } -} - -fn format_uptime(secs: u64) -> String { - let h = secs / 3600; - let m = (secs % 3600) / 60; - let s = secs % 60; - if h > 0 { - format!("{h}h{m:02}m{s:02}s") - } else if m > 0 { - format!("{m}m{s:02}s") - } else { - format!("{s}s") - } -} - -fn format_gas(gas: u64) -> String { - if gas >= 1_000_000 { - format!("{:.1}M", gas as f64 / 1_000_000.0) - } else if gas >= 1_000 { - format!("{:.1}k", gas as f64 / 1_000.0) - } else { - gas.to_string() - } -} - -pub(crate) fn draw(frame: &mut Frame<'_>, app: &App) { - let area = frame.area(); - - let outer = Layout::vertical([ - Constraint::Length(3), // header - Constraint::Min(6), // main content - Constraint::Length(3), // footer - ]) - .split(area); - - draw_header(frame, app, outer[0]); - draw_main(frame, app, outer[1]); - draw_footer(frame, app, outer[2]); - - if let Some(ref detail) = app.block_detail { - draw_block_detail(frame, detail, area); - } -} - -fn draw_header(frame: &mut Frame<'_>, app: &App, area: Rect) { - let block_time_str = if app.block_time == 0 { - "auto".to_string() - } else { - format!("{}s", app.block_time) - }; - - let text = Line::from(vec![ - Span::styled(" Chain: ", Style::default().fg(Color::DarkGray)), - Span::styled(app.chain_id.to_string(), Style::default().fg(Color::White)), - Span::styled(" RPC: ", Style::default().fg(Color::DarkGray)), - Span::styled(&app.rpc_url, Style::default().fg(Color::Cyan)), - Span::styled(" Block: ", Style::default().fg(Color::DarkGray)), - Span::styled(block_time_str, Style::default().fg(Color::White)), - ]); - - let block = Block::default() - .borders(Borders::ALL) - .title(" ev-dev ") - .title_style( - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD), - ) - .border_style(Style::default().fg(Color::Cyan)); - - let paragraph = Paragraph::new(text).block(block); - frame.render_widget(paragraph, area); -} - -fn draw_main(frame: &mut Frame<'_>, app: &App, area: Rect) { - let main_split = Layout::vertical([ - Constraint::Percentage(45), // top row (blocks + accounts) - Constraint::Percentage(55), // logs - ]) - .split(area); - - let top_split = Layout::horizontal([ - Constraint::Percentage(55), // blocks - Constraint::Percentage(45), // accounts - ]) - .split(main_split[0]); - - draw_blocks(frame, app, top_split[0]); - draw_accounts(frame, app, top_split[1]); - draw_logs(frame, app, main_split[1]); -} - -fn draw_blocks(frame: &mut Frame<'_>, app: &App, area: Rect) { - let is_focused = app.active_panel == Panel::Blocks; - - let block = Block::default() - .borders(Borders::ALL) - .title(" Blocks ") - .border_style(border_style(app, Panel::Blocks)); - - let header = Row::new(vec![ - Cell::from("Block").style(Style::default().add_modifier(Modifier::BOLD)), - Cell::from("Hash").style(Style::default().add_modifier(Modifier::BOLD)), - Cell::from("Txs").style(Style::default().add_modifier(Modifier::BOLD)), - Cell::from("Gas").style(Style::default().add_modifier(Modifier::BOLD)), - ]) - .style(Style::default().fg(Color::DarkGray)); - - // Auto-scroll to keep selected block visible - let inner_height = area.height.saturating_sub(4) as usize; // borders + header + header separator - let scroll = if inner_height > 0 && app.block_selected >= inner_height { - app.block_selected - inner_height + 1 - } else { - 0 - }; - - let rows: Vec> = app - .blocks - .iter() - .enumerate() - .skip(scroll) - .take(inner_height.max(1)) - .map(|(i, b)| { - let selected = is_focused && i == app.block_selected; - let marker = if selected { "▸" } else { " " }; - let style = if selected { - Style::default().fg(Color::Cyan) - } else { - Style::default() - }; - - Row::new(vec![ - Cell::from(format!("{marker}#{}", b.number)), - Cell::from(b.hash.clone()).style(Style::default().fg(Color::DarkGray)), - Cell::from(format!("{}", b.tx_count)), - Cell::from(format_gas(b.gas_used)), - ]) - .style(style) - }) - .collect(); - - let widths = [ - Constraint::Length(10), - Constraint::Length(12), - Constraint::Length(5), - Constraint::Min(6), - ]; - - let table = Table::new(rows, widths).header(header).block(block); - - frame.render_widget(table, area); -} - -fn draw_accounts(frame: &mut Frame<'_>, app: &App, area: Rect) { - let is_focused = app.active_panel == Panel::Accounts; - let mut items: Vec> = app - .accounts - .iter() - .enumerate() - .map(|(i, (addr, _key))| { - let truncated = if addr.len() > 10 { - format!("{}..{}", &addr[..6], &addr[addr.len() - 4..]) - } else { - addr.clone() - }; - let balance = app - .balances - .get(i) - .cloned() - .unwrap_or_else(|| "? ETH".to_string()); - - let selected = is_focused && i == app.account_selected; - let marker = if selected { "▸ " } else { " " }; - let addr_color = if selected { Color::Cyan } else { Color::White }; - - ListItem::new(Line::from(vec![ - Span::styled(marker, Style::default().fg(Color::Cyan)), - Span::styled(format!("({i}) "), Style::default().fg(Color::DarkGray)), - Span::styled(truncated, Style::default().fg(addr_color)), - Span::styled(format!(" {balance}"), Style::default().fg(Color::Green)), - ])) - }) - .collect(); - - if let Some(ref contracts) = app.deploy_contracts { - items.push(ListItem::new(Line::from(""))); - items.push(ListItem::new(Line::from(Span::styled( - "Genesis Contracts", - Style::default() - .fg(Color::Yellow) - .add_modifier(Modifier::BOLD), - )))); - for (name, addr) in contracts { - let truncated = if addr.len() > 10 { - format!("{}..{}", &addr[..6], &addr[addr.len() - 4..]) - } else { - addr.clone() - }; - items.push(ListItem::new(Line::from(vec![ - Span::styled(format!("{name:18} "), Style::default().fg(Color::DarkGray)), - Span::styled(truncated, Style::default().fg(Color::White)), - ]))); - } - } - - let block = Block::default() - .borders(Borders::ALL) - .title(" Accounts ") - .border_style(border_style(app, Panel::Accounts)); - - let list = List::new(items).block(block); - frame.render_widget(list, area); -} - -fn draw_logs(frame: &mut Frame<'_>, app: &App, area: Rect) { - let block = Block::default() - .borders(Borders::ALL) - .title(" Logs ") - .border_style(border_style(app, Panel::Logs)); - - let inner_height = area.height.saturating_sub(2) as usize; - let total = app.logs.len(); - - let end = total.saturating_sub(app.log_scroll); - let start = end.saturating_sub(inner_height); - - let items: Vec> = app - .logs - .iter() - .skip(start) - .take(end.saturating_sub(start)) - .map(|entry| { - let color = level_color(&entry.level); - let level_str = format!("{:5}", entry.level); - let elapsed = entry.timestamp.elapsed().as_secs(); - let ts = format!("{elapsed:>4}s"); - - let target_short = entry.target.rsplit("::").next().unwrap_or(&entry.target); - - let mut spans = vec![ - Span::styled(ts, Style::default().fg(Color::DarkGray)), - Span::raw(" "), - Span::styled(level_str, Style::default().fg(color)), - Span::raw(" "), - Span::styled( - format!("{target_short:>16} "), - Style::default().fg(Color::DarkGray), - ), - Span::styled(entry.message.clone(), Style::default().fg(Color::White)), - ]; - - for (k, v) in &entry.fields { - spans.push(Span::raw(" ")); - spans.push(Span::styled( - format!("{k}="), - Style::default().fg(Color::DarkGray), - )); - spans.push(Span::styled(v.clone(), Style::default().fg(Color::Gray))); - } - - ListItem::new(Line::from(spans)) - }) - .collect(); - - let list = List::new(items).block(block); - frame.render_widget(list, area); -} - -fn draw_block_detail(frame: &mut Frame<'_>, detail: &BlockDetail, area: Rect) { - let popup = centered_rect(80, 60, area); - frame.render_widget(Clear, popup); - - let title = format!(" Block #{} ({} txs) ", detail.number, detail.txs.len()); - - let block = Block::default() - .borders(Borders::ALL) - .title(title) - .title_style( - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD), - ) - .border_style(Style::default().fg(Color::Cyan)); - - if detail.txs.is_empty() { - let text = Paragraph::new(Line::from(vec![Span::styled( - " No transactions in this block", - Style::default().fg(Color::DarkGray), - )])) - .block(block); - frame.render_widget(text, popup); - } else { - let header = Row::new(vec![ - Cell::from("Hash").style(Style::default().add_modifier(Modifier::BOLD)), - Cell::from("From").style(Style::default().add_modifier(Modifier::BOLD)), - Cell::from("To").style(Style::default().add_modifier(Modifier::BOLD)), - Cell::from("Value").style(Style::default().add_modifier(Modifier::BOLD)), - ]) - .style(Style::default().fg(Color::DarkGray)); - - let rows: Vec> = detail - .txs - .iter() - .map(|tx| { - Row::new(vec![ - Cell::from(tx.hash.clone()).style(Style::default().fg(Color::DarkGray)), - Cell::from(tx.from.clone()), - Cell::from(tx.to.clone()), - Cell::from(tx.value.clone()).style(Style::default().fg(Color::Green)), - ]) - }) - .collect(); - - let widths = [ - Constraint::Length(14), - Constraint::Length(14), - Constraint::Length(18), - Constraint::Min(10), - ]; - - let table = Table::new(rows, widths).header(header).block(block); - frame.render_widget(table, popup); - } -} - -fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { - let popup_layout = Layout::vertical([ - Constraint::Percentage((100 - percent_y) / 2), - Constraint::Percentage(percent_y), - Constraint::Percentage((100 - percent_y) / 2), - ]) - .split(r); - - Layout::horizontal([ - Constraint::Percentage((100 - percent_x) / 2), - Constraint::Percentage(percent_x), - Constraint::Percentage((100 - percent_x) / 2), - ]) - .split(popup_layout[1])[1] -} - -fn draw_footer(frame: &mut Frame<'_>, app: &App, area: Rect) { - let uptime = app.start_time.elapsed().as_secs(); - - // Check for clipboard flash message (show for 2 seconds) - let clipboard_flash = app.clipboard_msg.as_ref().and_then(|(msg, when)| { - if when.elapsed().as_secs() < 2 { - Some(msg.clone()) - } else { - None - } - }); - - let mut spans = vec![ - Span::styled(" Up: ", Style::default().fg(Color::DarkGray)), - Span::styled(format_uptime(uptime), Style::default().fg(Color::White)), - Span::styled(" Block: ", Style::default().fg(Color::DarkGray)), - Span::styled( - format!("#{}", app.current_block), - Style::default().fg(Color::Cyan), - ), - Span::styled(" | ", Style::default().fg(Color::DarkGray)), - Span::styled("[q]", Style::default().fg(Color::Yellow)), - Span::styled("uit ", Style::default().fg(Color::DarkGray)), - Span::styled("[Tab]", Style::default().fg(Color::Yellow)), - Span::styled("focus ", Style::default().fg(Color::DarkGray)), - ]; - - if app.block_detail.is_some() { - spans.extend([ - Span::styled("[Esc]", Style::default().fg(Color::Yellow)), - Span::styled("close", Style::default().fg(Color::DarkGray)), - ]); - } else { - match app.active_panel { - Panel::Accounts => { - spans.extend([ - Span::styled("[↑↓]", Style::default().fg(Color::Yellow)), - Span::styled("select ", Style::default().fg(Color::DarkGray)), - Span::styled("[a]", Style::default().fg(Color::Yellow)), - Span::styled("ddress ", Style::default().fg(Color::DarkGray)), - Span::styled("[k]", Style::default().fg(Color::Yellow)), - Span::styled("ey", Style::default().fg(Color::DarkGray)), - ]); - } - Panel::Blocks => { - spans.extend([ - Span::styled("[↑↓]", Style::default().fg(Color::Yellow)), - Span::styled("select ", Style::default().fg(Color::DarkGray)), - Span::styled("[Enter]", Style::default().fg(Color::Yellow)), - Span::styled("txs", Style::default().fg(Color::DarkGray)), - ]); - } - Panel::Logs => { - spans.extend([ - Span::styled("[↑↓]", Style::default().fg(Color::Yellow)), - Span::styled("scroll", Style::default().fg(Color::DarkGray)), - ]); - } - } - } - - if let Some(msg) = clipboard_flash { - spans.extend([ - Span::styled(" ", Style::default()), - Span::styled( - format!("✓ {msg}"), - Style::default() - .fg(Color::Green) - .add_modifier(Modifier::BOLD), - ), - ]); - } - - let text = Line::from(spans); - - let block = Block::default() - .borders(Borders::ALL) - .border_style(Style::default().fg(Color::DarkGray)); - - let paragraph = Paragraph::new(text).block(block); - frame.render_widget(paragraph, area); -} diff --git a/justfile b/justfile index 941833a6..9e233307 100644 --- a/justfile +++ b/justfile @@ -34,18 +34,6 @@ build-maxperf: build-all: {{cargo}} build --workspace --release -# Build the ev-deployer binary in release mode -build-deployer: - {{cargo}} build --release --bin ev-deployer - -# Install ev-dev to ~/.cargo/bin -install-ev-dev: - {{cargo}} install --path bin/ev-dev - -# Install ev-deployer to ~/.cargo/bin -install-ev-deployer: - {{cargo}} install --path bin/ev-deployer - # Testing ────────────────────────────────────────────── # Run all tests @@ -76,10 +64,6 @@ test-evolve: test-common: {{cargo}} test -p ev-common -# Test the deployer crate -test-deployer: - {{cargo}} test -p ev-deployer - # Development ────────────────────────────────────────── # Run the ev-reth node with default settings @@ -90,14 +74,6 @@ run: build-dev run-dev: build-dev RUST_LOG=debug ./{{target_dir}}/debug/{{binary}} node -# Build the ev-dev binary in release mode -build-ev-dev: - {{cargo}} build --release --bin ev-dev - -# Build and run the local dev chain -dev-chain: build-ev-dev - ./{{target_dir}}/release/ev-dev - # Format code using rustfmt (nightly) fmt: {{cargo}} +nightly fmt --all