1use std::time::Instant;
2
3use clap::Parser;
4use mega_evm::{
5 revm::{
6 context::result::ExecutionResult, context_interface::transaction::Transaction as _,
7 primitives::TxKind, DatabaseRef,
8 },
9 MegaTransaction, MegaTxType,
10};
11use tracing::{debug, info, trace, warn};
12
13use crate::common::{
14 load_hex, op_receipt_to_tx_receipt, print_execution_summary, print_execution_trace,
15 print_receipt, DecodedRawTx, EvmeError, EvmeOutcome, ExecutionSummary,
16};
17
18use super::Result;
19
20#[derive(Parser, Debug)]
22pub struct Cmd {
23 #[arg(value_name = "RAW_TX")]
26 pub raw: Option<String>,
27
28 #[command(flatten)]
31 pub tx_args: crate::run::TxArgs,
32
33 #[command(flatten)]
35 pub prestate_args: crate::run::PreStateArgs,
36
37 #[command(flatten)]
39 pub rpc_args: crate::run::RpcArgs,
40
41 #[command(flatten)]
43 pub env_args: crate::run::EnvArgs,
44
45 #[command(flatten)]
47 pub dump_args: crate::run::StateDumpArgs,
48
49 #[command(flatten)]
51 pub trace_args: crate::run::TraceArgs,
52
53 #[command(flatten)]
55 pub output_args: crate::run::OutputArgs,
56}
57
58impl Cmd {
59 pub async fn run(&self) -> Result<()> {
61 let chain_id = self.env_args.chain.chain_id;
62 let spec = self.env_args.spec_id()?;
63
64 info!("Creating transaction");
66 let tx = if let Some(ref raw) = self.raw {
67 let raw_bytes = load_hex(Some(raw.clone()), None)?.unwrap_or_default();
68 let decoded = DecodedRawTx::from_raw(raw_bytes)?.override_tx_env(&self.tx_args)?;
69 if decoded.tx_env.chain_id != Some(chain_id) {
70 warn!(
71 chain_id,
72 decoded_chain_id = decoded.tx_env.chain_id,
73 "Raw transaction chain_id does not match the configured chain_id"
74 );
75 }
76 decoded.into_tx()
77 } else {
78 self.tx_args.create_tx(chain_id)?
79 };
80
81 debug!(
82 tx_type = tx.base.tx_type,
83 gas_limit = tx.base.gas_limit,
84 value = %tx.base.value,
85 "Transaction created"
86 );
87
88 let sender = tx.base.caller;
90 info!("Setting up initial state");
91 let (mut state, cache_store) =
92 self.prestate_args.create_initial_state(&sender, &self.rpc_args).await?;
93 debug!(sender = %sender, "State initialized");
94
95 state.deploy_system_contracts(spec);
96 debug!(spec = ?spec, "System contracts deployed");
97
98 let pre_execution_nonce = state.basic_ref(sender)?.map(|acc| acc.nonce).unwrap_or(0);
99 debug!(nonce = pre_execution_nonce, "Pre-execution nonce");
100
101 info!("Executing transaction");
103 let evm_context = self.env_args.create_evm_context(&mut state)?;
104 let start = Instant::now();
105 let (exec_result, evm_state, trace_data) =
106 self.trace_args.execute_transaction(evm_context, tx.clone())?;
107 let exec_time = start.elapsed();
108
109 match &exec_result {
111 ExecutionResult::Success { gas_used, .. } => {
112 info!(gas_used, "Execution succeeded");
113 }
114 ExecutionResult::Revert { gas_used, .. } => {
115 warn!(gas_used, "Execution reverted");
116 }
117 ExecutionResult::Halt { reason, gas_used } => {
118 warn!(?reason, gas_used, "Execution halted");
119 }
120 }
121
122 let outcome = EvmeOutcome {
123 pre_execution_nonce,
124 exec_result,
125 state: evm_state,
126 exec_time,
127 trace_data,
128 };
129
130 trace!("Writing output results");
132 self.output_results(&outcome, &tx)?;
133
134 cache_store.persist()?;
136
137 Ok(())
138 }
139
140 fn output_results(&self, outcome: &EvmeOutcome, tx: &MegaTransaction) -> Result<()> {
142 let tx_type = MegaTxType::try_from(tx.base.tx_type)
143 .map_err(|_| EvmeError::UnsupportedTxType(tx.base.tx_type))?;
144 let sender = tx.base.caller;
145 let is_create = tx.base.kind == TxKind::Create;
146 let receiver = match tx.base.kind {
147 TxKind::Call(addr) => Some(addr),
148 TxKind::Create => None,
149 };
150 let effective_gas_price = tx.effective_gas_price(self.env_args.block.block_basefee as u128);
151
152 let op_receipt = outcome.to_op_receipt(tx_type, outcome.pre_execution_nonce);
154
155 let contract_address = (is_create && op_receipt.is_success())
157 .then(|| sender.create(outcome.pre_execution_nonce));
158
159 let receipt = op_receipt_to_tx_receipt(
160 &op_receipt,
161 self.env_args.block.block_number,
162 self.env_args.block.block_timestamp,
163 sender,
164 receiver,
165 contract_address,
166 effective_gas_price,
167 outcome.exec_result.gas_used(),
168 None,
169 None,
170 0,
171 );
172
173 if self.output_args.json {
174 let mut summary = ExecutionSummary::from_result(&outcome.exec_result, contract_address);
175 summary.fill_trace_and_dump(outcome, &self.trace_args, &self.dump_args)?;
176 summary.receipt =
177 Some(serde_json::to_value(&receipt).expect("failed to serialize receipt"));
178 println!(
179 "{}",
180 serde_json::to_string_pretty(&summary).expect("failed to serialize output")
181 );
182 } else {
183 print_execution_summary(&outcome.exec_result, contract_address, outcome.exec_time);
185
186 print_receipt(&receipt);
187
188 print_execution_trace(
189 outcome.trace_data.as_deref(),
190 self.trace_args.trace_output_file.as_deref(),
191 )?;
192
193 if self.dump_args.dump {
194 self.dump_args.dump_evm_state(&outcome.state)?;
195 }
196 }
197
198 Ok(())
199 }
200}