1use std::str::FromStr;
4
5use alloy_primitives::{Address, B256, U256};
6use clap::{Args, Parser};
7use std::convert::Infallible;
8
9use mega_evm::{
10 alloy_evm::Database,
11 revm::{
12 context::{block::BlockEnv, cfg::CfgEnv},
13 primitives::eip4844,
14 },
15 AHashBucketHasher, MegaContext, MegaSpecId, TestExternalEnvs,
16};
17
18pub type EvmeExternalEnvs = TestExternalEnvs<Infallible, AHashBucketHasher>;
20use tracing::{debug, trace};
21
22use super::{EvmeError, Result};
23
24#[derive(Args, Debug, Clone)]
26#[command(next_help_heading = "Chain Options")]
27pub struct ChainArgs {
28 #[arg(long = "spec", default_value = "Rex6")]
31 pub spec: String,
32
33 #[arg(long = "chain-id", visible_aliases = ["chainid"], default_value = "6342")]
35 pub chain_id: u64,
36}
37
38impl ChainArgs {
39 pub fn spec_id(&self) -> Result<MegaSpecId> {
41 MegaSpecId::from_str(&self.spec)
42 .map_err(|e| EvmeError::InvalidInput(format!("Invalid spec name: {:?}", e)))
43 }
44
45 pub fn create_cfg_env(&self) -> Result<CfgEnv<MegaSpecId>> {
47 let mut cfg = CfgEnv::default();
48 cfg.chain_id = self.chain_id;
49 cfg.spec = self.spec_id()?;
50 debug!(cfg = ?cfg, "Evm CfgEnv created");
51 Ok(cfg)
52 }
53}
54
55#[derive(Args, Debug, Clone)]
57#[command(next_help_heading = "Block Options")]
58pub struct BlockEnvArgs {
59 #[arg(long = "block.number", default_value = "1")]
61 pub block_number: u64,
62
63 #[arg(long = "block.coinbase", visible_aliases = ["block.beneficiary"], default_value = "0x0000000000000000000000000000000000000000")]
65 pub block_coinbase: Address,
66
67 #[arg(long = "block.timestamp", default_value = "1")]
69 pub block_timestamp: u64,
70
71 #[arg(long = "block.gaslimit", visible_aliases = ["block.gas-limit", "block.gas"], default_value = "10000000000")]
73 pub block_gas_limit: u64,
74
75 #[arg(long = "block.basefee", visible_aliases = ["block.base-fee"], default_value = "0")]
77 pub block_basefee: u64,
78
79 #[arg(long = "block.difficulty", default_value = "0")]
81 pub block_difficulty: U256,
82
83 #[arg(
85 long = "block.prevrandao",
86 visible_aliases = ["block.random"],
87 default_value = "0x0000000000000000000000000000000000000000000000000000000000000000"
88 )]
89 pub block_prevrandao: B256,
90
91 #[arg(long = "block.blobexcessgas", visible_aliases = ["block.blob-excess-gas"], default_value = "0")]
93 pub block_blob_excess_gas: Option<u64>,
94}
95
96impl BlockEnvArgs {
97 pub fn create_block_env(&self) -> Result<BlockEnv> {
99 let mut block = BlockEnv {
100 number: U256::from(self.block_number),
101 beneficiary: self.block_coinbase,
102 timestamp: U256::from(self.block_timestamp),
103 gas_limit: self.block_gas_limit,
104 basefee: self.block_basefee,
105 difficulty: self.block_difficulty,
106 prevrandao: Some(self.block_prevrandao),
107 blob_excess_gas_and_price: None,
108 };
109
110 if let Some(excess_gas) = self.block_blob_excess_gas {
112 block.set_blob_excess_gas_and_price(
113 excess_gas,
114 eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN,
115 );
116 }
117 debug!(block = ?block, "Evm BlockEnv created");
118
119 Ok(block)
120 }
121}
122
123#[derive(Args, Debug, Clone)]
125#[command(next_help_heading = "External Environment Options")]
126pub struct ExtEnvArgs {
127 #[arg(long = "bucket-capacity", value_name = "BUCKET_ID:CAPACITY")]
131 pub bucket_capacity: Vec<String>,
132}
133
134impl ExtEnvArgs {
135 pub fn create_external_envs(&self) -> Result<EvmeExternalEnvs> {
137 let mut external_envs = EvmeExternalEnvs::new();
138
139 for bucket_capacity_str in &self.bucket_capacity {
141 let (bucket_id, capacity) = parse_bucket_capacity(bucket_capacity_str)?;
142 external_envs = external_envs.with_bucket_capacity(bucket_id, capacity);
143 }
144 debug!(external_envs = ?external_envs, "Evm EvmeExternalEnvs created");
145
146 Ok(external_envs)
147 }
148}
149
150#[derive(Parser, Debug, Clone)]
152pub struct EnvArgs {
153 #[command(flatten)]
155 pub chain: ChainArgs,
156
157 #[command(flatten)]
159 pub block: BlockEnvArgs,
160
161 #[command(flatten)]
163 pub ext: ExtEnvArgs,
164}
165
166impl EnvArgs {
167 pub fn spec_id(&self) -> Result<MegaSpecId> {
169 self.chain.spec_id()
170 }
171
172 pub fn create_cfg_env(&self) -> Result<CfgEnv<MegaSpecId>> {
174 self.chain.create_cfg_env()
175 }
176
177 pub fn create_block_env(&self) -> Result<BlockEnv> {
179 self.block.create_block_env()
180 }
181
182 pub fn create_external_envs(&self) -> Result<EvmeExternalEnvs> {
184 self.ext.create_external_envs()
185 }
186
187 pub fn create_evm_context<DB: Database>(
194 &self,
195 db: DB,
196 ) -> Result<MegaContext<DB, EvmeExternalEnvs>> {
197 let cfg = self.create_cfg_env()?;
198 let block = self.create_block_env()?;
199 let external_envs = self.create_external_envs()?;
200
201 Ok(MegaContext::new(db, cfg.spec)
202 .with_cfg(cfg)
203 .with_block(block)
204 .with_external_envs(external_envs.into()))
205 }
206}
207
208pub fn parse_bucket_capacity(s: &str) -> Result<(u32, u64)> {
211 let parts: Vec<&str> = s.split(':').collect();
212 if parts.len() != 2 {
213 return Err(EvmeError::InvalidInput(format!(
214 "Invalid bucket capacity format: '{}'. Expected format: 'bucket_id:capacity'",
215 s
216 )));
217 }
218
219 let bucket_id = parts[0]
220 .parse::<u32>()
221 .map_err(|e| EvmeError::InvalidInput(format!("Invalid bucket ID '{}': {}", parts[0], e)))?;
222
223 let capacity = parts[1]
224 .parse::<u64>()
225 .map_err(|e| EvmeError::InvalidInput(format!("Invalid capacity '{}': {}", parts[1], e)))?;
226
227 trace!(string = %s, bucket_id = %bucket_id, capacity = %capacity, "Parsed bucket capacity");
228 Ok((bucket_id, capacity))
229}