Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
ae45e30
initial start
Jul 26, 2023
636ff05
traces working
Jul 26, 2023
19c9c39
Update opts.rs
nhtyy Jul 26, 2023
c1cfb82
clean up
Jul 26, 2023
3589fc6
Merge branch 'master' of https://github.com/nhtyy/foundry
Jul 26, 2023
3a37650
comments/debugger
Jul 26, 2023
8e1fb61
comment
Jul 26, 2023
e797891
change build -> fill
Jul 27, 2023
eb63029
start of move to evm crate
Jul 27, 2023
1aaa59f
rename
Jul 27, 2023
ff90e6b
put fork setup on tracing executor
Jul 27, 2023
dee0d34
comment
Jul 27, 2023
2136cce
return err on no traces
Jul 27, 2023
5de5f05
moving trace handlers to cast::cmd::utils
Jul 28, 2023
ca5ec5d
rm formatting
Jul 28, 2023
e31c432
fmt
Jul 28, 2023
6dbf0cd
use a ref
Jul 28, 2023
005a5ca
fix unneeded borrow
Jul 28, 2023
ba5e0f2
Merge branch 'master' of https://github.com/foundry-rs/foundry
Jul 31, 2023
4ce14bd
unused import
Jul 31, 2023
72c5bab
panic instead of bail
Jul 31, 2023
5c26bdd
initial
Jul 31, 2023
a3a72ee
Update cli/src/cmd/cast/call.rs
nhtyy Jul 31, 2023
c62ed43
Update cli/src/cmd/utils.rs
nhtyy Jul 31, 2023
e1f441a
Update cli/src/cmd/utils.rs
nhtyy Jul 31, 2023
312a738
Update evm/src/trace/mod.rs
nhtyy Jul 31, 2023
d97ae30
comment
Jul 31, 2023
1b994f7
unresolve convo
Jul 31, 2023
c544211
TraceResult::from
Aug 1, 2023
6ab8770
clippy --fix
Aug 1, 2023
f49609d
merge upstream
Aug 1, 2023
f3e84fa
use from/try_from
Aug 1, 2023
770d052
clippy --fix
Aug 1, 2023
bd24641
clean up, no extra url in panic
Aug 1, 2023
5600a5e
Merge branch 'master' of https://github.com/nhtyy/foundry into n/run
Aug 1, 2023
2c9b5a6
formatting
Aug 1, 2023
866475d
Merge branch 'master' of https://github.com/foundry-rs/foundry
Aug 2, 2023
8445445
fix imports
Aug 2, 2023
78130fd
fix clap rxequirements
Aug 2, 2023
93ad848
pull upstream
Aug 7, 2023
2d6bd74
Update cli/src/cmd/cast/call.rs
Evalir Aug 10, 2023
cdbca4d
cargo fmt
Aug 10, 2023
9c8590c
Merge branch 'master' of https://github.com/nhtyy/foundry
Aug 10, 2023
b78187a
move tracing executor to its own file
Aug 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
229 changes: 208 additions & 21 deletions cli/src/cmd/cast/call.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
// 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::types::{BlockId, NameOrAddress, U256};
use ethers::{
solc::EvmVersion,
types::{BlockId, NameOrAddress, U256},
};
use eyre::WrapErr;
use foundry_config::Config;
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 std::str::FromStr;

type Provider =
ethers::providers::Provider<ethers::providers::RetryClient<ethers::providers::Http>>;

/// CLI arguments for `cast call`.
#[derive(Debug, Parser)]
pub struct CallArgs {
Expand All @@ -31,13 +43,39 @@ pub struct CallArgs {
)]
data: Option<String>,

/// 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,

/// Can only be used with "--trace"
/// Labels to apply to the traces.
///
/// Format: `address:label`
#[clap(long, requires = "trace")]
labels: Vec<String>,

/// Can only be used with "--trace"
Comment thread
Evalir marked this conversation as resolved.
#[clap(long)]
evm_version: Option<EvmVersion>,
Comment thread
nhtyy marked this conversation as resolved.

/// The block height to query at.
///
/// Can also be the tags earliest, finalized, safe, latest, or pending.
#[clap(long, short)]
block: Option<BlockId>,

/// Simulate a contract deployment.
#[clap(subcommand)]
command: Option<CallSubcommands>,

Expand All @@ -50,6 +88,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.
Expand All @@ -73,54 +112,202 @@ 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,
debug,
verbose,
labels,
} = self;

let config = Config::from(&eth);
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)))
.gas_price(tx.gas_price)
.priority_gas_price(tx.priority_gas_price)
.nonce(tx.nonce);

match command {
Some(CallSubcommands::Create { code, sig, args, value }) => {
builder.value(value);
if trace {
let figment = Config::figment_with_root(find_project_root_path(None).unwrap())
.merge(eth.rpc);

let evm_opts = figment.extract::<EvmOpts>()?;

let (env, fork, chain) =
TracingExecutor::get_fork_material(&config, evm_opts).await?;

let mut data = hex::decode(code.strip_prefix("0x").unwrap_or(&code))?;
let mut executor =
foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug)
.await;

if let Some(s) = sig {
let (mut sigdata, _func) = builder.create_args(&s, args).await?;
data.append(&mut sigdata);
let 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.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
)
}
};

handle_traces(trace, &config, chain, labels, verbose, debug).await?;

return Ok(());
}

builder.set_data(data);
// fill the builder after the comditional so we dont move values
Comment thread
nhtyy marked this conversation as resolved.
Outdated
fill_create(&mut builder, value, code, sig, args).await?;
}
_ => {
builder.value(tx.value);
// fill first here because we need to use the builder in the conditional
fill_tx(&mut builder, tx.value, sig, args, data).await?;

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")?,
);
if trace {
let figment = Config::figment_with_root(find_project_root_path(None).unwrap())
.merge(eth.rpc);

let evm_opts = figment.extract::<EvmOpts>()?;

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)
.await;

let (tx, _) = builder.build();

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(),
) {
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?;

return Ok(());
}
}
};

let builder_output = builder.build();
let builder_output: (
ethers::types::transaction::eip2718::TypedTransaction,
Option<ethers::abi::Function>,
) = builder.build();

println!("{}", Cast::new(provider).call(builder_output, block).await?);

Ok(())
}
}

/// fills the builder from create arg
async fn fill_create(
builder: &mut TxBuilder<'_, Provider>,
value: Option<U256>,
code: String,
sig: Option<String>,
args: Vec<String>,
) -> 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(())
}

/// fills the builder from args
async fn fill_tx(
builder: &mut TxBuilder<'_, Provider>,
value: Option<U256>,
sig: Option<String>,
args: Vec<String>,
data: Option<String>,
) -> 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(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading