From ae45e30fbee2eb5f919517cccd0358f0bb5b5fd8 Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 00:29:54 -0400 Subject: [PATCH 01/37] initial start --- cli/src/cmd/cast/call.rs | 116 ++++++++++++++++++++++++++++++--------- evm/src/executor/opts.rs | 1 + 2 files changed, 90 insertions(+), 27 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 65fece91fc13d..6bc113ce00144 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -5,11 +5,17 @@ use crate::{ }; use cast::{Cast, TxBuilder}; use clap::Parser; +use ethers::solc::EvmVersion; use ethers::types::{BlockId, NameOrAddress, U256}; use eyre::WrapErr; -use foundry_config::Config; +use forge::executor::{opts::EvmOpts, Backend, ExecutorBuilder}; +use foundry_config::{find_project_root_path, Config}; +use foundry_evm::utils::evm_spec; use std::str::FromStr; +type Provider = + ethers::providers::Provider>; + /// CLI arguments for `cast call`. #[derive(Debug, Parser)] pub struct CallArgs { @@ -31,13 +37,18 @@ pub struct CallArgs { )] data: Option, + #[clap(long, default_value_t = false)] + trace: bool, + + #[clap(long, required_if_eq("trace", "true"))] + evm_version: Option, + /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short)] block: Option, - /// Simulate a contract deployment. #[clap(subcommand)] command: Option, @@ -50,6 +61,7 @@ pub struct CallArgs { #[derive(Debug, Parser)] pub enum CallSubcommands { + /// ignores the address field and simulates creating a contract #[clap(name = "--create")] Create { /// Bytecode of contract. @@ -73,14 +85,16 @@ pub enum CallSubcommands { impl CallArgs { pub async fn run(self) -> eyre::Result<()> { - let CallArgs { to, sig, args, data, tx, eth, command, block } = self; + let CallArgs { to, sig, args, data, tx, eth, command, block, trace, evm_version } = self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain_id, &provider).await?; let sender = eth.wallet.sender().await; - let mut builder = TxBuilder::new(&provider, sender, to, chain, tx.legacy).await?; + let mut builder: TxBuilder<'_, Provider> = + TxBuilder::new(&provider, sender, to, chain, tx.legacy).await?; + builder .gas(tx.gas_limit) .etherscan_api_key(config.get_etherscan_api_key(Some(chain))) @@ -89,38 +103,86 @@ impl CallArgs { .nonce(tx.nonce); match command { Some(CallSubcommands::Create { code, sig, args, value }) => { - builder.value(value); - - let mut data = hex::decode(code.strip_prefix("0x").unwrap_or(&code))?; - - if let Some(s) = sig { - let (mut sigdata, _func) = builder.create_args(&s, args).await?; - data.append(&mut sigdata); - } - - builder.set_data(data); + build_create(&mut builder, value, code, sig, args).await?; } _ => { - builder.value(tx.value); - - if let Some(sig) = sig { - builder.set_args(sig.as_str(), args).await?; - } - if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - builder.set_data( - hex::decode(data).wrap_err("Expected hex encoded function data")?, - ); - } + build_tx(&mut builder, tx.value, sig, args, data).await?; } }; - let builder_output = builder.build(); - println!("{}", Cast::new(provider).call(builder_output, block).await?); + if trace { + // todo:n find out what this is + let figment = + Config::figment_with_root(find_project_root_path(None).unwrap()).merge(eth.rpc); + + let mut evm_opts = figment.extract::()?; + + evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + + // Set up the execution environment + let mut env = evm_opts.evm_env().await; + + let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())).await; + + // configures a bare version of the evm executor: no cheatcode inspector is enabled, + // tracing will be enabled only for the targeted transaction + let builder = ExecutorBuilder::default() + .with_config(env) + .with_spec(evm_spec(&evm_version.unwrap_or(config.evm_version))); + + let mut executor = builder.build(db); + } else { + let builder_output = builder.build(); + println!("{}", Cast::new(provider).call(builder_output, block).await?); + } Ok(()) } } +async fn build_create( + builder: &mut TxBuilder<'_, Provider>, + value: Option, + code: String, + sig: Option, + args: Vec, +) -> eyre::Result<()> { + builder.value(value); + + let mut data = hex::decode(code.strip_prefix("0x").unwrap_or(&code))?; + + if let Some(s) = sig { + let (mut sigdata, _func) = builder.create_args(&s, args).await?; + data.append(&mut sigdata); + } + + builder.set_data(data); + + Ok(()) +} + +async fn build_tx( + builder: &mut TxBuilder<'_, Provider>, + value: Option, + sig: Option, + args: Vec, + data: Option, +) -> eyre::Result<()> { + builder.value(value); + + if let Some(sig) = sig { + builder.set_args(sig.as_str(), args).await?; + } + + if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + } + + Ok(()) +} + +fn typed_transaction_to_transaction() -> () {} + #[cfg(test)] mod tests { use super::*; diff --git a/evm/src/executor/opts.rs b/evm/src/executor/opts.rs index 99410d9403489..6ab1334682a60 100644 --- a/evm/src/executor/opts.rs +++ b/evm/src/executor/opts.rs @@ -24,6 +24,7 @@ pub struct EvmOpts { pub fork_url: Option, /// pins the block number for the state fork + /// if left unset, the latest block will be used pub fork_block_number: Option, /// initial retry backoff From 636ff052d05a713e58acbf2f94a1da444d7be991 Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 03:02:04 -0400 Subject: [PATCH 02/37] traces working --- cli/src/cmd/cast/call.rs | 226 +++++++++++++++++++++++++++++++++------ 1 file changed, 193 insertions(+), 33 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 6bc113ce00144..78398f6eb1b19 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -1,17 +1,26 @@ // cast estimate subcommands use crate::{ - opts::{EthereumOpts, TransactionOpts}, + opts::{EthereumOpts, RpcOpts, TransactionOpts}, utils::{self, parse_ether_value}, }; use cast::{Cast, TxBuilder}; use clap::Parser; -use ethers::solc::EvmVersion; -use ethers::types::{BlockId, NameOrAddress, U256}; +use ethers::{ + solc::EvmVersion, + types::{transaction::eip2718::TypedTransaction, Transaction}, + types::{BlockId, NameOrAddress, U256}, +}; use eyre::WrapErr; use forge::executor::{opts::EvmOpts, Backend, ExecutorBuilder}; use foundry_config::{find_project_root_path, Config}; -use foundry_evm::utils::evm_spec; -use std::str::FromStr; +use foundry_evm::{ + debug::DebugArena, + executor::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + trace::{CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces}, + utils::{evm_spec, h160_to_b160}, +}; +use std::{ops::DerefMut, str::FromStr}; +use yansi::Paint; type Provider = ethers::providers::Provider>; @@ -20,6 +29,8 @@ type Provider = #[derive(Debug, Parser)] pub struct CallArgs { /// The destination of the transaction. + /// + /// use --create tp ignore this field #[clap(value_parser = NameOrAddress::from_str)] to: Option, @@ -40,7 +51,11 @@ pub struct CallArgs { #[clap(long, default_value_t = false)] trace: bool, - #[clap(long, required_if_eq("trace", "true"))] + #[clap(long, requires = "trace")] + debug: bool, + + /// only for tracing mode + #[clap(long)] evm_version: Option, /// The block height to query at. @@ -83,9 +98,62 @@ pub enum CallSubcommands { }, } +struct TracingExecutor { + executor: Executor, +} + +impl TracingExecutor { + pub async fn new( + config: foundry_config::Config, + version: Option, + rpc_opts: RpcOpts, + debug: bool, + ) -> eyre::Result { + // todo:n find out what this is + let figment = + Config::figment_with_root(find_project_root_path(None).unwrap()).merge(rpc_opts); + + let mut evm_opts = figment.extract::()?; + + evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + + // Set up the execution environment + let env = evm_opts.evm_env().await; + + let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())).await; + + // configures a bare version of the evm executor: no cheatcode inspector is enabled, + // tracing will be enabled only for the targeted transaction + let builder = ExecutorBuilder::default() + .with_config(env) + .with_spec(evm_spec(&version.unwrap_or(config.evm_version))); + + let mut executor = builder.build(db); + + executor.set_tracing(true).set_debugger(debug); + + Ok(Self { executor }) + } +} + +impl std::ops::Deref for TracingExecutor { + type Target = Executor; + + fn deref(&self) -> &Self::Target { + &self.executor + } +} + +impl DerefMut for TracingExecutor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.executor + } +} + impl CallArgs { pub async fn run(self) -> eyre::Result<()> { - let CallArgs { to, sig, args, data, tx, eth, command, block, trace, evm_version } = self; + let CallArgs { to, sig, args, data, tx, eth, command, block, trace, evm_version, debug } = + self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; @@ -101,45 +169,101 @@ impl CallArgs { .gas_price(tx.gas_price) .priority_gas_price(tx.priority_gas_price) .nonce(tx.nonce); + match command { Some(CallSubcommands::Create { code, sig, args, value }) => { - build_create(&mut builder, value, code, sig, args).await?; + if trace { + let mut executor = + TracingExecutor::new(config, evm_version, eth.rpc, debug).await?; + + let mut trace = match executor.deploy( + sender, + code.into(), + value.unwrap_or(U256::zero()), + None, + ) { + Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => { + TraceResult { + success: true, + traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + debug: run_debug.unwrap_or_default(), + gas_used, + } + } + Err(EvmError::Execution(inner)) => { + let ExecutionErr { + reverted, gas_used, traces, debug: run_debug, .. + } = *inner; + TraceResult { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + debug: run_debug.unwrap_or_default(), + gas_used, + } + } + Err(err) => { + eyre::bail!( + "unexpected error when running create transaction: {:?}", + err + ) + } + }; + + let decoder = CallTraceDecoderBuilder::new().build(); + + print_traces(&mut trace, decoder, debug).await?; + + return Ok(()); + } + + // build it last because we dont need anything from the built one + build_create_tx(&mut builder, value, code, sig, args).await?; } _ => { + // build it first becasue builder parses args / addr build_tx(&mut builder, tx.value, sig, args, data).await?; + + if trace { + let mut executor = + TracingExecutor::new(config, evm_version, eth.rpc, debug).await?; + + let (tx, _) = builder.build(); + + let mut trace = match executor.call_raw_committing( + sender, + tx.to_addr().map(|a| a.clone()).expect("an address to be here"), + tx.data().map(|d| d.clone()).unwrap_or_default().to_vec().into(), + tx.value().map(|v| v.clone()).unwrap_or_default(), + ) { + Ok(RawCallResult { gas_used, traces, reverted, .. }) => TraceResult { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + debug: DebugArena::default(), + gas_used, + }, + Err(e) => { + eyre::bail!("unexpected error when running call transaction: {:?}", e) + } + }; + + let decoder = CallTraceDecoderBuilder::new().build(); + + print_traces(&mut trace, decoder, debug).await?; + + return Ok(()); + } } }; - if trace { - // todo:n find out what this is - let figment = - Config::figment_with_root(find_project_root_path(None).unwrap()).merge(eth.rpc); - - let mut evm_opts = figment.extract::()?; - - evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); - - // Set up the execution environment - let mut env = evm_opts.evm_env().await; + let builder_output = builder.build(); - let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())).await; + println!("{}", Cast::new(provider).call(builder_output, block).await?); - // configures a bare version of the evm executor: no cheatcode inspector is enabled, - // tracing will be enabled only for the targeted transaction - let builder = ExecutorBuilder::default() - .with_config(env) - .with_spec(evm_spec(&evm_version.unwrap_or(config.evm_version))); - - let mut executor = builder.build(db); - } else { - let builder_output = builder.build(); - println!("{}", Cast::new(provider).call(builder_output, block).await?); - } Ok(()) } } -async fn build_create( +async fn build_create_tx( builder: &mut TxBuilder<'_, Provider>, value: Option, code: String, @@ -181,7 +305,43 @@ async fn build_tx( Ok(()) } -fn typed_transaction_to_transaction() -> () {} +async fn print_traces( + result: &mut TraceResult, + decoder: CallTraceDecoder, + verbose: bool, +) -> eyre::Result<()> { + if result.traces.is_empty() { + eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + } + + println!("Traces:"); + for (_, trace) in &mut result.traces { + decoder.decode(trace).await; + if !verbose { + println!("{trace}"); + } else { + println!("{trace:#}"); + } + } + println!(); + + if result.success { + println!("{}", Paint::green("Transaction successfully executed.")); + } else { + println!("{}", Paint::red("Transaction failed.")); + } + + println!("Gas used: {}", result.gas_used); + Ok(()) +} + +/// taken from cast run, should find common place +struct TraceResult { + pub success: bool, + pub traces: Traces, + pub debug: DebugArena, + pub gas_used: u64, +} #[cfg(test)] mod tests { From 19c9c392092f08ef046b73f50e94306c4964d981 Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 03:12:50 -0400 Subject: [PATCH 03/37] Update opts.rs --- evm/src/executor/opts.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/src/executor/opts.rs b/evm/src/executor/opts.rs index 6ab1334682a60..99410d9403489 100644 --- a/evm/src/executor/opts.rs +++ b/evm/src/executor/opts.rs @@ -24,7 +24,6 @@ pub struct EvmOpts { pub fork_url: Option, /// pins the block number for the state fork - /// if left unset, the latest block will be used pub fork_block_number: Option, /// initial retry backoff From c1cfb829c9e0ea4919386e3faac5ded8a186c12c Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 03:47:04 -0400 Subject: [PATCH 04/37] clean up --- cli/src/cmd/cast/call.rs | 152 ++++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 24 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 78398f6eb1b19..e10167099309c 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -16,10 +16,14 @@ use foundry_config::{find_project_root_path, Config}; use foundry_evm::{ debug::DebugArena, executor::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, - trace::{CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces}, + trace::{ + identifier::{EtherscanIdentifier, SignaturesIdentifier}, + CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, + }, utils::{evm_spec, h160_to_b160}, }; -use std::{ops::DerefMut, str::FromStr}; +use std::{collections::BTreeMap, ops::DerefMut, str::FromStr}; +use ui::{TUIExitReason, Tui, Ui}; use yansi::Paint; type Provider = @@ -48,12 +52,28 @@ pub struct CallArgs { )] data: Option, + /// Forks the remote rpc, executes the transaction locally and prints a trace #[clap(long, default_value_t = false)] trace: bool, + /// Can only be used with "--trace" + /// + /// opens an interactive debugger #[clap(long, requires = "trace")] debug: bool, + /// Can only be used with "--trace" + /// + /// prints a more verbose trace + #[clap(long, requires = "trace")] + verbose: bool, + + /// Labels to apply to the traces. + /// + /// Format: `address:label` + #[clap(long, requires = "trace")] + labels: Vec, + /// only for tracing mode #[clap(long)] evm_version: Option, @@ -104,11 +124,11 @@ struct TracingExecutor { impl TracingExecutor { pub async fn new( - config: foundry_config::Config, + config: &foundry_config::Config, version: Option, rpc_opts: RpcOpts, debug: bool, - ) -> eyre::Result { + ) -> eyre::Result<(Self, EvmOpts)> { // todo:n find out what this is let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(rpc_opts); @@ -132,7 +152,7 @@ impl TracingExecutor { executor.set_tracing(true).set_debugger(debug); - Ok(Self { executor }) + Ok((Self { executor }, evm_opts)) } } @@ -152,8 +172,21 @@ impl DerefMut for TracingExecutor { impl CallArgs { pub async fn run(self) -> eyre::Result<()> { - let CallArgs { to, sig, args, data, tx, eth, command, block, trace, evm_version, debug } = - self; + let CallArgs { + to, + sig, + args, + data, + tx, + eth, + command, + block, + trace, + evm_version, + debug, + verbose, + labels, + } = self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; @@ -173,10 +206,10 @@ impl CallArgs { match command { Some(CallSubcommands::Create { code, sig, args, value }) => { if trace { - let mut executor = - TracingExecutor::new(config, evm_version, eth.rpc, debug).await?; + let (mut executor, opts) = + TracingExecutor::new(&config, evm_version, eth.rpc, debug).await?; - let mut trace = match executor.deploy( + let trace = match executor.deploy( sender, code.into(), value.unwrap_or(U256::zero()), @@ -209,9 +242,7 @@ impl CallArgs { } }; - let decoder = CallTraceDecoderBuilder::new().build(); - - print_traces(&mut trace, decoder, debug).await?; + handle_traces(trace, config, opts, labels, verbose, debug).await?; return Ok(()); } @@ -224,8 +255,8 @@ impl CallArgs { build_tx(&mut builder, tx.value, sig, args, data).await?; if trace { - let mut executor = - TracingExecutor::new(config, evm_version, eth.rpc, debug).await?; + let (mut executor, opts) = + TracingExecutor::new(&config, evm_version, eth.rpc, debug).await?; let (tx, _) = builder.build(); @@ -235,20 +266,20 @@ impl CallArgs { tx.data().map(|d| d.clone()).unwrap_or_default().to_vec().into(), tx.value().map(|v| v.clone()).unwrap_or_default(), ) { - Ok(RawCallResult { gas_used, traces, reverted, .. }) => TraceResult { - success: !reverted, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], - debug: DebugArena::default(), - gas_used, - }, + Ok(RawCallResult { gas_used, traces, reverted, debug, .. }) => { + TraceResult { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + debug: debug.unwrap_or_default(), + gas_used, + } + } Err(e) => { eyre::bail!("unexpected error when running call transaction: {:?}", e) } }; - let decoder = CallTraceDecoderBuilder::new().build(); - - print_traces(&mut trace, decoder, debug).await?; + handle_traces(trace, config, opts, labels, verbose, debug).await?; return Ok(()); } @@ -305,6 +336,51 @@ async fn build_tx( Ok(()) } +async fn handle_traces( + mut result: TraceResult, + config: Config, + evm_opts: EvmOpts, + labels: Vec, + verbose: bool, + debug: bool, +) -> eyre::Result<()> { + let mut etherscan_identifier = + EtherscanIdentifier::new(&config, evm_opts.get_remote_chain_id())?; + + let labeled_addresses = labels.iter().filter_map(|label_str| { + let mut iter = label_str.split(':'); + + if let Some(addr) = iter.next() { + if let (Ok(address), Some(label)) = + (ethers::types::Address::from_str(addr), iter.next()) + { + return Some((address, label.to_string())); + } + } + None + }); + + let mut decoder = CallTraceDecoderBuilder::new().with_labels(labeled_addresses).build(); + + decoder.add_signature_identifier(SignaturesIdentifier::new( + Config::foundry_cache_dir(), + config.offline, + )?); + + for (_, trace) in &mut result.traces { + decoder.identify(trace, &mut etherscan_identifier); + } + + if debug { + let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; + // run_debugger(result, decoder, bytecode, sources)?; + } else { + print_traces(&mut result, decoder, verbose).await?; + } + + Ok(()) +} + async fn print_traces( result: &mut TraceResult, decoder: CallTraceDecoder, @@ -335,6 +411,34 @@ async fn print_traces( Ok(()) } +// fn run_debugger( +// result: RunResult, +// decoder: CallTraceDecoder, +// known_contracts: BTreeMap, +// sources: BTreeMap, +// ) -> eyre::Result<()> { +// let calls: Vec = vec![result.debug]; +// let flattened = calls.last().expect("we should have collected debug info").flatten(0); +// let tui = Tui::new( +// flattened, +// 0, +// decoder.contracts, +// known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(), +// sources +// .into_iter() +// .map(|(id, source)| { +// let mut sources = BTreeMap::new(); +// sources.insert(0, source); +// (id.name, sources) +// }) +// .collect(), +// Default::default(), +// )?; +// match tui.start().expect("Failed to start tui") { +// TUIExitReason::CharExit => Ok(()), +// } +// } + /// taken from cast run, should find common place struct TraceResult { pub success: bool, From 3a376507906cb509266867671c89fc01aaa90c40 Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 14:44:09 -0400 Subject: [PATCH 05/37] comments/debugger --- cli/src/cmd/cast/call.rs | 71 +++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index e10167099309c..7db46007ffa3f 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -6,8 +6,7 @@ use crate::{ use cast::{Cast, TxBuilder}; use clap::Parser; use ethers::{ - solc::EvmVersion, - types::{transaction::eip2718::TypedTransaction, Transaction}, + solc::{artifacts::ContractBytecodeSome, ArtifactId, EvmVersion}, types::{BlockId, NameOrAddress, U256}, }; use eyre::WrapErr; @@ -20,7 +19,7 @@ use foundry_evm::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, - utils::{evm_spec, h160_to_b160}, + utils::evm_spec, }; use std::{collections::BTreeMap, ops::DerefMut, str::FromStr}; use ui::{TUIExitReason, Tui, Ui}; @@ -33,8 +32,6 @@ type Provider = #[derive(Debug, Parser)] pub struct CallArgs { /// The destination of the transaction. - /// - /// use --create tp ignore this field #[clap(value_parser = NameOrAddress::from_str)] to: Option, @@ -68,6 +65,7 @@ pub struct CallArgs { #[clap(long, requires = "trace")] verbose: bool, + /// Can only be used with "--trace" /// Labels to apply to the traces. /// /// Format: `address:label` @@ -118,6 +116,7 @@ pub enum CallSubcommands { }, } +/// a default executor with tracing enabled struct TracingExecutor { executor: Executor, } @@ -136,6 +135,7 @@ impl TracingExecutor { let mut evm_opts = figment.extract::()?; evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + evm_opts.fork_block_number = config.fork_block_number; // Set up the execution environment let env = evm_opts.evm_env().await; @@ -260,7 +260,7 @@ impl CallArgs { let (tx, _) = builder.build(); - let mut trace = match executor.call_raw_committing( + let trace = match executor.call_raw_committing( sender, tx.to_addr().map(|a| a.clone()).expect("an address to be here"), tx.data().map(|d| d.clone()).unwrap_or_default().to_vec().into(), @@ -294,6 +294,7 @@ impl CallArgs { } } +/// builds the transaction from create args async fn build_create_tx( builder: &mut TxBuilder<'_, Provider>, value: Option, @@ -315,6 +316,7 @@ async fn build_create_tx( Ok(()) } +/// builds the tx from args async fn build_tx( builder: &mut TxBuilder<'_, Provider>, value: Option, @@ -336,6 +338,7 @@ async fn build_tx( Ok(()) } +/// labels the traces, conditonally prints them or opens the debugger async fn handle_traces( mut result: TraceResult, config: Config, @@ -373,7 +376,7 @@ async fn handle_traces( if debug { let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; - // run_debugger(result, decoder, bytecode, sources)?; + run_debugger(result, decoder, bytecode, sources)?; } else { print_traces(&mut result, decoder, verbose).await?; } @@ -411,33 +414,33 @@ async fn print_traces( Ok(()) } -// fn run_debugger( -// result: RunResult, -// decoder: CallTraceDecoder, -// known_contracts: BTreeMap, -// sources: BTreeMap, -// ) -> eyre::Result<()> { -// let calls: Vec = vec![result.debug]; -// let flattened = calls.last().expect("we should have collected debug info").flatten(0); -// let tui = Tui::new( -// flattened, -// 0, -// decoder.contracts, -// known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(), -// sources -// .into_iter() -// .map(|(id, source)| { -// let mut sources = BTreeMap::new(); -// sources.insert(0, source); -// (id.name, sources) -// }) -// .collect(), -// Default::default(), -// )?; -// match tui.start().expect("Failed to start tui") { -// TUIExitReason::CharExit => Ok(()), -// } -// } +fn run_debugger( + result: TraceResult, + decoder: CallTraceDecoder, + known_contracts: BTreeMap, + sources: BTreeMap, +) -> eyre::Result<()> { + let calls: Vec = vec![result.debug]; + let flattened = calls.last().expect("we should have collected debug info").flatten(0); + let tui = Tui::new( + flattened, + 0, + decoder.contracts, + known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(), + sources + .into_iter() + .map(|(id, source)| { + let mut sources = BTreeMap::new(); + sources.insert(0, source); + (id.name, sources) + }) + .collect(), + Default::default(), + )?; + match tui.start().expect("Failed to start tui") { + TUIExitReason::CharExit => Ok(()), + } +} /// taken from cast run, should find common place struct TraceResult { From 8e1fb61c7fe219a27fc9234fab473c6f2d075e04 Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 17:26:35 -0400 Subject: [PATCH 06/37] comment --- cli/src/cmd/cast/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 7db46007ffa3f..5c03e02d085d9 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -72,7 +72,7 @@ pub struct CallArgs { #[clap(long, requires = "trace")] labels: Vec, - /// only for tracing mode + /// Can only be used with "--trace" #[clap(long)] evm_version: Option, From e797891cafc489f00ef7bb4e4a880d04532d23a2 Mon Sep 17 00:00:00 2001 From: N Date: Wed, 26 Jul 2023 23:21:20 -0400 Subject: [PATCH 07/37] change build -> fill --- cli/src/cmd/cast/call.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 5c03e02d085d9..09e96650dc029 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -247,12 +247,12 @@ impl CallArgs { return Ok(()); } - // build it last because we dont need anything from the built one - build_create_tx(&mut builder, value, code, sig, args).await?; + // fill it last because we dont need anything from the built one + fill_create(&mut builder, value, code, sig, args).await?; } _ => { - // build it first becasue builder parses args / addr - build_tx(&mut builder, tx.value, sig, args, data).await?; + // fill it first becasue builder parses args / addr + fill_tx(&mut builder, tx.value, sig, args, data).await?; if trace { let (mut executor, opts) = @@ -295,7 +295,7 @@ impl CallArgs { } /// builds the transaction from create args -async fn build_create_tx( +async fn fill_create( builder: &mut TxBuilder<'_, Provider>, value: Option, code: String, @@ -317,7 +317,7 @@ async fn build_create_tx( } /// builds the tx from args -async fn build_tx( +async fn fill_tx( builder: &mut TxBuilder<'_, Provider>, value: Option, sig: Option, From eb630291ac54f0db8b95ed326f1f83b3d6c56bb1 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 15:38:24 -0400 Subject: [PATCH 08/37] start of move to evm crate --- cli/src/cmd/cast/call.rs | 156 ++++++++++++--------------------------- evm/src/trace/mod.rs | 52 ++++++++++++- evm/src/trace/utils.rs | 108 +++++++++++++++++++-------- 3 files changed, 175 insertions(+), 141 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 09e96650dc029..4a5aad56b10bd 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -11,17 +11,19 @@ use ethers::{ }; use eyre::WrapErr; use forge::executor::{opts::EvmOpts, Backend, ExecutorBuilder}; -use foundry_config::{find_project_root_path, Config}; +use foundry_config::{find_project_root_path, Chain, Config}; use foundry_evm::{ debug::DebugArena, - executor::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + executor::{ + fork::CreateFork, DeployResult, Env, EvmError, ExecutionErr, Executor, RawCallResult, + }, trace::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, + utils::{print_traces, TraceResult}, + CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, TracingExecutor, }, - utils::evm_spec, }; -use std::{collections::BTreeMap, ops::DerefMut, str::FromStr}; +use std::{collections::BTreeMap, str::FromStr}; use ui::{TUIExitReason, Tui, Ui}; use yansi::Paint; @@ -116,60 +118,6 @@ pub enum CallSubcommands { }, } -/// a default executor with tracing enabled -struct TracingExecutor { - executor: Executor, -} - -impl TracingExecutor { - pub async fn new( - config: &foundry_config::Config, - version: Option, - rpc_opts: RpcOpts, - debug: bool, - ) -> eyre::Result<(Self, EvmOpts)> { - // todo:n find out what this is - let figment = - Config::figment_with_root(find_project_root_path(None).unwrap()).merge(rpc_opts); - - let mut evm_opts = figment.extract::()?; - - evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); - evm_opts.fork_block_number = config.fork_block_number; - - // Set up the execution environment - let env = evm_opts.evm_env().await; - - let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())).await; - - // configures a bare version of the evm executor: no cheatcode inspector is enabled, - // tracing will be enabled only for the targeted transaction - let builder = ExecutorBuilder::default() - .with_config(env) - .with_spec(evm_spec(&version.unwrap_or(config.evm_version))); - - let mut executor = builder.build(db); - - executor.set_tracing(true).set_debugger(debug); - - Ok((Self { executor }, evm_opts)) - } -} - -impl std::ops::Deref for TracingExecutor { - type Target = Executor; - - fn deref(&self) -> &Self::Target { - &self.executor - } -} - -impl DerefMut for TracingExecutor { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.executor - } -} - impl CallArgs { pub async fn run(self) -> eyre::Result<()> { let CallArgs { @@ -206,8 +154,11 @@ impl CallArgs { match command { Some(CallSubcommands::Create { code, sig, args, value }) => { if trace { - let (mut executor, opts) = - TracingExecutor::new(&config, evm_version, eth.rpc, debug).await?; + let (opts, env, fork, chain) = setup_fork_env(&config, eth.rpc).await?; + + let mut executor = + foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) + .await; let trace = match executor.deploy( sender, @@ -242,7 +193,7 @@ impl CallArgs { } }; - handle_traces(trace, config, opts, labels, verbose, debug).await?; + handle_traces(trace, config, chain, labels, verbose, debug).await?; return Ok(()); } @@ -255,8 +206,11 @@ impl CallArgs { fill_tx(&mut builder, tx.value, sig, args, data).await?; if trace { - let (mut executor, opts) = - TracingExecutor::new(&config, evm_version, eth.rpc, debug).await?; + let (opts, env, fork, chain) = setup_fork_env(&config, eth.rpc).await?; + + let mut executor = + foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) + .await; let (tx, _) = builder.build(); @@ -279,14 +233,17 @@ impl CallArgs { } }; - handle_traces(trace, config, opts, labels, verbose, debug).await?; + handle_traces(trace, config, chain, labels, verbose, debug).await?; return Ok(()); } } }; - let builder_output = builder.build(); + let builder_output: ( + ethers::types::transaction::eip2718::TypedTransaction, + Option, + ) = builder.build(); println!("{}", Cast::new(provider).call(builder_output, block).await?); @@ -294,7 +251,27 @@ impl CallArgs { } } -/// builds the transaction from create args +async fn setup_fork_env( + config: &Config, + rpc: RpcOpts, +) -> eyre::Result<(EvmOpts, Env, Option, Option)> { + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(rpc); + + let mut evm_opts = figment.extract::()?; + + evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + evm_opts.fork_block_number = config.fork_block_number; + + let env = evm_opts.evm_env().await; + + let fork = evm_opts.get_fork(config, env.clone()); + + let chain = evm_opts.get_remote_chain_id(); + + Ok((evm_opts, env, fork, chain)) +} + +/// builds the transaction from create arg async fn fill_create( builder: &mut TxBuilder<'_, Provider>, value: Option, @@ -342,13 +319,12 @@ async fn fill_tx( async fn handle_traces( mut result: TraceResult, config: Config, - evm_opts: EvmOpts, + chain: Option, labels: Vec, verbose: bool, debug: bool, ) -> eyre::Result<()> { - let mut etherscan_identifier = - EtherscanIdentifier::new(&config, evm_opts.get_remote_chain_id())?; + let mut etherscan_identifier = EtherscanIdentifier::new(&config, chain)?; let labeled_addresses = labels.iter().filter_map(|label_str| { let mut iter = label_str.split(':'); @@ -378,39 +354,9 @@ async fn handle_traces( let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; run_debugger(result, decoder, bytecode, sources)?; } else { - print_traces(&mut result, decoder, verbose).await?; - } - - Ok(()) -} - -async fn print_traces( - result: &mut TraceResult, - decoder: CallTraceDecoder, - verbose: bool, -) -> eyre::Result<()> { - if result.traces.is_empty() { - eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); - } - - println!("Traces:"); - for (_, trace) in &mut result.traces { - decoder.decode(trace).await; - if !verbose { - println!("{trace}"); - } else { - println!("{trace:#}"); - } - } - println!(); - - if result.success { - println!("{}", Paint::green("Transaction successfully executed.")); - } else { - println!("{}", Paint::red("Transaction failed.")); + foundry_evm::trace::utils::print_traces(&mut result, decoder, verbose).await?; } - println!("Gas used: {}", result.gas_used); Ok(()) } @@ -442,14 +388,6 @@ fn run_debugger( } } -/// taken from cast run, should find common place -struct TraceResult { - pub success: bool, - pub traces: Traces, - pub debug: DebugArena, - pub gas_used: u64, -} - #[cfg(test)] mod tests { use super::*; diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 9f276479be3b4..49ecbca92e2a2 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -1,10 +1,16 @@ use crate::{ - abi::CHEATCODE_ADDRESS, debug::Instruction, trace::identifier::LocalTraceIdentifier, CallKind, + abi::CHEATCODE_ADDRESS, + debug::Instruction, + executor::{fork::CreateFork, Backend, Executor, ExecutorBuilder}, + trace::identifier::LocalTraceIdentifier, + utils::evm_spec, + CallKind, }; pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; use ethers::{ abi::{ethereum_types::BigEndianHash, Address, RawLog}, core::utils::to_checksum, + solc::EvmVersion, types::{Bytes, DefaultFrame, GethDebugTracingOptions, StructLog, H256, U256}, }; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; @@ -15,6 +21,7 @@ use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashSet}, fmt::{self, Write}, + ops::{Deref, DerefMut}, }; use yansi::{Color, Paint}; @@ -25,8 +32,49 @@ pub mod identifier; mod decoder; pub mod node; -mod utils; +pub mod utils; +/// a default executor with tracing enabled +pub struct TracingExecutor { + executor: Executor, +} + +impl TracingExecutor { + pub async fn new( + env: revm::primitives::Env, + fork: Option, + version: Option, + debug: bool, + ) -> Self { + let db = Backend::spawn(fork).await; + + // configures a bare version of the evm executor: no cheatcode inspector is enabled, + // tracing will be enabled only for the targeted transaction + let builder = ExecutorBuilder::default() + .with_config(env) + .with_spec(evm_spec(&version.unwrap_or_default())); + + let mut executor = builder.build(db); + + executor.set_tracing(true).set_debugger(debug); + + Self { executor } + } +} + +impl Deref for TracingExecutor { + type Target = Executor; + + fn deref(&self) -> &Self::Target { + &self.executor + } +} + +impl DerefMut for TracingExecutor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.executor + } +} pub type Traces = Vec<(TraceKind, CallTraceArena)>; /// An arena of [CallTraceNode]s diff --git a/evm/src/trace/utils.rs b/evm/src/trace/utils.rs index e65d6c30346ec..5fa6c3c65e18d 100644 --- a/evm/src/trace/utils.rs +++ b/evm/src/trace/utils.rs @@ -1,12 +1,23 @@ //! utilities used within tracing -use crate::decode; +use crate::{debug::DebugArena, decode}; use ethers::{ abi::{Abi, Address, Function, ParamType, Token}, core::utils::to_checksum, + solc::{artifacts::ContractBytecodeSome, ArtifactId}, }; use foundry_common::{abi::format_token, SELECTOR_LEN}; -use std::collections::HashMap; +use foundry_config::{Chain, Config}; +use std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, +}; +use yansi::Paint; + +use super::{ + identifier::{EtherscanIdentifier, SignaturesIdentifier}, + CallTraceDecoder, CallTraceDecoderBuilder, Traces, +}; /// Returns the label for the given `token` /// @@ -55,30 +66,30 @@ pub(crate) fn decode_cheatcode_inputs( Some(decoded.iter().map(format_token).collect()) } "deriveKey" => Some(vec!["".to_string()]), - "parseJson" | - "parseJsonUint" | - "parseJsonUintArray" | - "parseJsonInt" | - "parseJsonIntArray" | - "parseJsonString" | - "parseJsonStringArray" | - "parseJsonAddress" | - "parseJsonAddressArray" | - "parseJsonBool" | - "parseJsonBoolArray" | - "parseJsonBytes" | - "parseJsonBytesArray" | - "parseJsonBytes32" | - "parseJsonBytes32Array" | - "writeJson" | - "keyExists" | - "serializeBool" | - "serializeUint" | - "serializeInt" | - "serializeAddress" | - "serializeBytes32" | - "serializeString" | - "serializeBytes" => { + "parseJson" + | "parseJsonUint" + | "parseJsonUintArray" + | "parseJsonInt" + | "parseJsonIntArray" + | "parseJsonString" + | "parseJsonStringArray" + | "parseJsonAddress" + | "parseJsonAddressArray" + | "parseJsonBool" + | "parseJsonBoolArray" + | "parseJsonBytes" + | "parseJsonBytesArray" + | "parseJsonBytes32" + | "parseJsonBytes32Array" + | "writeJson" + | "keyExists" + | "serializeBool" + | "serializeUint" + | "serializeInt" + | "serializeAddress" + | "serializeBytes32" + | "serializeString" + | "serializeBytes" => { if verbosity == 5 { None } else { @@ -105,17 +116,54 @@ pub(crate) fn decode_cheatcode_outputs( ) -> Option { if func.name.starts_with("env") { // redacts the value stored in the env var - return Some("".to_string()) + return Some("".to_string()); } if func.name == "deriveKey" { // redacts derived private key - return Some("".to_string()) + return Some("".to_string()); } if func.name == "parseJson" && verbosity != 5 { - return Some("".to_string()) + return Some("".to_string()); } if func.name == "readFile" && verbosity != 5 { - return Some("".to_string()) + return Some("".to_string()); } None } + +pub async fn print_traces( + result: &mut TraceResult, + decoder: CallTraceDecoder, + verbose: bool, +) -> eyre::Result<()> { + if result.traces.is_empty() { + eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + } + + println!("Traces:"); + for (_, trace) in &mut result.traces { + decoder.decode(trace).await; + if !verbose { + println!("{trace}"); + } else { + println!("{trace:#}"); + } + } + println!(); + + if result.success { + println!("{}", Paint::green("Transaction successfully executed.")); + } else { + println!("{}", Paint::red("Transaction failed.")); + } + + println!("Gas used: {}", result.gas_used); + Ok(()) +} + +pub struct TraceResult { + pub success: bool, + pub traces: Traces, + pub debug: DebugArena, + pub gas_used: u64, +} From 1aaa59ff47157086c6fa0a2e6bb6535de5440be7 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 15:45:39 -0400 Subject: [PATCH 09/37] rename --- cli/src/cmd/cast/call.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 4a5aad56b10bd..c18838d5b26cc 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -154,7 +154,7 @@ impl CallArgs { match command { Some(CallSubcommands::Create { code, sig, args, value }) => { if trace { - let (opts, env, fork, chain) = setup_fork_env(&config, eth.rpc).await?; + let (env, fork, chain) = get_fork_material(&config, eth.rpc).await?; let mut executor = foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) @@ -206,7 +206,7 @@ impl CallArgs { fill_tx(&mut builder, tx.value, sig, args, data).await?; if trace { - let (opts, env, fork, chain) = setup_fork_env(&config, eth.rpc).await?; + let (env, fork, chain) = get_fork_material(&config, eth.rpc).await?; let mut executor = foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) @@ -251,10 +251,10 @@ impl CallArgs { } } -async fn setup_fork_env( +async fn get_fork_material( config: &Config, rpc: RpcOpts, -) -> eyre::Result<(EvmOpts, Env, Option, Option)> { +) -> eyre::Result<(Env, Option, Option)> { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(rpc); let mut evm_opts = figment.extract::()?; @@ -266,9 +266,7 @@ async fn setup_fork_env( let fork = evm_opts.get_fork(config, env.clone()); - let chain = evm_opts.get_remote_chain_id(); - - Ok((evm_opts, env, fork, chain)) + Ok((env, fork, evm_opts.get_remote_chain_id())) } /// builds the transaction from create arg @@ -354,7 +352,7 @@ async fn handle_traces( let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; run_debugger(result, decoder, bytecode, sources)?; } else { - foundry_evm::trace::utils::print_traces(&mut result, decoder, verbose).await?; + print_traces(&mut result, decoder, verbose).await?; } Ok(()) From ff90e6bd64e29932653c2f9ac5cded253af89168 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 16:19:10 -0400 Subject: [PATCH 10/37] put fork setup on tracing executor --- cli/src/cmd/cast/call.rs | 51 +++++++++++++++++----------------------- evm/src/trace/mod.rs | 22 +++++++++++++++-- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index c18838d5b26cc..14ad313aa0132 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -1,6 +1,6 @@ // cast estimate subcommands use crate::{ - opts::{EthereumOpts, RpcOpts, TransactionOpts}, + opts::{EthereumOpts, TransactionOpts}, utils::{self, parse_ether_value}, }; use cast::{Cast, TxBuilder}; @@ -10,22 +10,19 @@ use ethers::{ types::{BlockId, NameOrAddress, U256}, }; use eyre::WrapErr; -use forge::executor::{opts::EvmOpts, Backend, ExecutorBuilder}; -use foundry_config::{find_project_root_path, Chain, Config}; +use forge::executor::opts::EvmOpts; +use foundry_config::{find_project_root_path, Config}; use foundry_evm::{ debug::DebugArena, - executor::{ - fork::CreateFork, DeployResult, Env, EvmError, ExecutionErr, Executor, RawCallResult, - }, + executor::{DeployResult, EvmError, ExecutionErr, RawCallResult}, trace::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, utils::{print_traces, TraceResult}, - CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, TracingExecutor, + CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, TracingExecutor, }, }; use std::{collections::BTreeMap, str::FromStr}; use ui::{TUIExitReason, Tui, Ui}; -use yansi::Paint; type Provider = ethers::providers::Provider>; @@ -154,7 +151,13 @@ impl CallArgs { match command { Some(CallSubcommands::Create { code, sig, args, value }) => { if trace { - let (env, fork, chain) = get_fork_material(&config, eth.rpc).await?; + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) + .merge(eth.rpc); + + let evm_opts = figment.extract::()?; + + let (env, fork, chain) = + TracingExecutor::get_fork_material(&config, evm_opts).await?; let mut executor = foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) @@ -198,15 +201,21 @@ impl CallArgs { return Ok(()); } - // fill it last because we dont need anything from the built one + // fill the builder after the comditional so we dont move values fill_create(&mut builder, value, code, sig, args).await?; } _ => { - // fill it first becasue builder parses args / addr + // fill first here because we need to use the builder in the conditional fill_tx(&mut builder, tx.value, sig, args, data).await?; if trace { - let (env, fork, chain) = get_fork_material(&config, eth.rpc).await?; + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) + .merge(eth.rpc); + + let evm_opts = figment.extract::()?; + + let (env, fork, chain) = + TracingExecutor::get_fork_material(&config, evm_opts).await?; let mut executor = foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) @@ -251,24 +260,6 @@ impl CallArgs { } } -async fn get_fork_material( - config: &Config, - rpc: RpcOpts, -) -> eyre::Result<(Env, Option, Option)> { - let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(rpc); - - let mut evm_opts = figment.extract::()?; - - evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); - evm_opts.fork_block_number = config.fork_block_number; - - let env = evm_opts.evm_env().await; - - let fork = evm_opts.get_fork(config, env.clone()); - - Ok((env, fork, evm_opts.get_remote_chain_id())) -} - /// builds the transaction from create arg async fn fill_create( builder: &mut TxBuilder<'_, Provider>, diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 49ecbca92e2a2..57e6a5629478b 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -1,7 +1,7 @@ use crate::{ abi::CHEATCODE_ADDRESS, debug::Instruction, - executor::{fork::CreateFork, Backend, Executor, ExecutorBuilder}, + executor::{fork::CreateFork, opts::EvmOpts, Backend, Executor, ExecutorBuilder}, trace::identifier::LocalTraceIdentifier, utils::evm_spec, CallKind, @@ -14,9 +14,13 @@ use ethers::{ types::{Bytes, DefaultFrame, GethDebugTracingOptions, StructLog, H256, U256}, }; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_config::{find_project_root_path, Config}; use hashbrown::HashMap; use node::CallTraceNode; -use revm::interpreter::{opcode, CallContext, InstructionResult, Memory, Stack}; +use revm::{ + interpreter::{opcode, CallContext, InstructionResult, Memory, Stack}, + primitives::Env, +}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashSet}, @@ -60,6 +64,20 @@ impl TracingExecutor { Self { executor } } + + pub async fn get_fork_material( + config: &Config, + mut evm_opts: EvmOpts, + ) -> eyre::Result<(Env, Option, Option)> { + evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + evm_opts.fork_block_number = config.fork_block_number; + + let env = evm_opts.evm_env().await; + + let fork = evm_opts.get_fork(config, env.clone()); + + Ok((env, fork, evm_opts.get_remote_chain_id())) + } } impl Deref for TracingExecutor { From dee0d34c94f2bdbdb236fcc2bbdeebb64a53fd41 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 16:20:12 -0400 Subject: [PATCH 11/37] comment --- cli/src/cmd/cast/call.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 14ad313aa0132..215099df8f245 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -260,7 +260,7 @@ impl CallArgs { } } -/// builds the transaction from create arg +/// fills the builder from create arg async fn fill_create( builder: &mut TxBuilder<'_, Provider>, value: Option, @@ -282,7 +282,7 @@ async fn fill_create( Ok(()) } -/// builds the tx from args +/// fills the builder from args async fn fill_tx( builder: &mut TxBuilder<'_, Provider>, value: Option, From 2136cce0729dbef203e62b0c33b7f00bfd374237 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 16:30:07 -0400 Subject: [PATCH 12/37] return err on no traces --- cli/src/cmd/cast/call.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 215099df8f245..c57d07c0ce4aa 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -172,7 +172,10 @@ impl CallArgs { Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => { TraceResult { success: true, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + traces: vec![( + TraceKind::Execution, + traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, + )], debug: run_debug.unwrap_or_default(), gas_used, } @@ -183,7 +186,10 @@ impl CallArgs { } = *inner; TraceResult { success: !reverted, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + traces: vec![( + TraceKind::Execution, + traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, + )], debug: run_debug.unwrap_or_default(), gas_used, } @@ -232,7 +238,10 @@ impl CallArgs { Ok(RawCallResult { gas_used, traces, reverted, debug, .. }) => { TraceResult { success: !reverted, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], + traces: vec![( + TraceKind::Execution, + traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, + )], debug: debug.unwrap_or_default(), gas_used, } From 5de5f0520086a79d16bf980a2b3b0bfe96c8ad6c Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 21:16:29 -0400 Subject: [PATCH 13/37] moving trace handlers to cast::cmd::utils --- cli/src/cmd/cast/call.rs | 86 ++----------------------- cli/src/cmd/utils.rs | 131 +++++++++++++++++++++++++++++++++++++-- evm/src/trace/utils.rs | 52 +--------------- 3 files changed, 131 insertions(+), 138 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index c57d07c0ce4aa..83d762420b48f 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -1,28 +1,23 @@ // cast estimate subcommands use crate::{ + cmd::utils::{handle_traces, TraceResult}, opts::{EthereumOpts, TransactionOpts}, utils::{self, parse_ether_value}, }; use cast::{Cast, TxBuilder}; use clap::Parser; use ethers::{ - solc::{artifacts::ContractBytecodeSome, ArtifactId, EvmVersion}, + solc::EvmVersion, types::{BlockId, NameOrAddress, U256}, }; use eyre::WrapErr; use forge::executor::opts::EvmOpts; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{ - debug::DebugArena, executor::{DeployResult, EvmError, ExecutionErr, RawCallResult}, - trace::{ - identifier::{EtherscanIdentifier, SignaturesIdentifier}, - utils::{print_traces, TraceResult}, - CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, TracingExecutor, - }, + trace::{TraceKind, TracingExecutor}, }; -use std::{collections::BTreeMap, str::FromStr}; -use ui::{TUIExitReason, Tui, Ui}; +use std::str::FromStr; type Provider = ethers::providers::Provider>; @@ -313,79 +308,6 @@ async fn fill_tx( Ok(()) } -/// labels the traces, conditonally prints them or opens the debugger -async fn handle_traces( - mut result: TraceResult, - config: Config, - chain: Option, - labels: Vec, - verbose: bool, - debug: bool, -) -> eyre::Result<()> { - let mut etherscan_identifier = EtherscanIdentifier::new(&config, chain)?; - - let labeled_addresses = labels.iter().filter_map(|label_str| { - let mut iter = label_str.split(':'); - - if let Some(addr) = iter.next() { - if let (Ok(address), Some(label)) = - (ethers::types::Address::from_str(addr), iter.next()) - { - return Some((address, label.to_string())); - } - } - None - }); - - let mut decoder = CallTraceDecoderBuilder::new().with_labels(labeled_addresses).build(); - - decoder.add_signature_identifier(SignaturesIdentifier::new( - Config::foundry_cache_dir(), - config.offline, - )?); - - for (_, trace) in &mut result.traces { - decoder.identify(trace, &mut etherscan_identifier); - } - - if debug { - let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; - run_debugger(result, decoder, bytecode, sources)?; - } else { - print_traces(&mut result, decoder, verbose).await?; - } - - Ok(()) -} - -fn run_debugger( - result: TraceResult, - decoder: CallTraceDecoder, - known_contracts: BTreeMap, - sources: BTreeMap, -) -> eyre::Result<()> { - let calls: Vec = vec![result.debug]; - let flattened = calls.last().expect("we should have collected debug info").flatten(0); - let tui = Tui::new( - flattened, - 0, - decoder.contracts, - known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(), - sources - .into_iter() - .map(|(id, source)| { - let mut sources = BTreeMap::new(); - sources.insert(0, source); - (id.name, sources) - }) - .collect(), - Default::default(), - )?; - match tui.start().expect("Failed to start tui") { - TUIExitReason::CharExit => Ok(()), - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index 6ab7890ee3862..2aaf6814c7b9c 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -3,19 +3,27 @@ use ethers::{ abi::Abi, core::types::Chain, solc::{ - artifacts::{CompactBytecode, CompactDeployedBytecode}, + artifacts::{CompactBytecode, CompactDeployedBytecode, ContractBytecodeSome}, cache::{CacheEntry, SolFilesCache}, info::ContractInfo, utils::read_json_file, - Artifact, ProjectCompileOutput, + Artifact, ArtifactId, ProjectCompileOutput, }, }; use eyre::WrapErr; use forge::executor::opts::EvmOpts; use foundry_common::{cli_warn, fs, TestFunctionExt}; use foundry_config::{error::ExtractConfigError, figment::Figment, Chain as ConfigChain, Config}; -use std::{fmt::Write, path::PathBuf}; +use foundry_evm::{ + debug::DebugArena, + trace::{ + identifier::{EtherscanIdentifier, SignaturesIdentifier}, + CallTraceDecoder, CallTraceDecoderBuilder, Traces, + }, +}; +use std::{collections::BTreeMap, fmt::Write, path::PathBuf, str::FromStr}; use tracing::trace; +use ui::{TUIExitReason, Tui, Ui}; use yansi::Paint; /// Common trait for all cli commands @@ -94,7 +102,7 @@ pub fn get_cached_entry_by_name( } if let Some(entry) = cached_entry { - return Ok(entry) + return Ok(entry); } let mut err = format!("could not find artifact: `{name}`"); @@ -166,7 +174,7 @@ macro_rules! update_progress { /// True if the network calculates gas costs differently. pub fn has_different_gas_calc(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) + return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); } false } @@ -174,7 +182,7 @@ pub fn has_different_gas_calc(chain: u64) -> bool { /// True if it supports broadcasting in batches. pub fn has_batch_support(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) + return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); } true } @@ -299,3 +307,114 @@ pub fn read_constructor_args_file(constructor_args_path: PathBuf) -> eyre::Resul }; Ok(args) } + +/// A slimmed down return from the executor +pub struct TraceResult { + pub success: bool, + pub traces: Traces, + pub debug: DebugArena, + pub gas_used: u64, +} + +/// labels the traces, conditonally prints them or opens the debugger +pub async fn handle_traces( + mut result: TraceResult, + config: Config, + chain: Option, + labels: Vec, + verbose: bool, + debug: bool, +) -> eyre::Result<()> { + let mut etherscan_identifier = EtherscanIdentifier::new(&config, chain)?; + + let labeled_addresses = labels.iter().filter_map(|label_str| { + let mut iter = label_str.split(':'); + + if let Some(addr) = iter.next() { + if let (Ok(address), Some(label)) = + (ethers::types::Address::from_str(addr), iter.next()) + { + return Some((address, label.to_string())); + } + } + None + }); + + let mut decoder = CallTraceDecoderBuilder::new().with_labels(labeled_addresses).build(); + + decoder.add_signature_identifier(SignaturesIdentifier::new( + Config::foundry_cache_dir(), + config.offline, + )?); + + for (_, trace) in &mut result.traces { + decoder.identify(trace, &mut etherscan_identifier); + } + + if debug { + let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; + run_debugger(result, decoder, bytecode, sources)?; + } else { + print_traces(&mut result, decoder, verbose).await?; + } + + Ok(()) +} + +pub async fn print_traces( + result: &mut TraceResult, + decoder: CallTraceDecoder, + verbose: bool, +) -> eyre::Result<()> { + if result.traces.is_empty() { + eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + } + + println!("Traces:"); + for (_, trace) in &mut result.traces { + decoder.decode(trace).await; + if !verbose { + println!("{trace}"); + } else { + println!("{trace:#}"); + } + } + println!(); + + if result.success { + println!("{}", Paint::green("Transaction successfully executed.")); + } else { + println!("{}", Paint::red("Transaction failed.")); + } + + println!("Gas used: {}", result.gas_used); + Ok(()) +} + +pub fn run_debugger( + result: TraceResult, + decoder: CallTraceDecoder, + known_contracts: BTreeMap, + sources: BTreeMap, +) -> eyre::Result<()> { + let calls: Vec = vec![result.debug]; + let flattened = calls.last().expect("we should have collected debug info").flatten(0); + let tui = Tui::new( + flattened, + 0, + decoder.contracts, + known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(), + sources + .into_iter() + .map(|(id, source)| { + let mut sources = BTreeMap::new(); + sources.insert(0, source); + (id.name, sources) + }) + .collect(), + Default::default(), + )?; + match tui.start().expect("Failed to start tui") { + TUIExitReason::CharExit => Ok(()), + } +} diff --git a/evm/src/trace/utils.rs b/evm/src/trace/utils.rs index 5fa6c3c65e18d..333de99238824 100644 --- a/evm/src/trace/utils.rs +++ b/evm/src/trace/utils.rs @@ -1,23 +1,12 @@ //! utilities used within tracing -use crate::{debug::DebugArena, decode}; +use crate::decode; use ethers::{ abi::{Abi, Address, Function, ParamType, Token}, core::utils::to_checksum, - solc::{artifacts::ContractBytecodeSome, ArtifactId}, }; use foundry_common::{abi::format_token, SELECTOR_LEN}; -use foundry_config::{Chain, Config}; -use std::{ - collections::{BTreeMap, HashMap}, - str::FromStr, -}; -use yansi::Paint; - -use super::{ - identifier::{EtherscanIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, Traces, -}; +use std::collections::HashMap; /// Returns the label for the given `token` /// @@ -130,40 +119,3 @@ pub(crate) fn decode_cheatcode_outputs( } None } - -pub async fn print_traces( - result: &mut TraceResult, - decoder: CallTraceDecoder, - verbose: bool, -) -> eyre::Result<()> { - if result.traces.is_empty() { - eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); - } - - println!("Traces:"); - for (_, trace) in &mut result.traces { - decoder.decode(trace).await; - if !verbose { - println!("{trace}"); - } else { - println!("{trace:#}"); - } - } - println!(); - - if result.success { - println!("{}", Paint::green("Transaction successfully executed.")); - } else { - println!("{}", Paint::red("Transaction failed.")); - } - - println!("Gas used: {}", result.gas_used); - Ok(()) -} - -pub struct TraceResult { - pub success: bool, - pub traces: Traces, - pub debug: DebugArena, - pub gas_used: u64, -} From ca5ec5d1ca75d31a5b4f33901c6394b0b2c79bae Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 21:22:22 -0400 Subject: [PATCH 14/37] rm formatting --- evm/src/trace/utils.rs | 56 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/evm/src/trace/utils.rs b/evm/src/trace/utils.rs index 333de99238824..c390cbc620b6a 100644 --- a/evm/src/trace/utils.rs +++ b/evm/src/trace/utils.rs @@ -55,30 +55,30 @@ pub(crate) fn decode_cheatcode_inputs( Some(decoded.iter().map(format_token).collect()) } "deriveKey" => Some(vec!["".to_string()]), - "parseJson" - | "parseJsonUint" - | "parseJsonUintArray" - | "parseJsonInt" - | "parseJsonIntArray" - | "parseJsonString" - | "parseJsonStringArray" - | "parseJsonAddress" - | "parseJsonAddressArray" - | "parseJsonBool" - | "parseJsonBoolArray" - | "parseJsonBytes" - | "parseJsonBytesArray" - | "parseJsonBytes32" - | "parseJsonBytes32Array" - | "writeJson" - | "keyExists" - | "serializeBool" - | "serializeUint" - | "serializeInt" - | "serializeAddress" - | "serializeBytes32" - | "serializeString" - | "serializeBytes" => { + "parseJson" | + "parseJsonUint" | + "parseJsonUintArray" | + "parseJsonInt" | + "parseJsonIntArray" | + "parseJsonString" | + "parseJsonStringArray" | + "parseJsonAddress" | + "parseJsonAddressArray" | + "parseJsonBool" | + "parseJsonBoolArray" | + "parseJsonBytes" | + "parseJsonBytesArray" | + "parseJsonBytes32" | + "parseJsonBytes32Array" | + "writeJson" | + "keyExists" | + "serializeBool" | + "serializeUint" | + "serializeInt" | + "serializeAddress" | + "serializeBytes32" | + "serializeString" | + "serializeBytes" => { if verbosity == 5 { None } else { @@ -105,17 +105,17 @@ pub(crate) fn decode_cheatcode_outputs( ) -> Option { if func.name.starts_with("env") { // redacts the value stored in the env var - return Some("".to_string()); + return Some("".to_string()) } if func.name == "deriveKey" { // redacts derived private key - return Some("".to_string()); + return Some("".to_string()) } if func.name == "parseJson" && verbosity != 5 { - return Some("".to_string()); + return Some("".to_string()) } if func.name == "readFile" && verbosity != 5 { - return Some("".to_string()); + return Some("".to_string()) } None } From e31c432f23e632167324157ff38265774c3ff8a4 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 27 Jul 2023 21:24:54 -0400 Subject: [PATCH 15/37] fmt --- cli/src/cmd/utils.rs | 6 +++--- evm/src/trace/utils.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index 2aaf6814c7b9c..7b0549d641f56 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -102,7 +102,7 @@ pub fn get_cached_entry_by_name( } if let Some(entry) = cached_entry { - return Ok(entry); + return Ok(entry) } let mut err = format!("could not find artifact: `{name}`"); @@ -174,7 +174,7 @@ macro_rules! update_progress { /// True if the network calculates gas costs differently. pub fn has_different_gas_calc(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); + return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) } false } @@ -182,7 +182,7 @@ pub fn has_different_gas_calc(chain: u64) -> bool { /// True if it supports broadcasting in batches. pub fn has_batch_support(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); + return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) } true } diff --git a/evm/src/trace/utils.rs b/evm/src/trace/utils.rs index c390cbc620b6a..e65d6c30346ec 100644 --- a/evm/src/trace/utils.rs +++ b/evm/src/trace/utils.rs @@ -78,7 +78,7 @@ pub(crate) fn decode_cheatcode_inputs( "serializeAddress" | "serializeBytes32" | "serializeString" | - "serializeBytes" => { + "serializeBytes" => { if verbosity == 5 { None } else { From 6dbf0cd9926664cf234e1696738c41d152ecfe59 Mon Sep 17 00:00:00 2001 From: N Date: Fri, 28 Jul 2023 00:30:44 -0400 Subject: [PATCH 16/37] use a ref --- cli/src/cmd/cast/call.rs | 4 ++-- cli/src/cmd/utils.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 83d762420b48f..b0500f6c294a6 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -197,7 +197,7 @@ impl CallArgs { } }; - handle_traces(trace, config, chain, labels, verbose, debug).await?; + handle_traces(trace, &config, chain, labels, verbose, debug).await?; return Ok(()); } @@ -246,7 +246,7 @@ impl CallArgs { } }; - handle_traces(trace, config, chain, labels, verbose, debug).await?; + handle_traces(trace, &config, chain, labels, verbose, debug).await?; return Ok(()); } diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index 7b0549d641f56..21a33f9ae80f5 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -319,7 +319,7 @@ pub struct TraceResult { /// labels the traces, conditonally prints them or opens the debugger pub async fn handle_traces( mut result: TraceResult, - config: Config, + config: &Config, chain: Option, labels: Vec, verbose: bool, From 005a5cadd8399ba47696edf262f9168fe1bd1dee Mon Sep 17 00:00:00 2001 From: N Date: Fri, 28 Jul 2023 00:34:24 -0400 Subject: [PATCH 17/37] fix unneeded borrow --- cli/src/cmd/utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index 21a33f9ae80f5..e632cf25dcf6d 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -102,7 +102,7 @@ pub fn get_cached_entry_by_name( } if let Some(entry) = cached_entry { - return Ok(entry) + return Ok(entry); } let mut err = format!("could not find artifact: `{name}`"); @@ -174,7 +174,7 @@ macro_rules! update_progress { /// True if the network calculates gas costs differently. pub fn has_different_gas_calc(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) + return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); } false } @@ -182,7 +182,7 @@ pub fn has_different_gas_calc(chain: u64) -> bool { /// True if it supports broadcasting in batches. pub fn has_batch_support(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) + return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); } true } @@ -325,7 +325,7 @@ pub async fn handle_traces( verbose: bool, debug: bool, ) -> eyre::Result<()> { - let mut etherscan_identifier = EtherscanIdentifier::new(&config, chain)?; + let mut etherscan_identifier = EtherscanIdentifier::new(config, chain)?; let labeled_addresses = labels.iter().filter_map(|label_str| { let mut iter = label_str.split(':'); From 4ce14bd803f7a6a3a9e50f3e99a4f4483441a93a Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 18:40:02 -0400 Subject: [PATCH 18/37] unused import --- evm/src/trace/mod.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 57e6a5629478b..d24abfbf8c7ec 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -14,7 +14,7 @@ use ethers::{ types::{Bytes, DefaultFrame, GethDebugTracingOptions, StructLog, H256, U256}, }; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; -use foundry_config::{find_project_root_path, Config}; +use foundry_config::Config; use hashbrown::HashMap; use node::CallTraceNode; use revm::{ @@ -72,7 +72,7 @@ impl TracingExecutor { evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); evm_opts.fork_block_number = config.fork_block_number; - let env = evm_opts.evm_env().await; + let env = evm_opts.evm_env().await?; let fork = evm_opts.get_fork(config, env.clone()); @@ -93,6 +93,7 @@ impl DerefMut for TracingExecutor { &mut self.executor } } + pub type Traces = Vec<(TraceKind, CallTraceArena)>; /// An arena of [CallTraceNode]s @@ -148,7 +149,7 @@ impl CallTraceArena { .map(|node| { if node.trace.created() { if let RawOrDecodedReturnData::Raw(ref bytes) = node.trace.output { - return (&node.trace.address, Some(bytes.as_ref())) + return (&node.trace.address, Some(bytes.as_ref())); } } @@ -193,12 +194,12 @@ impl CallTraceArena { Instruction::OpCode(opc) => { match opc { // If yes, descend into a child trace - opcode::CREATE | - opcode::CREATE2 | - opcode::DELEGATECALL | - opcode::CALL | - opcode::STATICCALL | - opcode::CALLCODE => { + opcode::CREATE + | opcode::CREATE2 + | opcode::DELEGATECALL + | opcode::CALL + | opcode::STATICCALL + | opcode::CALLCODE => { self.add_to_geth_trace( storage, &self.arena[trace_node.children[child_id]], @@ -222,7 +223,7 @@ impl CallTraceArena { opts: GethDebugTracingOptions, ) -> DefaultFrame { if self.arena.is_empty() { - return Default::default() + return Default::default(); } let mut storage = HashMap::new(); @@ -660,7 +661,7 @@ pub fn load_contracts( .iter() .filter_map(|(addr, name)| { if let Ok(Some((_, (abi, _)))) = contracts.find_by_name_or_identifier(name) { - return Some((*addr, (name.clone(), abi.clone()))) + return Some((*addr, (name.clone(), abi.clone()))); } None }) From 72c5bab9ff6b2e53e379d99cb0918ce694cfe8e6 Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 18:45:35 -0400 Subject: [PATCH 19/37] panic instead of bail --- cli/src/cmd/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index e632cf25dcf6d..051b646b06523 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -367,7 +367,7 @@ pub async fn print_traces( verbose: bool, ) -> eyre::Result<()> { if result.traces.is_empty() { - eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + panic!("No traces found") } println!("Traces:"); From 5c26bddd8d59b59d87bb02f4ff8f1ef140b03182 Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:20:16 -0400 Subject: [PATCH 20/37] initial --- cli/src/cmd/cast/run.rs | 167 ++++++---------------------------------- evm/src/trace/mod.rs | 1 + 2 files changed, 26 insertions(+), 142 deletions(-) diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 4a7a99846522c..f6e4a996d9b3c 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -1,32 +1,24 @@ -use crate::{init_progress, opts::RpcOpts, update_progress, utils}; -use cast::{ - executor::{EvmError, ExecutionErr}, - trace::{identifier::SignaturesIdentifier, CallTraceDecoder, Traces}, +use crate::{ + cmd::utils::{handle_traces, TraceResult}, + init_progress, + opts::RpcOpts, + update_progress, utils, }; +use cast::executor::{EvmError, ExecutionErr}; use clap::Parser; -use ethers::{ - abi::Address, - prelude::{artifacts::ContractBytecodeSome, ArtifactId, Middleware}, - solc::EvmVersion, - types::H160, -}; +use ethers::{prelude::Middleware, solc::EvmVersion, types::H160}; use eyre::WrapErr; use forge::{ - debug::DebugArena, executor::{ - inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, Backend, DeployResult, - ExecutorBuilder, RawCallResult, + inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, DeployResult, RawCallResult, }, revm::primitives::U256 as rU256, - trace::{identifier::EtherscanIdentifier, CallTraceDecoderBuilder, TraceKind}, + trace::TraceKind, utils::h256_to_b256, }; use foundry_config::{find_project_root_path, Config}; -use foundry_evm::utils::evm_spec; -use std::{collections::BTreeMap, str::FromStr}; +use foundry_evm::trace::TracingExecutor; use tracing::trace; -use ui::{TUIExitReason, Tui, Ui}; -use yansi::Paint; const ARBITRUM_SENDER: H160 = H160([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -82,8 +74,9 @@ impl RunArgs { pub async fn run(self) -> eyre::Result<()> { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc); - let mut evm_opts = figment.extract::()?; - let config = Config::from_provider(figment).sanitized(); + let evm_opts = figment.extract::()?; + let mut config = Config::from_provider(figment).sanitized(); + let provider = utils::get_provider(&config)?; let tx_hash = self.tx_hash.parse().wrap_err("invalid tx hash")?; @@ -96,27 +89,14 @@ impl RunArgs { .block_number .ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))? .as_u64(); - evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); - // we need to set the fork block to the previous block, because that's the state at - // which we access the data in order to execute the transaction(s) - evm_opts.fork_block_number = Some(tx_block_number - 1); - // Set up the execution environment - let mut env = evm_opts.evm_env().await?; - // can safely disable base fee checks on replaying txs because can - // assume those checks already passed on confirmed txs - env.cfg.disable_base_fee = true; - let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())).await; + config.fork_block_number = Some(tx_block_number - 1); - // configures a bare version of the evm executor: no cheatcode inspector is enabled, - // tracing will be enabled only for the targeted transaction - let builder = ExecutorBuilder::default() - .with_config(env) - .with_spec(evm_spec(&self.evm_version.unwrap_or(config.evm_version))); + let (mut env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; - let mut executor = builder.build(db); + let mut executor = + TracingExecutor::new(env.clone(), fork, self.evm_version, self.debug).await; - let mut env = executor.env().clone(); env.block.number = rU256::from(tx_block_number); let block = provider.get_block_with_txs(tx_block_number).await?; @@ -142,10 +122,10 @@ impl RunArgs { // and gas limit 0 which causes reverts, so we skip it if tx.from == ARBITRUM_SENDER { update_progress!(pb, index); - continue + continue; } if tx.hash().eq(&tx_hash) { - break + break; } configure_tx_env(&mut env, &tx); @@ -168,11 +148,8 @@ impl RunArgs { } // Execute our transaction - let mut result = { - executor - .set_tracing(true) - .set_debugger(self.debug) - .set_trace_printer(self.trace_printer); + let result = { + executor.set_trace_printer(self.trace_printer); configure_tx_env(&mut env, &tx); @@ -187,7 +164,7 @@ impl RunArgs { .. } = executor.commit_tx_with_env(env).unwrap(); - RunResult { + TraceResult { success: !reverted, traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], debug: run_debug.unwrap_or_default(), @@ -196,7 +173,7 @@ impl RunArgs { } else { trace!(tx=?tx.hash, "executing create transaction"); match executor.deploy_with_env(env, None) { - Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => RunResult { + Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => TraceResult { success: true, traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], debug: run_debug.unwrap_or_default(), @@ -205,7 +182,7 @@ impl RunArgs { Err(EvmError::Execution(inner)) => { let ExecutionErr { reverted, gas_used, traces, debug: run_debug, .. } = *inner; - RunResult { + TraceResult { success: !reverted, traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], debug: run_debug.unwrap_or_default(), @@ -219,102 +196,8 @@ impl RunArgs { } }; - let mut etherscan_identifier = - EtherscanIdentifier::new(&config, evm_opts.get_remote_chain_id())?; - - let labeled_addresses = self.label.iter().filter_map(|label_str| { - let mut iter = label_str.split(':'); - - if let Some(addr) = iter.next() { - if let (Ok(address), Some(label)) = (Address::from_str(addr), iter.next()) { - return Some((address, label.to_string())) - } - } - None - }); - - let mut decoder = CallTraceDecoderBuilder::new().with_labels(labeled_addresses).build(); + handle_traces(result, &config, chain, self.label, self.verbose, self.debug).await?; - decoder.add_signature_identifier(SignaturesIdentifier::new( - Config::foundry_cache_dir(), - config.offline, - )?); - - for (_, trace) in &mut result.traces { - decoder.identify(trace, &mut etherscan_identifier); - } - - if self.debug { - let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?; - run_debugger(result, decoder, bytecode, sources)?; - } else { - print_traces(&mut result, decoder, self.verbose).await?; - } Ok(()) } } - -fn run_debugger( - result: RunResult, - decoder: CallTraceDecoder, - known_contracts: BTreeMap, - sources: BTreeMap, -) -> eyre::Result<()> { - let calls: Vec = vec![result.debug]; - let flattened = calls.last().expect("we should have collected debug info").flatten(0); - let tui = Tui::new( - flattened, - 0, - decoder.contracts, - known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(), - sources - .into_iter() - .map(|(id, source)| { - let mut sources = BTreeMap::new(); - sources.insert(0, source); - (id.name, sources) - }) - .collect(), - Default::default(), - )?; - match tui.start().expect("Failed to start tui") { - TUIExitReason::CharExit => Ok(()), - } -} - -async fn print_traces( - result: &mut RunResult, - decoder: CallTraceDecoder, - verbose: bool, -) -> eyre::Result<()> { - if result.traces.is_empty() { - eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); - } - - println!("Traces:"); - for (_, trace) in &mut result.traces { - decoder.decode(trace).await; - if !verbose { - println!("{trace}"); - } else { - println!("{trace:#}"); - } - } - println!(); - - if result.success { - println!("{}", Paint::green("Transaction successfully executed.")); - } else { - println!("{}", Paint::red("Transaction failed.")); - } - - println!("Gas used: {}", result.gas_used); - Ok(()) -} - -struct RunResult { - pub success: bool, - pub traces: Traces, - pub debug: DebugArena, - pub gas_used: u64, -} diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index d24abfbf8c7ec..20ee2119d4e36 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -65,6 +65,7 @@ impl TracingExecutor { Self { executor } } + /// uses the fork block number from the config pub async fn get_fork_material( config: &Config, mut evm_opts: EvmOpts, From a3a72eeceb10e7bd7c31732478f2885ab5dd17d7 Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:23:43 -0400 Subject: [PATCH 21/37] Update cli/src/cmd/cast/call.rs Co-authored-by: evalir --- cli/src/cmd/cast/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index b0500f6c294a6..52d308c49c9b4 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -202,7 +202,7 @@ impl CallArgs { return Ok(()); } - // fill the builder after the comditional so we dont move values + // fill the builder after the conditional so we dont move values fill_create(&mut builder, value, code, sig, args).await?; } _ => { From c62ed43bdc290315368f46118df6c799c8706c3b Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:23:48 -0400 Subject: [PATCH 22/37] Update cli/src/cmd/utils.rs Co-authored-by: evalir --- cli/src/cmd/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index e632cf25dcf6d..ec52c07ed64ea 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -308,7 +308,7 @@ pub fn read_constructor_args_file(constructor_args_path: PathBuf) -> eyre::Resul Ok(args) } -/// A slimmed down return from the executor +/// A slimmed down return from the executor used for returning minimal trace + gas metering info pub struct TraceResult { pub success: bool, pub traces: Traces, From e1f441a1de1c063c0baffe0d890d3ad6aca8c7ce Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:23:54 -0400 Subject: [PATCH 23/37] Update cli/src/cmd/utils.rs Co-authored-by: evalir --- cli/src/cmd/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index ec52c07ed64ea..6c1e95291ffaa 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -316,7 +316,7 @@ pub struct TraceResult { pub gas_used: u64, } -/// labels the traces, conditonally prints them or opens the debugger +/// labels the traces, conditionally prints them or opens the debugger pub async fn handle_traces( mut result: TraceResult, config: &Config, From 312a7381268eef0ec291b891640657223cc5e932 Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:24:21 -0400 Subject: [PATCH 24/37] Update evm/src/trace/mod.rs Co-authored-by: evalir --- evm/src/trace/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index d24abfbf8c7ec..b7ba9db8ad498 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -38,7 +38,7 @@ mod decoder; pub mod node; pub mod utils; -/// a default executor with tracing enabled +/// A default executor with tracing enabled pub struct TracingExecutor { executor: Executor, } From d97ae30d0d877850d4a963bbff05887ec0554d40 Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:25:12 -0400 Subject: [PATCH 25/37] comment --- cli/src/cmd/cast/call.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 52d308c49c9b4..6ff993be4a2da 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -66,7 +66,6 @@ pub struct CallArgs { #[clap(long, requires = "trace")] labels: Vec, - /// Can only be used with "--trace" #[clap(long)] evm_version: Option, From 1b994f70e81d666c77db97ea9ae0dc7d6b90aaf4 Mon Sep 17 00:00:00 2001 From: N Date: Mon, 31 Jul 2023 19:29:44 -0400 Subject: [PATCH 26/37] unresolve convo --- cli/src/cmd/cast/call.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 6ff993be4a2da..52d308c49c9b4 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -66,6 +66,7 @@ pub struct CallArgs { #[clap(long, requires = "trace")] labels: Vec, + /// Can only be used with "--trace" #[clap(long)] evm_version: Option, From c544211f4bd9f899ed201901122452e325c6ee8d Mon Sep 17 00:00:00 2001 From: N Date: Tue, 1 Aug 2023 14:33:09 -0400 Subject: [PATCH 27/37] TraceResult::from --- cli/src/cmd/utils.rs | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index 051b646b06523..3ff9cdf014707 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -16,9 +16,10 @@ use foundry_common::{cli_warn, fs, TestFunctionExt}; use foundry_config::{error::ExtractConfigError, figment::Figment, Chain as ConfigChain, Config}; use foundry_evm::{ debug::DebugArena, + executor::{DeployResult, EvmError, ExecutionErr, RawCallResult}, trace::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, Traces, + CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, }; use std::{collections::BTreeMap, fmt::Write, path::PathBuf, str::FromStr}; @@ -316,6 +317,51 @@ pub struct TraceResult { pub gas_used: u64, } +impl From for TraceResult { + fn from(result: RawCallResult) -> Self { + let RawCallResult { gas_used, traces, reverted, debug, .. } = result; + + Self { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], + debug: debug.unwrap_or_default(), + gas_used, + } + } +} + +impl From for TraceResult { + fn from(result: DeployResult) -> Self { + let DeployResult { gas_used, traces, debug, .. } = result; + + Self { + success: true, + traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], + debug: debug.unwrap_or_default(), + gas_used, + } + } +} + +impl TryFrom for TraceResult { + type Error = EvmError; + + fn try_from(err: EvmError) -> Result { + match err { + EvmError::Execution(err) => { + let ExecutionErr { reverted, gas_used, traces, debug: run_debug, .. } = *err; + Ok(TraceResult { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], + debug: run_debug.unwrap_or_default(), + gas_used, + }) + } + _ => Err(err), + } + } +} + /// labels the traces, conditonally prints them or opens the debugger pub async fn handle_traces( mut result: TraceResult, From 6ab87700c0bf66a3ddc17d69e54a84443fa5ce67 Mon Sep 17 00:00:00 2001 From: N Date: Tue, 1 Aug 2023 14:37:51 -0400 Subject: [PATCH 28/37] clippy --fix --- cli/src/cmd/cast/call.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 52d308c49c9b4..24e248ebf192d 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -226,9 +226,9 @@ impl CallArgs { let trace = match executor.call_raw_committing( sender, - tx.to_addr().map(|a| a.clone()).expect("an address to be here"), - tx.data().map(|d| d.clone()).unwrap_or_default().to_vec().into(), - tx.value().map(|v| v.clone()).unwrap_or_default(), + tx.to_addr().copied().expect("an address to be here"), + tx.data().cloned().unwrap_or_default().to_vec().into(), + tx.value().copied().unwrap_or_default(), ) { Ok(RawCallResult { gas_used, traces, reverted, debug, .. }) => { TraceResult { From f3e84fa94d04b6d3885f798b2cf4f716be346208 Mon Sep 17 00:00:00 2001 From: N Date: Tue, 1 Aug 2023 19:09:18 -0400 Subject: [PATCH 29/37] use from/try_from --- cli/src/cmd/cast/call.rs | 54 ++++------------------------------------ cli/src/cmd/cast/run.rs | 37 +++------------------------ 2 files changed, 8 insertions(+), 83 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 24e248ebf192d..33d63b9114833 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -8,7 +8,7 @@ use cast::{Cast, TxBuilder}; use clap::Parser; use ethers::{ solc::EvmVersion, - types::{BlockId, NameOrAddress, U256}, + types::{BlockId, NameOrAddress, Trace, U256}, }; use eyre::WrapErr; use forge::executor::opts::EvmOpts; @@ -164,37 +164,8 @@ impl CallArgs { value.unwrap_or(U256::zero()), None, ) { - Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => { - TraceResult { - success: true, - traces: vec![( - TraceKind::Execution, - traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, - )], - debug: run_debug.unwrap_or_default(), - gas_used, - } - } - Err(EvmError::Execution(inner)) => { - let ExecutionErr { - reverted, gas_used, traces, debug: run_debug, .. - } = *inner; - TraceResult { - success: !reverted, - traces: vec![( - TraceKind::Execution, - traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, - )], - debug: run_debug.unwrap_or_default(), - gas_used, - } - } - Err(err) => { - eyre::bail!( - "unexpected error when running create transaction: {:?}", - err - ) - } + Ok(deploy_result) => TraceResult::from(deploy_result), + Err(evm_err) => TraceResult::try_from(evm_err)?, }; handle_traces(trace, &config, chain, labels, verbose, debug).await?; @@ -224,27 +195,12 @@ impl CallArgs { let (tx, _) = builder.build(); - let trace = match executor.call_raw_committing( + let trace = TraceResult::from(executor.call_raw_committing( sender, tx.to_addr().copied().expect("an address to be here"), tx.data().cloned().unwrap_or_default().to_vec().into(), tx.value().copied().unwrap_or_default(), - ) { - Ok(RawCallResult { gas_used, traces, reverted, debug, .. }) => { - TraceResult { - success: !reverted, - traces: vec![( - TraceKind::Execution, - traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, - )], - debug: debug.unwrap_or_default(), - gas_used, - } - } - Err(e) => { - eyre::bail!("unexpected error when running call transaction: {:?}", e) - } - }; + )?); handle_traces(trace, &config, chain, labels, verbose, debug).await?; diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index f6e4a996d9b3c..cae69d9480094 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -155,43 +155,12 @@ impl RunArgs { if let Some(to) = tx.to { trace!(tx=?tx.hash,to=?to, "executing call transaction"); - let RawCallResult { - reverted, - gas_used: gas, - traces, - debug: run_debug, - exit_reason: _, - .. - } = executor.commit_tx_with_env(env).unwrap(); - - TraceResult { - success: !reverted, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], - debug: run_debug.unwrap_or_default(), - gas_used: gas, - } + TraceResult::from(executor.commit_tx_with_env(env)?) } else { trace!(tx=?tx.hash, "executing create transaction"); match executor.deploy_with_env(env, None) { - Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => TraceResult { - success: true, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], - debug: run_debug.unwrap_or_default(), - gas_used, - }, - Err(EvmError::Execution(inner)) => { - let ExecutionErr { reverted, gas_used, traces, debug: run_debug, .. } = - *inner; - TraceResult { - success: !reverted, - traces: vec![(TraceKind::Execution, traces.unwrap_or_default())], - debug: run_debug.unwrap_or_default(), - gas_used, - } - } - Err(err) => { - eyre::bail!("unexpected error when running create transaction: {:?}", err) - } + Ok(res) => TraceResult::from(res), + Err(err) => TraceResult::try_from(err)?, } } }; From 770d0526312ceffce201a65c8b48c8385df1ef9a Mon Sep 17 00:00:00 2001 From: N Date: Tue, 1 Aug 2023 19:12:23 -0400 Subject: [PATCH 30/37] clippy --fix --- cli/src/cmd/cast/call.rs | 7 ++----- cli/src/cmd/cast/run.rs | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 33d63b9114833..4fbeef222dc8a 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -8,15 +8,12 @@ use cast::{Cast, TxBuilder}; use clap::Parser; use ethers::{ solc::EvmVersion, - types::{BlockId, NameOrAddress, Trace, U256}, + types::{BlockId, NameOrAddress, U256}, }; use eyre::WrapErr; use forge::executor::opts::EvmOpts; use foundry_config::{find_project_root_path, Config}; -use foundry_evm::{ - executor::{DeployResult, EvmError, ExecutionErr, RawCallResult}, - trace::{TraceKind, TracingExecutor}, -}; +use foundry_evm::trace::TracingExecutor; use std::str::FromStr; type Provider = diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index cae69d9480094..36387b6e47b81 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -4,16 +4,13 @@ use crate::{ opts::RpcOpts, update_progress, utils, }; -use cast::executor::{EvmError, ExecutionErr}; + use clap::Parser; use ethers::{prelude::Middleware, solc::EvmVersion, types::H160}; use eyre::WrapErr; use forge::{ - executor::{ - inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, DeployResult, RawCallResult, - }, + executor::{inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts}, revm::primitives::U256 as rU256, - trace::TraceKind, utils::h256_to_b256, }; use foundry_config::{find_project_root_path, Config}; From bd2464169b61b035c1360ebe2ac259afbb416bed Mon Sep 17 00:00:00 2001 From: N Date: Tue, 1 Aug 2023 19:25:07 -0400 Subject: [PATCH 31/37] clean up, no extra url in panic --- cli/src/cmd/cast/call.rs | 57 ++++------------------------------------ cli/src/cmd/utils.rs | 50 +++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 24e248ebf192d..4fbeef222dc8a 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -13,10 +13,7 @@ use ethers::{ use eyre::WrapErr; use forge::executor::opts::EvmOpts; use foundry_config::{find_project_root_path, Config}; -use foundry_evm::{ - executor::{DeployResult, EvmError, ExecutionErr, RawCallResult}, - trace::{TraceKind, TracingExecutor}, -}; +use foundry_evm::trace::TracingExecutor; use std::str::FromStr; type Provider = @@ -164,37 +161,8 @@ impl CallArgs { value.unwrap_or(U256::zero()), None, ) { - Ok(DeployResult { gas_used, traces, debug: run_debug, .. }) => { - TraceResult { - success: true, - traces: vec![( - TraceKind::Execution, - traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, - )], - debug: run_debug.unwrap_or_default(), - gas_used, - } - } - Err(EvmError::Execution(inner)) => { - let ExecutionErr { - reverted, gas_used, traces, debug: run_debug, .. - } = *inner; - TraceResult { - success: !reverted, - traces: vec![( - TraceKind::Execution, - traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, - )], - debug: run_debug.unwrap_or_default(), - gas_used, - } - } - Err(err) => { - eyre::bail!( - "unexpected error when running create transaction: {:?}", - err - ) - } + Ok(deploy_result) => TraceResult::from(deploy_result), + Err(evm_err) => TraceResult::try_from(evm_err)?, }; handle_traces(trace, &config, chain, labels, verbose, debug).await?; @@ -224,27 +192,12 @@ impl CallArgs { let (tx, _) = builder.build(); - let trace = match executor.call_raw_committing( + let trace = TraceResult::from(executor.call_raw_committing( sender, tx.to_addr().copied().expect("an address to be here"), tx.data().cloned().unwrap_or_default().to_vec().into(), tx.value().copied().unwrap_or_default(), - ) { - Ok(RawCallResult { gas_used, traces, reverted, debug, .. }) => { - TraceResult { - success: !reverted, - traces: vec![( - TraceKind::Execution, - traces.ok_or_else(|| eyre::eyre!("no traces recorded"))?, - )], - debug: debug.unwrap_or_default(), - gas_used, - } - } - Err(e) => { - eyre::bail!("unexpected error when running call transaction: {:?}", e) - } - }; + )?); handle_traces(trace, &config, chain, labels, verbose, debug).await?; diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index 6c1e95291ffaa..d6116d8e558a2 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -16,9 +16,10 @@ use foundry_common::{cli_warn, fs, TestFunctionExt}; use foundry_config::{error::ExtractConfigError, figment::Figment, Chain as ConfigChain, Config}; use foundry_evm::{ debug::DebugArena, + executor::{DeployResult, EvmError, ExecutionErr, RawCallResult}, trace::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, Traces, + CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, }; use std::{collections::BTreeMap, fmt::Write, path::PathBuf, str::FromStr}; @@ -316,6 +317,51 @@ pub struct TraceResult { pub gas_used: u64, } +impl From for TraceResult { + fn from(result: RawCallResult) -> Self { + let RawCallResult { gas_used, traces, reverted, debug, .. } = result; + + Self { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], + debug: debug.unwrap_or_default(), + gas_used, + } + } +} + +impl From for TraceResult { + fn from(result: DeployResult) -> Self { + let DeployResult { gas_used, traces, debug, .. } = result; + + Self { + success: true, + traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], + debug: debug.unwrap_or_default(), + gas_used, + } + } +} + +impl TryFrom for TraceResult { + type Error = EvmError; + + fn try_from(err: EvmError) -> Result { + match err { + EvmError::Execution(err) => { + let ExecutionErr { reverted, gas_used, traces, debug: run_debug, .. } = *err; + Ok(TraceResult { + success: !reverted, + traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], + debug: run_debug.unwrap_or_default(), + gas_used, + }) + } + _ => Err(err), + } + } +} + /// labels the traces, conditionally prints them or opens the debugger pub async fn handle_traces( mut result: TraceResult, @@ -367,7 +413,7 @@ pub async fn print_traces( verbose: bool, ) -> eyre::Result<()> { if result.traces.is_empty() { - eyre::bail!("Unexpected error: No traces. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + panic!("No traces found") } println!("Traces:"); From 2c9b5a6d79fd79f1cbd85337a453bbd7b9539ea7 Mon Sep 17 00:00:00 2001 From: N Date: Tue, 1 Aug 2023 19:30:29 -0400 Subject: [PATCH 32/37] formatting --- evm/src/trace/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 384529dc85aeb..cc388210d07a4 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -195,12 +195,12 @@ impl CallTraceArena { Instruction::OpCode(opc) => { match opc { // If yes, descend into a child trace - opcode::CREATE - | opcode::CREATE2 - | opcode::DELEGATECALL - | opcode::CALL - | opcode::STATICCALL - | opcode::CALLCODE => { + opcode::CREATE | + opcode::CREATE2 | + opcode::DELEGATECALL | + opcode::CALL | + opcode::STATICCALL | + opcode::CALLCODE => { self.add_to_geth_trace( storage, &self.arena[trace_node.children[child_id]], @@ -662,7 +662,7 @@ pub fn load_contracts( .iter() .filter_map(|(addr, name)| { if let Ok(Some((_, (abi, _)))) = contracts.find_by_name_or_identifier(name) { - return Some((*addr, (name.clone(), abi.clone()))); + return Some((*addr, (name.clone(), abi.clone()))) } None }) From 844544589c4a7f9efbdd50ac038491d18825fafc Mon Sep 17 00:00:00 2001 From: N Date: Wed, 2 Aug 2023 13:13:35 -0400 Subject: [PATCH 33/37] fix imports --- cli/src/cmd/cast/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index ca032db5d93e5..a25ac310b070b 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -14,7 +14,7 @@ use forge::{ utils::h256_to_b256, }; use foundry_config::{find_project_root_path, Config}; -use foundry_evm::trace::TracingExecutor; +use foundry_evm::{executor::EvmError, trace::TracingExecutor}; use tracing::trace; const ARBITRUM_SENDER: H160 = H160([ From 78130fdad0fd3962b4db4f06ed741530ad12633d Mon Sep 17 00:00:00 2001 From: N Date: Wed, 2 Aug 2023 13:18:07 -0400 Subject: [PATCH 34/37] fix clap rxequirements --- cli/src/cmd/cast/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index 4fbeef222dc8a..c72e8b05aea4a 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -64,7 +64,7 @@ pub struct CallArgs { labels: Vec, /// Can only be used with "--trace" - #[clap(long)] + #[clap(long, requires = "trace")] evm_version: Option, /// The block height to query at. From 2d6bd7434bc258083ab0094e2f3c120f3f8a0cbf Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 10 Aug 2023 11:16:41 -0400 Subject: [PATCH 35/37] Update cli/src/cmd/cast/call.rs --- cli/src/cmd/cast/call.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index c72e8b05aea4a..7f703a1257ed3 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -64,6 +64,8 @@ pub struct CallArgs { labels: Vec, /// Can only be used with "--trace" + /// + /// The EVM Version to use. #[clap(long, requires = "trace")] evm_version: Option, From cdbca4d241c7c96ff7da505abd2e2a555028cc74 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 10 Aug 2023 11:22:10 -0400 Subject: [PATCH 36/37] cargo fmt --- cli/src/cmd/cast/call.rs | 4 ++-- cli/src/cmd/cast/run.rs | 4 ++-- cli/src/cmd/utils.rs | 8 ++++---- evm/src/trace/mod.rs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/src/cmd/cast/call.rs b/cli/src/cmd/cast/call.rs index c72e8b05aea4a..748958bd5f30f 100644 --- a/cli/src/cmd/cast/call.rs +++ b/cli/src/cmd/cast/call.rs @@ -167,7 +167,7 @@ impl CallArgs { handle_traces(trace, &config, chain, labels, verbose, debug).await?; - return Ok(()); + return Ok(()) } // fill the builder after the conditional so we dont move values @@ -201,7 +201,7 @@ impl CallArgs { handle_traces(trace, &config, chain, labels, verbose, debug).await?; - return Ok(()); + return Ok(()) } } }; diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 567f835a7eea5..0722f01b281c2 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -139,10 +139,10 @@ impl RunArgs { // and gas limit 0 which causes reverts, so we skip it if tx.from == ARBITRUM_SENDER { update_progress!(pb, index); - continue; + continue } if tx.hash().eq(&tx_hash) { - break; + break } configure_tx_env(&mut env, &tx); diff --git a/cli/src/cmd/utils.rs b/cli/src/cmd/utils.rs index d6116d8e558a2..118c4abb6a196 100644 --- a/cli/src/cmd/utils.rs +++ b/cli/src/cmd/utils.rs @@ -103,7 +103,7 @@ pub fn get_cached_entry_by_name( } if let Some(entry) = cached_entry { - return Ok(entry); + return Ok(entry) } let mut err = format!("could not find artifact: `{name}`"); @@ -175,7 +175,7 @@ macro_rules! update_progress { /// True if the network calculates gas costs differently. pub fn has_different_gas_calc(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); + return matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) } false } @@ -183,7 +183,7 @@ pub fn has_different_gas_calc(chain: u64) -> bool { /// True if it supports broadcasting in batches. pub fn has_batch_support(chain: u64) -> bool { if let ConfigChain::Named(chain) = ConfigChain::from(chain) { - return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli); + return !matches!(chain, Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli) } true } @@ -380,7 +380,7 @@ pub async fn handle_traces( if let (Ok(address), Some(label)) = (ethers::types::Address::from_str(addr), iter.next()) { - return Some((address, label.to_string())); + return Some((address, label.to_string())) } } None diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index cc388210d07a4..172a2f178a726 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -150,7 +150,7 @@ impl CallTraceArena { .map(|node| { if node.trace.created() { if let RawOrDecodedReturnData::Raw(ref bytes) = node.trace.output { - return (&node.trace.address, Some(bytes.as_ref())); + return (&node.trace.address, Some(bytes.as_ref())) } } @@ -224,7 +224,7 @@ impl CallTraceArena { opts: GethDebugTracingOptions, ) -> DefaultFrame { if self.arena.is_empty() { - return Default::default(); + return Default::default() } let mut storage = HashMap::new(); From b78187aea0986657828b030cb2bf1a68bd1d4a75 Mon Sep 17 00:00:00 2001 From: N Date: Thu, 10 Aug 2023 11:39:02 -0400 Subject: [PATCH 37/37] move tracing executor to its own file --- evm/src/trace/executor.rs | 65 ++++++++++++++++++++++++++++++++++ evm/src/trace/mod.rs | 74 +++------------------------------------ 2 files changed, 69 insertions(+), 70 deletions(-) create mode 100644 evm/src/trace/executor.rs diff --git a/evm/src/trace/executor.rs b/evm/src/trace/executor.rs new file mode 100644 index 0000000000000..6d941aa4435ef --- /dev/null +++ b/evm/src/trace/executor.rs @@ -0,0 +1,65 @@ +use crate::{ + executor::{fork::CreateFork, opts::EvmOpts, Backend, Executor, ExecutorBuilder}, + utils::evm_spec, +}; +use ethers::solc::EvmVersion; +use foundry_config::Config; +use revm::primitives::Env; +use std::ops::{Deref, DerefMut}; + +/// A default executor with tracing enabled +pub struct TracingExecutor { + executor: Executor, +} + +impl TracingExecutor { + pub async fn new( + env: revm::primitives::Env, + fork: Option, + version: Option, + debug: bool, + ) -> Self { + let db = Backend::spawn(fork).await; + + // configures a bare version of the evm executor: no cheatcode inspector is enabled, + // tracing will be enabled only for the targeted transaction + let builder = ExecutorBuilder::default() + .with_config(env) + .with_spec(evm_spec(&version.unwrap_or_default())); + + let mut executor = builder.build(db); + + executor.set_tracing(true).set_debugger(debug); + + Self { executor } + } + + /// uses the fork block number from the config + pub async fn get_fork_material( + config: &Config, + mut evm_opts: EvmOpts, + ) -> eyre::Result<(Env, Option, Option)> { + evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + evm_opts.fork_block_number = config.fork_block_number; + + let env = evm_opts.evm_env().await?; + + let fork = evm_opts.get_fork(config, env.clone()); + + Ok((env, fork, evm_opts.get_remote_chain_id())) + } +} + +impl Deref for TracingExecutor { + type Target = Executor; + + fn deref(&self) -> &Self::Target { + &self.executor + } +} + +impl DerefMut for TracingExecutor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.executor + } +} diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 172a2f178a726..23b5d1fa47a69 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -1,31 +1,21 @@ use crate::{ - abi::CHEATCODE_ADDRESS, - debug::Instruction, - executor::{fork::CreateFork, opts::EvmOpts, Backend, Executor, ExecutorBuilder}, - trace::identifier::LocalTraceIdentifier, - utils::evm_spec, - CallKind, + abi::CHEATCODE_ADDRESS, debug::Instruction, trace::identifier::LocalTraceIdentifier, CallKind, }; pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; use ethers::{ abi::{ethereum_types::BigEndianHash, Address, RawLog}, core::utils::to_checksum, - solc::EvmVersion, types::{Bytes, DefaultFrame, GethDebugTracingOptions, StructLog, H256, U256}, }; +pub use executor::TracingExecutor; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; -use foundry_config::Config; use hashbrown::HashMap; use node::CallTraceNode; -use revm::{ - interpreter::{opcode, CallContext, InstructionResult, Memory, Stack}, - primitives::Env, -}; +use revm::interpreter::{opcode, CallContext, InstructionResult, Memory, Stack}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashSet}, fmt::{self, Write}, - ops::{Deref, DerefMut}, }; use yansi::{Color, Paint}; @@ -35,66 +25,10 @@ use yansi::{Color, Paint}; pub mod identifier; mod decoder; +mod executor; pub mod node; pub mod utils; -/// A default executor with tracing enabled -pub struct TracingExecutor { - executor: Executor, -} - -impl TracingExecutor { - pub async fn new( - env: revm::primitives::Env, - fork: Option, - version: Option, - debug: bool, - ) -> Self { - let db = Backend::spawn(fork).await; - - // configures a bare version of the evm executor: no cheatcode inspector is enabled, - // tracing will be enabled only for the targeted transaction - let builder = ExecutorBuilder::default() - .with_config(env) - .with_spec(evm_spec(&version.unwrap_or_default())); - - let mut executor = builder.build(db); - - executor.set_tracing(true).set_debugger(debug); - - Self { executor } - } - - /// uses the fork block number from the config - pub async fn get_fork_material( - config: &Config, - mut evm_opts: EvmOpts, - ) -> eyre::Result<(Env, Option, Option)> { - evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); - evm_opts.fork_block_number = config.fork_block_number; - - let env = evm_opts.evm_env().await?; - - let fork = evm_opts.get_fork(config, env.clone()); - - Ok((env, fork, evm_opts.get_remote_chain_id())) - } -} - -impl Deref for TracingExecutor { - type Target = Executor; - - fn deref(&self) -> &Self::Target { - &self.executor - } -} - -impl DerefMut for TracingExecutor { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.executor - } -} - pub type Traces = Vec<(TraceKind, CallTraceArena)>; /// An arena of [CallTraceNode]s