use std::str::FromStr;
use alloy_primitives::{Address, B256, U256};
use clap::{Args, Parser};
use std::convert::Infallible;
use mega_evm::{
alloy_evm::Database,
revm::{
context::{block::BlockEnv, cfg::CfgEnv},
primitives::eip4844,
},
AHashBucketHasher, MegaContext, MegaSpecId, TestExternalEnvs,
};
pub type EvmeExternalEnvs = TestExternalEnvs<Infallible, AHashBucketHasher>;
use tracing::{debug, trace};
use super::{EvmeError, Result};
#[derive(Args, Debug, Clone)]
#[command(next_help_heading = "Chain Options")]
pub struct ChainArgs {
#[arg(long = "spec", default_value = "Rex6")]
pub spec: String,
#[arg(long = "chain-id", visible_aliases = ["chainid"], default_value = "6342")]
pub chain_id: u64,
}
impl ChainArgs {
pub fn spec_id(&self) -> Result<MegaSpecId> {
MegaSpecId::from_str(&self.spec)
.map_err(|e| EvmeError::InvalidInput(format!("Invalid spec name: {:?}", e)))
}
pub fn create_cfg_env(&self) -> Result<CfgEnv<MegaSpecId>> {
let mut cfg = CfgEnv::default();
cfg.chain_id = self.chain_id;
cfg.spec = self.spec_id()?;
debug!(cfg = ?cfg, "Evm CfgEnv created");
Ok(cfg)
}
}
#[derive(Args, Debug, Clone)]
#[command(next_help_heading = "Block Options")]
pub struct BlockEnvArgs {
#[arg(long = "block.number", default_value = "1")]
pub block_number: u64,
#[arg(long = "block.coinbase", visible_aliases = ["block.beneficiary"], default_value = "0x0000000000000000000000000000000000000000")]
pub block_coinbase: Address,
#[arg(long = "block.timestamp", default_value = "1")]
pub block_timestamp: u64,
#[arg(long = "block.gaslimit", visible_aliases = ["block.gas-limit", "block.gas"], default_value = "10000000000")]
pub block_gas_limit: u64,
#[arg(long = "block.basefee", visible_aliases = ["block.base-fee"], default_value = "0")]
pub block_basefee: u64,
#[arg(long = "block.difficulty", default_value = "0")]
pub block_difficulty: U256,
#[arg(
long = "block.prevrandao",
visible_aliases = ["block.random"],
default_value = "0x0000000000000000000000000000000000000000000000000000000000000000"
)]
pub block_prevrandao: B256,
#[arg(long = "block.blobexcessgas", visible_aliases = ["block.blob-excess-gas"], default_value = "0")]
pub block_blob_excess_gas: Option<u64>,
}
impl BlockEnvArgs {
pub fn create_block_env(&self) -> Result<BlockEnv> {
let mut block = BlockEnv {
number: U256::from(self.block_number),
beneficiary: self.block_coinbase,
timestamp: U256::from(self.block_timestamp),
gas_limit: self.block_gas_limit,
basefee: self.block_basefee,
difficulty: self.block_difficulty,
prevrandao: Some(self.block_prevrandao),
blob_excess_gas_and_price: None,
};
if let Some(excess_gas) = self.block_blob_excess_gas {
block.set_blob_excess_gas_and_price(
excess_gas,
eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN,
);
}
debug!(block = ?block, "Evm BlockEnv created");
Ok(block)
}
}
#[derive(Args, Debug, Clone)]
#[command(next_help_heading = "External Environment Options")]
pub struct ExtEnvArgs {
#[arg(long = "bucket-capacity", value_name = "BUCKET_ID:CAPACITY")]
pub bucket_capacity: Vec<String>,
}
impl ExtEnvArgs {
pub fn create_external_envs(&self) -> Result<EvmeExternalEnvs> {
let mut external_envs = EvmeExternalEnvs::new();
for bucket_capacity_str in &self.bucket_capacity {
let (bucket_id, capacity) = parse_bucket_capacity(bucket_capacity_str)?;
external_envs = external_envs.with_bucket_capacity(bucket_id, capacity);
}
debug!(external_envs = ?external_envs, "Evm EvmeExternalEnvs created");
Ok(external_envs)
}
}
#[derive(Parser, Debug, Clone)]
pub struct EnvArgs {
#[command(flatten)]
pub chain: ChainArgs,
#[command(flatten)]
pub block: BlockEnvArgs,
#[command(flatten)]
pub ext: ExtEnvArgs,
}
impl EnvArgs {
pub fn spec_id(&self) -> Result<MegaSpecId> {
self.chain.spec_id()
}
pub fn create_cfg_env(&self) -> Result<CfgEnv<MegaSpecId>> {
self.chain.create_cfg_env()
}
pub fn create_block_env(&self) -> Result<BlockEnv> {
self.block.create_block_env()
}
pub fn create_external_envs(&self) -> Result<EvmeExternalEnvs> {
self.ext.create_external_envs()
}
pub fn create_evm_context<DB: Database>(
&self,
db: DB,
) -> Result<MegaContext<DB, EvmeExternalEnvs>> {
let cfg = self.create_cfg_env()?;
let block = self.create_block_env()?;
let external_envs = self.create_external_envs()?;
Ok(MegaContext::new(db, cfg.spec)
.with_cfg(cfg)
.with_block(block)
.with_external_envs(external_envs.into()))
}
}
pub fn parse_bucket_capacity(s: &str) -> Result<(u32, u64)> {
let parts: Vec<&str> = s.split(':').collect();
if parts.len() != 2 {
return Err(EvmeError::InvalidInput(format!(
"Invalid bucket capacity format: '{}'. Expected format: 'bucket_id:capacity'",
s
)));
}
let bucket_id = parts[0]
.parse::<u32>()
.map_err(|e| EvmeError::InvalidInput(format!("Invalid bucket ID '{}': {}", parts[0], e)))?;
let capacity = parts[1]
.parse::<u64>()
.map_err(|e| EvmeError::InvalidInput(format!("Invalid capacity '{}': {}", parts[1], e)))?;
trace!(string = %s, bucket_id = %bucket_id, capacity = %capacity, "Parsed bucket capacity");
Ok((bucket_id, capacity))
}