Skip to main content

mega_evme/run/
cmd.rs

1use std::time::Instant;
2
3use clap::Parser;
4use mega_evm::revm::{context::result::ExecutionResult, state::Bytecode, DatabaseRef};
5use tracing::{debug, info, trace, warn};
6
7use super::{load_hex, Result, RunError};
8use crate::common::{
9    print_execution_summary, print_execution_trace, EvmeOutcome, ExecutionSummary,
10};
11
12// Re-export TracerType from common module
13pub use crate::common::TracerType;
14
15/// Run arbitrary EVM bytecode
16#[derive(Parser, Debug)]
17pub struct Cmd {
18    /// EVM bytecode as hex string (positional argument)
19    #[arg(value_name = "CODE")]
20    pub code: Option<String>,
21
22    /// File containing EVM code. If '-' is specified, code is read from stdin
23    #[arg(long = "codefile")]
24    pub codefile: Option<String>,
25
26    // Shared argument groups
27    /// Transaction configuration
28    #[command(flatten)]
29    pub tx_args: super::TxArgs,
30
31    /// Pre-execution state configuration
32    #[command(flatten)]
33    pub prestate_args: super::PreStateArgs,
34
35    /// RPC configuration (used when --fork is enabled)
36    #[command(flatten)]
37    pub rpc_args: super::RpcArgs,
38
39    /// Environment configuration
40    #[command(flatten)]
41    pub env_args: super::EnvArgs,
42
43    /// State dump configuration
44    #[command(flatten)]
45    pub dump_args: super::StateDumpArgs,
46
47    /// Trace configuration
48    #[command(flatten)]
49    pub trace_args: super::TraceArgs,
50
51    /// Output format configuration
52    #[command(flatten)]
53    pub output_args: super::OutputArgs,
54}
55
56impl Cmd {
57    /// Execute the run command
58    pub async fn run(&self) -> Result<()> {
59        // Step 1: Load bytecode
60        info!("Loading bytecode");
61        let code = load_hex(self.code.clone(), self.codefile.clone())?.ok_or_else(|| {
62            RunError::InvalidInput(
63                "No code provided. Use --codefile or provide code as argument".to_string(),
64            )
65        })?;
66        debug!(code_len = code.len(), "Bytecode loaded");
67
68        // Step 2: Setup initial state and environment
69        info!("Setting up initial state");
70        let sender = self.tx_args.sender();
71        let (mut state, cache_store) =
72            self.prestate_args.create_initial_state(&sender, &self.rpc_args).await?;
73        debug!(sender = %sender, "State initialized");
74
75        // Deploy system contracts based on spec
76        let spec = self.env_args.spec_id()?;
77        state.deploy_system_contracts(spec);
78        debug!(spec = ?spec, "System contracts deployed");
79
80        let pre_execution_nonce = state.basic_ref(sender)?.map(|acc| acc.nonce).unwrap_or(0);
81        debug!(nonce = pre_execution_nonce, "Pre-execution nonce");
82
83        // Run-specific: If not in create mode, set the code at the receiver address
84        if !self.tx_args.create() && !code.is_empty() {
85            let bytecode = Bytecode::new_raw_checked(code.clone())
86                .unwrap_or_else(|_| Bytecode::new_legacy(code.clone()));
87            debug!(receiver = %self.tx_args.receiver(), "Setting code at receiver address");
88            state.set_account_code(self.tx_args.receiver(), bytecode);
89        }
90
91        // Step 3: Execute bytecode
92        info!("Executing transaction");
93        let mut tx = self.tx_args.create_tx(self.env_args.chain.chain_id)?;
94        debug!(
95            tx_type = tx.base.tx_type,
96            gas_limit = tx.base.gas_limit,
97            value = %tx.base.value,
98            "Transaction created"
99        );
100
101        // In create mode, prepend code to input data
102        if self.tx_args.create() {
103            debug!("Create mode: prepending code to input data");
104            tx.base.data = [code.as_ref(), tx.base.data.as_ref()].concat().into();
105        }
106
107        // Create EVM context and execute transaction
108        let evm_context = self.env_args.create_evm_context(&mut state)?;
109        let start = Instant::now();
110        let (exec_result, evm_state, trace_data) =
111            self.trace_args.execute_transaction(evm_context, tx)?;
112        let exec_time = start.elapsed();
113
114        // Log execution result
115        match &exec_result {
116            ExecutionResult::Success { gas_used, .. } => {
117                info!(gas_used, "Execution succeeded");
118            }
119            ExecutionResult::Revert { gas_used, .. } => {
120                warn!(gas_used, "Execution reverted");
121            }
122            ExecutionResult::Halt { reason, gas_used } => {
123                warn!(?reason, gas_used, "Execution halted");
124            }
125        }
126
127        let outcome = EvmeOutcome {
128            pre_execution_nonce,
129            exec_result,
130            state: evm_state,
131            exec_time,
132            trace_data,
133        };
134
135        // Step 4: Output results (including state dump if requested)
136        trace!("Writing output results");
137        self.output_results(&outcome)?;
138
139        // Step 5: Persist the RPC cache (clean-exit only).
140        cache_store.persist()?;
141
142        Ok(())
143    }
144
145    /// Output execution results
146    fn output_results(&self, outcome: &EvmeOutcome) -> Result<()> {
147        // Determine contract address for CREATE transactions
148        let contract_address = (self.tx_args.create() && outcome.exec_result.is_success())
149            .then(|| self.tx_args.sender().create(outcome.pre_execution_nonce));
150
151        if self.output_args.json {
152            let mut summary = ExecutionSummary::from_result(&outcome.exec_result, contract_address);
153            summary.fill_trace_and_dump(outcome, &self.trace_args, &self.dump_args)?;
154            println!(
155                "{}",
156                serde_json::to_string_pretty(&summary).expect("failed to serialize output")
157            );
158        } else {
159            // Human-readable summary
160            print_execution_summary(&outcome.exec_result, contract_address, outcome.exec_time);
161
162            print_execution_trace(
163                outcome.trace_data.as_deref(),
164                self.trace_args.trace_output_file.as_deref(),
165            )?;
166
167            if self.dump_args.dump {
168                self.dump_args.dump_evm_state(&outcome.state)?;
169            }
170        }
171
172        Ok(())
173    }
174}