Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,31 @@ Implementation details:
- No runtime environment variables are required; the chainspec carries the policy alongside other fork settings
- When not configured, the EVM operates normally with standard fee burning

### Custom EIP-1559 Parameters (Custom Networks Only)

ev-reth also lets you override EIP-1559 base fee parameters through the same `evolve` stanza in
your chainspec. This is consensus-critical: all nodes must use the same values.

```json
"config": {
...,
"evolve": {
"baseFeeMaxChangeDenominator": 8,
"baseFeeElasticityMultiplier": 2,
"initialBaseFeePerGas": 1000000000
}
}
```

Notes:

- `baseFeeMaxChangeDenominator` and `baseFeeElasticityMultiplier` override the EIP-1559 formula.
- `initialBaseFeePerGas` only applies when `londonBlock` is `0` (London at genesis). It updates the
genesis `baseFeePerGas` value; if London is activated later, the initial base fee remains
hardcoded to the EIP-1559 constant.
- The node will fail fast if these values are invalid or inconsistent.
- See `docs/eip1559-configuration.md` for recommended values at 100ms block times.

### Custom Contract Size Limit

By default, Ethereum enforces a 24KB contract size limit per [EIP-170](https://eips.ethereum.org/EIPS/eip-170). If your network requires larger contracts, `ev-reth` supports configuring a custom limit via the chainspec.
Expand Down
12 changes: 6 additions & 6 deletions bin/ev-reth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use evolve_ev_reth::{
config::EvolveConfig,
rpc::txpool::{EvolveTxpoolApiImpl, EvolveTxpoolApiServer},
};
use reth_ethereum_cli::{chainspec::EthereumChainSpecParser, Cli};
use reth_ethereum_cli::Cli;
use reth_tracing_otlp::layer as otlp_layer;
use tracing::info;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

use ev_node::{log_startup, EvolveArgs, EvolveNode};
use ev_node::{log_startup, EvolveArgs, EvolveChainSpecParser, EvolveNode};

#[global_allocator]
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
Expand Down Expand Up @@ -47,8 +47,8 @@ fn main() {
init_otlp_tracing();
}

if let Err(err) = Cli::<EthereumChainSpecParser, EvolveArgs>::parse().run(
|builder, _evolve_args| async move {
if let Err(err) =
Cli::<EvolveChainSpecParser, EvolveArgs>::parse().run(|builder, _evolve_args| async move {
log_startup();
let handle = builder
.node(EvolveNode::new())
Expand All @@ -67,8 +67,8 @@ fn main() {

info!("=== EV-RETH: Node launched successfully with ev-reth payload builder ===");
handle.node_exit_future.await
},
) {
})
{
eprintln!("Error: {err:?}");
std::process::exit(1);
}
Expand Down
8 changes: 4 additions & 4 deletions contracts/script/GenerateAdminProxyAlloc.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ contract GenerateAdminProxyAlloc is Script {
console.log(' "alloc": {');
console.log(' "000000000000000000000000000000000000Ad00": {');
console.log(' "balance": "0x0",');
console.log(' "code": "0x%s",', vm.toString(runtimeCode));
console.log(' "code": "%s",', vm.toString(runtimeCode));
console.log(' "storage": {');
console.log(' "0x0": "0x%s"', vm.toString(ownerSlotValue));
console.log(' "0x0": "%s"', vm.toString(ownerSlotValue));
console.log(" }");
console.log(" }");
console.log(" }");
Expand Down Expand Up @@ -100,9 +100,9 @@ contract GenerateAdminProxyAllocJSON is Script {
// Output minimal JSON that can be merged into genesis
string memory json = string(
abi.encodePacked(
'{"000000000000000000000000000000000000Ad00":{"balance":"0x0","code":"0x',
'{"000000000000000000000000000000000000Ad00":{"balance":"0x0","code":"',
vm.toString(runtimeCode),
'","storage":{"0x0":"0x',
'","storage":{"0x0":"',
vm.toString(ownerSlotValue),
'"}}}'
)
Expand Down
189 changes: 189 additions & 0 deletions contracts/script/GenerateFeeVaultAlloc.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Script, console} from "forge-std/Script.sol";
import {FeeVault} from "../src/FeeVault.sol";

abstract contract FeeVaultAllocBase is Script {
struct Config {
address feeVaultAddress;
address owner;
uint32 destinationDomain;
bytes32 recipientAddress;
uint256 minimumAmount;
uint256 callFee;
uint256 bridgeShareBpsRaw;
uint256 bridgeShareBps;
address otherRecipient;
address hypNativeMinter;
bytes32 salt;
address deployer;
}

function loadConfig() internal view returns (Config memory cfg) {
cfg.owner = vm.envAddress("OWNER");
cfg.destinationDomain = uint32(vm.envOr("DESTINATION_DOMAIN", uint256(0)));
cfg.recipientAddress = vm.envOr("RECIPIENT_ADDRESS", bytes32(0));
cfg.minimumAmount = vm.envOr("MINIMUM_AMOUNT", uint256(0));
cfg.callFee = vm.envOr("CALL_FEE", uint256(0));
cfg.bridgeShareBpsRaw = vm.envOr("BRIDGE_SHARE_BPS", uint256(0));
cfg.otherRecipient = vm.envOr("OTHER_RECIPIENT", address(0));
cfg.hypNativeMinter = vm.envOr("HYP_NATIVE_MINTER", address(0));
cfg.feeVaultAddress = vm.envOr("FEE_VAULT_ADDRESS", address(0));
cfg.deployer = vm.envOr("DEPLOYER", address(0));
cfg.salt = vm.envOr("SALT", bytes32(0));

require(cfg.owner != address(0), "OWNER required");
require(cfg.bridgeShareBpsRaw <= 10000, "BRIDGE_SHARE_BPS > 10000");

cfg.bridgeShareBps = cfg.bridgeShareBpsRaw == 0 ? 10000 : cfg.bridgeShareBpsRaw;

if (cfg.feeVaultAddress == address(0) && cfg.deployer != address(0)) {
bytes32 initCodeHash = keccak256(
abi.encodePacked(
type(FeeVault).creationCode,
abi.encode(
cfg.owner,
cfg.destinationDomain,
cfg.recipientAddress,
cfg.minimumAmount,
cfg.callFee,
cfg.bridgeShareBpsRaw,
cfg.otherRecipient
)
)
);
cfg.feeVaultAddress = address(
uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), cfg.deployer, cfg.salt, initCodeHash))))
);
}

require(cfg.feeVaultAddress != address(0), "FEE_VAULT_ADDRESS or DEPLOYER required");
}

function computeSlots(Config memory cfg)
internal
pure
returns (
bytes32 slot0,
bytes32 slot1,
bytes32 slot2,
bytes32 slot3,
bytes32 slot4,
bytes32 slot5,
bytes32 slot6
)
{
slot0 = bytes32(uint256(uint160(cfg.hypNativeMinter)));
slot1 = bytes32((uint256(cfg.destinationDomain) << 160) | uint256(uint160(cfg.owner)));
slot2 = cfg.recipientAddress;
slot3 = bytes32(cfg.minimumAmount);
slot4 = bytes32(cfg.callFee);
slot5 = bytes32(uint256(uint160(cfg.otherRecipient)));
slot6 = bytes32(cfg.bridgeShareBps);
}

function addressKey(address addr) internal pure returns (string memory) {
bytes memory full = bytes(vm.toString(addr));
bytes memory key = new bytes(40);
// Fixed-length copy for address key without 0x prefix.
for (uint256 i = 0; i < 40; i++) {
key[i] = full[i + 2];
}
return string(key);
}
}

/// @title GenerateFeeVaultAlloc
/// @notice Generates genesis alloc JSON for deploying FeeVault at a deterministic address
/// @dev Run with: OWNER=0x... forge script script/GenerateFeeVaultAlloc.s.sol -vvv
contract GenerateFeeVaultAlloc is FeeVaultAllocBase {
function run() external view {
Config memory cfg = loadConfig();
bytes memory runtimeCode = type(FeeVault).runtimeCode;

(bytes32 slot0, bytes32 slot1, bytes32 slot2, bytes32 slot3, bytes32 slot4, bytes32 slot5, bytes32 slot6) =
computeSlots(cfg);

console.log("========== FeeVault Genesis Alloc ==========");
console.log("FeeVault address:", cfg.feeVaultAddress);
console.log("Owner:", cfg.owner);
console.log("Destination domain:", cfg.destinationDomain);
console.log("Bridge share bps (raw):", cfg.bridgeShareBpsRaw);
console.log("Bridge share bps (effective):", cfg.bridgeShareBps);
console.log("");

if (cfg.bridgeShareBpsRaw == 0) {
console.log("NOTE: BRIDGE_SHARE_BPS=0 defaults to 10000 (constructor behavior).");
}
if (cfg.bridgeShareBps < 10000 && cfg.otherRecipient == address(0)) {
console.log("WARNING: OTHER_RECIPIENT is zero but bridge share < 10000.");
}
if (cfg.hypNativeMinter == address(0)) {
console.log("NOTE: HYP_NATIVE_MINTER is zero; set it before calling sendToCelestia().");
}
console.log("");

console.log("Add this to your genesis.json 'alloc' section:");
console.log("");
console.log("{");
console.log(' "alloc": {');
console.log(' "%s": {', addressKey(cfg.feeVaultAddress));
console.log(' "balance": "0x0",');
console.log(' "code": "%s",', vm.toString(runtimeCode));
console.log(' "storage": {');
console.log(' "0x0": "%s",', vm.toString(slot0));
console.log(' "0x1": "%s",', vm.toString(slot1));
console.log(' "0x2": "%s",', vm.toString(slot2));
console.log(' "0x3": "%s",', vm.toString(slot3));
console.log(' "0x4": "%s",', vm.toString(slot4));
console.log(' "0x5": "%s",', vm.toString(slot5));
console.log(' "0x6": "%s"', vm.toString(slot6));
console.log(" }");
console.log(" }");
console.log(" }");
console.log("}");
console.log("");
console.log("Raw bytecode length:", runtimeCode.length);
console.log("=============================================");
}
}

/// @title GenerateFeeVaultAllocJSON
/// @notice Outputs just the JSON snippet for easy copy-paste
/// @dev Run with: OWNER=0x... forge script script/GenerateFeeVaultAlloc.s.sol:GenerateFeeVaultAllocJSON -vvv
contract GenerateFeeVaultAllocJSON is FeeVaultAllocBase {
function run() external view {
Config memory cfg = loadConfig();
bytes memory runtimeCode = type(FeeVault).runtimeCode;

(bytes32 slot0, bytes32 slot1, bytes32 slot2, bytes32 slot3, bytes32 slot4, bytes32 slot5, bytes32 slot6) =
computeSlots(cfg);

string memory json = string(
abi.encodePacked(
'{"',
addressKey(cfg.feeVaultAddress),
'":{"balance":"0x0","code":"',
vm.toString(runtimeCode),
'","storage":{"0x0":"',
vm.toString(slot0),
'","0x1":"',
vm.toString(slot1),
'","0x2":"',
vm.toString(slot2),
'","0x3":"',
vm.toString(slot3),
'","0x4":"',
vm.toString(slot4),
'","0x5":"',
vm.toString(slot5),
'","0x6":"',
vm.toString(slot6),
'"}}}'
)
);

console.log(json);
}
}
3 changes: 2 additions & 1 deletion crates/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ev-revm = { path = "../ev-revm" }
# Reth dependencies
reth-node-builder.workspace = true
reth-chainspec.workspace = true
reth-cli.workspace = true
reth-ethereum = { workspace = true, features = ["node", "cli", "pool"] }
reth-ethereum-forks.workspace = true
reth-ethereum-payload-builder.workspace = true
Expand Down Expand Up @@ -52,6 +53,7 @@ alloy-primitives.workspace = true
alloy-eips.workspace = true
alloy-consensus.workspace = true
alloy-evm.workspace = true
alloy-genesis.workspace = true

# Core dependencies
eyre.workspace = true
Expand All @@ -73,7 +75,6 @@ reth-transaction-pool.workspace = true
reth-consensus.workspace = true
reth-tasks.workspace = true
reth-tracing.workspace = true
alloy-genesis.workspace = true
tempfile.workspace = true
hex = "0.4"

Expand Down
Loading