1use clap::Parser;
2use database::BenchmarkDB;
3use inspector::{inspectors::TracerEip3155, InspectEvm};
4use revm::{
5 bytecode::{Bytecode, BytecodeDecodeError},
6 primitives::{address, hex, Address, TxKind},
7 Context, Database, ExecuteEvm, MainBuilder, MainContext,
8};
9use std::io::Error as IoError;
10use std::path::PathBuf;
11use std::time::Duration;
12use std::{borrow::Cow, fs};
13
14#[derive(Debug, thiserror::Error)]
15pub enum Errors {
16 #[error("The specified path does not exist")]
17 PathNotExists,
18 #[error("Invalid bytecode")]
19 InvalidBytecode,
20 #[error("Invalid input")]
21 InvalidInput,
22 #[error("EVM Error")]
23 EVMError,
24 #[error(transparent)]
25 Io(#[from] IoError),
26 #[error(transparent)]
27 BytecodeDecodeError(#[from] BytecodeDecodeError),
28}
29
30#[derive(Parser, Debug)]
34pub struct Cmd {
35 #[arg(required_unless_present = "path")]
37 bytecode: Option<String>,
38 #[arg(long)]
42 path: Option<PathBuf>,
43 #[arg(long)]
45 bench: bool,
46 #[arg(long, default_value = "")]
48 input: String,
49 #[arg(long)]
51 state: bool,
52 #[arg(long)]
54 trace: bool,
55}
56
57impl Cmd {
58 pub fn run(&self) -> Result<(), Errors> {
60 const CALLER: Address = address!("0000000000000000000000000000000000000001");
61
62 let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
63 if !path.exists() {
65 return Err(Errors::PathNotExists);
66 }
67 fs::read_to_string(path)?.into()
68 } else if let Some(bytecode) = &self.bytecode {
69 bytecode.as_str().into()
70 } else {
71 unreachable!()
72 };
73
74 let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?;
75 let input = hex::decode(self.input.trim())
76 .map_err(|_| Errors::InvalidInput)?
77 .into();
78
79 let mut db = BenchmarkDB::new_bytecode(Bytecode::new_raw_checked(bytecode.into())?);
80
81 let nonce = db.basic(CALLER).unwrap().map_or(0, |account| account.nonce);
82
83 let mut evm = Context::mainnet()
86 .with_db(db)
87 .modify_tx_chained(|tx| {
88 tx.caller = CALLER;
89 tx.kind = TxKind::Call(Address::ZERO);
90 tx.data = input;
91 tx.nonce = nonce;
92 })
93 .build_mainnet_with_inspector(TracerEip3155::new(Box::new(std::io::stdout())));
94
95 if self.bench {
96 let bench_options = microbench::Options::default().time(Duration::from_secs(3));
98
99 microbench::bench(&bench_options, "Run bytecode", || {
100 let _ = evm.transact_previous().unwrap();
101 });
102
103 return Ok(());
104 }
105
106 let out = if self.trace {
107 evm.inspect_previous().map_err(|_| Errors::EVMError)?
108 } else {
109 let out = evm.transact_previous().map_err(|_| Errors::EVMError)?;
110 println!("Result: {:#?}", out.result);
111 out
112 };
113
114 if self.state {
115 println!("State: {:#?}", out.state);
116 }
117
118 Ok(())
119 }
120}