use std::time::Instant;
use clap::Parser;
use mega_evm::revm::{context::result::ExecutionResult, state::Bytecode, DatabaseRef};
use tracing::{debug, info, trace, warn};
use super::{load_hex, Result, RunError};
use crate::common::{
print_execution_summary, print_execution_trace, EvmeOutcome, ExecutionSummary,
};
pub use crate::common::TracerType;
#[derive(Parser, Debug)]
pub struct Cmd {
#[arg(value_name = "CODE")]
pub code: Option<String>,
#[arg(long = "codefile")]
pub codefile: Option<String>,
#[command(flatten)]
pub tx_args: super::TxArgs,
#[command(flatten)]
pub prestate_args: super::PreStateArgs,
#[command(flatten)]
pub rpc_args: super::RpcArgs,
#[command(flatten)]
pub env_args: super::EnvArgs,
#[command(flatten)]
pub dump_args: super::StateDumpArgs,
#[command(flatten)]
pub trace_args: super::TraceArgs,
#[command(flatten)]
pub output_args: super::OutputArgs,
}
impl Cmd {
pub async fn run(&self) -> Result<()> {
info!("Loading bytecode");
let code = load_hex(self.code.clone(), self.codefile.clone())?.ok_or_else(|| {
RunError::InvalidInput(
"No code provided. Use --codefile or provide code as argument".to_string(),
)
})?;
debug!(code_len = code.len(), "Bytecode loaded");
info!("Setting up initial state");
let sender = self.tx_args.sender();
let (mut state, cache_store) =
self.prestate_args.create_initial_state(&sender, &self.rpc_args).await?;
debug!(sender = %sender, "State initialized");
let spec = self.env_args.spec_id()?;
state.deploy_system_contracts(spec);
debug!(spec = ?spec, "System contracts deployed");
let pre_execution_nonce = state.basic_ref(sender)?.map(|acc| acc.nonce).unwrap_or(0);
debug!(nonce = pre_execution_nonce, "Pre-execution nonce");
if !self.tx_args.create() && !code.is_empty() {
let bytecode = Bytecode::new_raw_checked(code.clone())
.unwrap_or_else(|_| Bytecode::new_legacy(code.clone()));
debug!(receiver = %self.tx_args.receiver(), "Setting code at receiver address");
state.set_account_code(self.tx_args.receiver(), bytecode);
}
info!("Executing transaction");
let mut tx = self.tx_args.create_tx(self.env_args.chain.chain_id)?;
debug!(
tx_type = tx.base.tx_type,
gas_limit = tx.base.gas_limit,
value = %tx.base.value,
"Transaction created"
);
if self.tx_args.create() {
debug!("Create mode: prepending code to input data");
tx.base.data = [code.as_ref(), tx.base.data.as_ref()].concat().into();
}
let evm_context = self.env_args.create_evm_context(&mut state)?;
let start = Instant::now();
let (exec_result, evm_state, trace_data) =
self.trace_args.execute_transaction(evm_context, tx)?;
let exec_time = start.elapsed();
match &exec_result {
ExecutionResult::Success { gas_used, .. } => {
info!(gas_used, "Execution succeeded");
}
ExecutionResult::Revert { gas_used, .. } => {
warn!(gas_used, "Execution reverted");
}
ExecutionResult::Halt { reason, gas_used } => {
warn!(?reason, gas_used, "Execution halted");
}
}
let outcome = EvmeOutcome {
pre_execution_nonce,
exec_result,
state: evm_state,
exec_time,
trace_data,
};
trace!("Writing output results");
self.output_results(&outcome)?;
cache_store.persist()?;
Ok(())
}
fn output_results(&self, outcome: &EvmeOutcome) -> Result<()> {
let contract_address = (self.tx_args.create() && outcome.exec_result.is_success())
.then(|| self.tx_args.sender().create(outcome.pre_execution_nonce));
if self.output_args.json {
let mut summary = ExecutionSummary::from_result(&outcome.exec_result, contract_address);
summary.fill_trace_and_dump(outcome, &self.trace_args, &self.dump_args)?;
println!(
"{}",
serde_json::to_string_pretty(&summary).expect("failed to serialize output")
);
} else {
print_execution_summary(&outcome.exec_result, contract_address, outcome.exec_time);
print_execution_trace(
outcome.trace_data.as_deref(),
self.trace_args.trace_output_file.as_deref(),
)?;
if self.dump_args.dump {
self.dump_args.dump_evm_state(&outcome.state)?;
}
}
Ok(())
}
}