From e888a52c5a0d70860ad1ec5d14e5b2696e9d4737 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Tue, 22 Mar 2022 12:17:01 -0700 Subject: [PATCH 1/5] :gear: cast run subcommand --- cli/src/cast.rs | 1 + cli/src/cmd/cast/mod.rs | 1 + cli/src/cmd/cast/run.rs | 155 ++++++++++++++++++++++++++++++++++++++++ cli/src/opts/cast.rs | 4 +- 4 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 cli/src/cmd/cast/run.rs diff --git a/cli/src/cast.rs b/cli/src/cast.rs index 5c8c82211e6c0..914e05d6e10c1 100644 --- a/cli/src/cast.rs +++ b/cli/src/cast.rs @@ -322,6 +322,7 @@ async fn main() -> eyre::Result<()> { eyre::bail!("No wallet or sender address provided.") } } + Subcommands::Run(cmd) => cmd.run()?, Subcommands::PublishTx { eth, raw_tx, cast_async } => { let provider = Provider::try_from(eth.rpc_url()?)?; let cast = Cast::new(&provider); diff --git a/cli/src/cmd/cast/mod.rs b/cli/src/cmd/cast/mod.rs index 3799246cfedee..6de643a2c6e0b 100644 --- a/cli/src/cmd/cast/mod.rs +++ b/cli/src/cmd/cast/mod.rs @@ -6,3 +6,4 @@ //! [`foundry_config::Config`]. pub mod find_block; +pub mod run; \ No newline at end of file diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs new file mode 100644 index 0000000000000..7455e76422d4c --- /dev/null +++ b/cli/src/cmd/cast/run.rs @@ -0,0 +1,155 @@ +use std::collections::BTreeMap; + +use crate::{ + utils, + cmd::{forge::build::BuildArgs, Cmd}, + opts::evm::EvmArgs +}; + +use clap::Parser; +use ansi_term::Colour; +use ethers::{ + abi::{Abi, RawLog}, + types::{Address, Bytes, U256} +}; + +use forge::{ + debug::DebugArena, + decode::decode_console_logs, + executor::{ + opts::EvmOpts, CallResult, DatabaseRef, DeployResult, EvmError, Executor, ExecutorBuilder, + RawCallResult, + }, + trace::{identifier::LocalTraceIdentifier, CallTraceArena, CallTraceDecoder, TraceKind}, + CALLER, +}; +use foundry_utils::{IntoFunction, encode_args}; +use foundry_config::{figment::Figment, Config}; + + +// Loads project's figment and merges the build cli arguments into it +foundry_config::impl_figment_convert!(RunArgs, opts, evm_opts); + +#[derive(Debug, Clone, Parser)] +pub struct RunArgs { + #[clap(help = "the bytecode to execute")] + pub bytecode: String, + + // Optional Calldata + #[clap(help = "the calldata to pass to the contract")] + pub calldata: Option, + + /// Open the script in the debugger. + #[clap(long)] + pub debug: bool, + + #[clap(flatten)] + opts: BuildArgs, + + #[clap(flatten)] + pub evm_opts: EvmArgs, +} + +impl Cmd for RunArgs { + type Output = (); + + fn run(self) -> eyre::Result { + // Parse bytecode string + let bytecode_vec = self.bytecode.strip_prefix("0x").unwrap_or(&self.bytecode); + let parsed_bytecode = Bytes::from(hex::decode(bytecode_vec)?); + + println!("Got bytecode: {:?}", parsed_bytecode.to_vec()); + + // Load figment + let figment: Figment = From::from(&self); + let evm_opts = figment.extract::()?; + let verbosity = 5; // evm_opts.verbosity; + let config = Config::from_provider(figment).sanitized(); + + // Create executor + let mut builder = ExecutorBuilder::new() + .with_cheatcodes(evm_opts.ffi) + .with_config(evm_opts.evm_env()) + .with_spec(crate::utils::evm_spec(&config.evm_version)) + .with_fork(utils::get_fork(&evm_opts, &config.rpc_storage_caching)); + if verbosity >= 3 { + builder = builder.with_tracing(); + } + if self.debug { + builder = builder.with_tracing().with_debugger(); + } + + // Parse Calldata + let calldata: Bytes = if let Some(calldata) = self.calldata.unwrap_or("0x".to_string()).strip_prefix("0x") { + hex::decode(calldata)?.into() + } else { + let args: Vec = vec![]; + encode_args(&IntoFunction::into("".to_string()), &args)?.into() + }; + println!("Calldata: {:?}", calldata.to_vec()); + + // Create the runner + let mut runner = + Runner::new(builder.build(), evm_opts.initial_balance, evm_opts.sender); + + // Deploy the bytecode + let DeployResult { + address, + gas, + .. + } = runner.setup(parsed_bytecode)?; + + println!("Deployed contract at: {:?}", address); + + // Run the bytecode at the deployed address + let rcr = runner.run( + address, + calldata, + )?; + + println!("Raw Call Result: {:?}", rcr); + + // TODO: Waterfall debug + + if rcr.reverted { + println!("{}", Colour::Red.paint("x FAILURE")); + } else { + println!("{}", Colour::Green.paint("✔ SUCCESS")); + } + + println!("Gas used: {}", rcr.gas); + + Ok(()) + } +} + +struct Runner { + pub executor: Executor, + pub initial_balance: U256, + pub sender: Address, +} + +impl Runner { + pub fn new(executor: Executor, initial_balance: U256, sender: Address) -> Self { + Self { executor, initial_balance, sender } + } + + pub fn setup( + &mut self, + code: Bytes, + ) -> eyre::Result { + // We max out their balance so that they can deploy and make calls. + self.executor.set_balance(self.sender, U256::MAX); + self.executor.set_balance(*CALLER, U256::MAX); + + // We set the nonce of the deployer accounts to 1 to get the same addresses as DappTools + self.executor.set_nonce(self.sender, 1); + + // Deploy an instance of the contract + Ok(self.executor.deploy(self.sender, code.0, 0u32.into()).expect("couldn't deploy")) + } + + pub fn run(&mut self, address: Address, calldata: Bytes) -> eyre::Result { + Ok(self.executor.call_raw(self.sender, address, calldata.0, 0_u64.into())?) + } +} diff --git a/cli/src/opts/cast.rs b/cli/src/opts/cast.rs index e61805889b9b5..2e79de584f5df 100644 --- a/cli/src/opts/cast.rs +++ b/cli/src/opts/cast.rs @@ -7,7 +7,7 @@ use ethers::{ }; use super::{ClapChain, EthereumOpts, Wallet}; -use crate::{cmd::cast::find_block::FindBlockArgs, utils::parse_u256}; +use crate::{cmd::cast::{find_block::FindBlockArgs, run::RunArgs}, utils::parse_u256}; #[derive(Debug, Subcommand)] #[clap(about = "Perform Ethereum RPC calls from the comfort of your command line.")] @@ -255,6 +255,8 @@ pub enum Subcommands { #[clap(long = "json", short = 'j')] to_json: bool, }, + #[clap(alias = "r", about = "Executes arbitrary bytecode in hex format")] + Run(RunArgs), #[clap(name = "publish")] #[clap(about = "Publish a raw transaction to the network")] PublishTx { From d2800e54c7fa2e416b3581d20034b291a5529c8a Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Tue, 22 Mar 2022 14:31:35 -0700 Subject: [PATCH 2/5] :art: finish up cast run --- cli/README.md | 28 ++++++++ cli/src/cmd/cast/mod.rs | 2 +- cli/src/cmd/cast/run.rs | 131 +++++++++++++++++++---------------- cli/src/opts/cast.rs | 5 +- cli/test-utils/src/macros.rs | 14 ++++ cli/test-utils/src/util.rs | 32 +++++++++ cli/tests/cast.rs | 38 ++++++++++ 7 files changed, 187 insertions(+), 63 deletions(-) create mode 100644 cli/tests/cast.rs diff --git a/cli/README.md b/cli/README.md index 9cf0ac773f504..445de380b422c 100644 --- a/cli/README.md +++ b/cli/README.md @@ -494,8 +494,36 @@ SUBCOMMANDS: namehash returns ENS namehash of provided name nonce Prints the number of transactions sent from
resolve-name Returns the address the provided ENS name resolves to + run Executes arbitrary bytecode in hex format send Publish a transaction signed by to call with storage Show the raw value of a contract's storage slot tx Show information about the transaction wallet Set of wallet management utilities ``` + + +### Run + +Cast's `run` subcommand executes arbitrary bytecode provided in hex format. + +To use the `run` command, run `cast run [CALLDATA]`. +Where `` is the bytcode in hex format (eg. `0x604260005260206000F3`) and `` is the calldata in hex format (eg. `0x70a08231000000000000000000000000b4c79dab8f259c7aee6e5b2aa729821864227e84`). + +To view extensive documentation of `cast run`, run `cast run --help`: +``` +cast-run +Executes arbitrary bytecode in hex format + +USAGE: + cast run [OPTIONS] [--] [CALLDATA] + +ARGS: + + the bytecode to execute + + + the calldata to pass to the contract + +OPTIONS: +... +``` diff --git a/cli/src/cmd/cast/mod.rs b/cli/src/cmd/cast/mod.rs index 6de643a2c6e0b..aab341215920f 100644 --- a/cli/src/cmd/cast/mod.rs +++ b/cli/src/cmd/cast/mod.rs @@ -6,4 +6,4 @@ //! [`foundry_config::Config`]. pub mod find_block; -pub mod run; \ No newline at end of file +pub mod run; diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 7455e76422d4c..0e902837e9bf5 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -1,31 +1,23 @@ -use std::collections::BTreeMap; - use crate::{ - utils, - cmd::{forge::build::BuildArgs, Cmd}, - opts::evm::EvmArgs + cmd::{forge::build::BuildArgs, Cmd}, + opts::evm::EvmArgs, + utils, }; -use clap::Parser; use ansi_term::Colour; -use ethers::{ - abi::{Abi, RawLog}, - types::{Address, Bytes, U256} -}; +use clap::Parser; +use ethers::types::{Address, Bytes, U256}; use forge::{ - debug::DebugArena, - decode::decode_console_logs, executor::{ - opts::EvmOpts, CallResult, DatabaseRef, DeployResult, EvmError, Executor, ExecutorBuilder, - RawCallResult, + opts::EvmOpts, DatabaseRef, DeployResult, Executor, ExecutorBuilder, RawCallResult, }, - trace::{identifier::LocalTraceIdentifier, CallTraceArena, CallTraceDecoder, TraceKind}, + trace::TraceKind, CALLER, }; -use foundry_utils::{IntoFunction, encode_args}; use foundry_config::{figment::Figment, Config}; - +use foundry_utils::{encode_args, IntoFunction}; +use hex::ToHex; // Loads project's figment and merges the build cli arguments into it foundry_config::impl_figment_convert!(RunArgs, opts, evm_opts); @@ -39,8 +31,8 @@ pub struct RunArgs { #[clap(help = "the calldata to pass to the contract")] pub calldata: Option, - /// Open the script in the debugger. - #[clap(long)] + /// Open the bytecode execution in debug mode + #[clap(long, help="debug the bytecode execution")] pub debug: bool, #[clap(flatten)] @@ -54,18 +46,25 @@ impl Cmd for RunArgs { type Output = (); fn run(self) -> eyre::Result { - // Parse bytecode string - let bytecode_vec = self.bytecode.strip_prefix("0x").unwrap_or(&self.bytecode); - let parsed_bytecode = Bytes::from(hex::decode(bytecode_vec)?); - - println!("Got bytecode: {:?}", parsed_bytecode.to_vec()); - // Load figment let figment: Figment = From::from(&self); let evm_opts = figment.extract::()?; - let verbosity = 5; // evm_opts.verbosity; + let verbosity = evm_opts.verbosity; let config = Config::from_provider(figment).sanitized(); + // Parse bytecode string + let bytecode_vec = self.bytecode.strip_prefix("0x").unwrap_or(&self.bytecode); + let parsed_bytecode = Bytes::from(hex::decode(bytecode_vec)?); + + // Parse Calldata + let calldata: Bytes = + if let Some(calldata) = self.calldata.unwrap_or("0x".to_string()).strip_prefix("0x") { + hex::decode(calldata)?.into() + } else { + let args: Vec = vec![]; + encode_args(&IntoFunction::into("".to_string()), &args)?.into() + }; + // Create executor let mut builder = ExecutorBuilder::new() .with_cheatcodes(evm_opts.ffi) @@ -79,65 +78,75 @@ impl Cmd for RunArgs { builder = builder.with_tracing().with_debugger(); } - // Parse Calldata - let calldata: Bytes = if let Some(calldata) = self.calldata.unwrap_or("0x".to_string()).strip_prefix("0x") { - hex::decode(calldata)?.into() - } else { - let args: Vec = vec![]; - encode_args(&IntoFunction::into("".to_string()), &args)?.into() - }; - println!("Calldata: {:?}", calldata.to_vec()); - // Create the runner - let mut runner = - Runner::new(builder.build(), evm_opts.initial_balance, evm_opts.sender); + let mut runner = Runner::new(builder.build(), evm_opts.sender); // Deploy the bytecode - let DeployResult { - address, - gas, - .. - } = runner.setup(parsed_bytecode)?; - - println!("Deployed contract at: {:?}", address); + let DeployResult { address, .. } = runner.setup(parsed_bytecode)?; // Run the bytecode at the deployed address - let rcr = runner.run( - address, - calldata, - )?; - - println!("Raw Call Result: {:?}", rcr); + let rcr = runner.run(address, calldata)?; // TODO: Waterfall debug + // Ex: https://twitter.com/danielvf/status/1503756428212936710 + + // Unwrap Traces + let mut traces = + rcr.traces.map(|traces| vec![(TraceKind::Execution, traces)]).unwrap_or_default(); + + if verbosity >= 3 { + if traces.is_empty() { + eyre::bail!("Unexpected error: No traces despite verbosity level. Please report this as a bug: https://github.com/gakonst/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + } + + if rcr.reverted { + println!("Traces:"); + for (kind, trace) in &mut traces { + let should_include = match kind { + TraceKind::Setup => (verbosity >= 5) || (verbosity == 4), + TraceKind::Execution => verbosity > 3, + _ => false, + }; + + if should_include { + // TODO: Create decoder using local fork + // decoder.decode(trace); + println!("{}", trace); + } + } + println!(); + } + } if rcr.reverted { - println!("{}", Colour::Red.paint("x FAILURE")); + println!("{}", Colour::Red.paint("[REVERT]")); + println!("Gas consumed: {}", rcr.gas); } else { - println!("{}", Colour::Green.paint("✔ SUCCESS")); + println!("{}", Colour::Green.paint("[SUCCESS]")); + let o = rcr.result.encode_hex::(); + if o.len() > 0 { + println!("Output: {}", o); + } else { + println!("{}", Colour::Yellow.paint("No Output")); + } + println!("Gas consumed: {}", rcr.gas); } - println!("Gas used: {}", rcr.gas); - Ok(()) } } struct Runner { pub executor: Executor, - pub initial_balance: U256, pub sender: Address, } impl Runner { - pub fn new(executor: Executor, initial_balance: U256, sender: Address) -> Self { - Self { executor, initial_balance, sender } + pub fn new(executor: Executor, sender: Address) -> Self { + Self { executor, sender } } - pub fn setup( - &mut self, - code: Bytes, - ) -> eyre::Result { + pub fn setup(&mut self, code: Bytes) -> eyre::Result { // We max out their balance so that they can deploy and make calls. self.executor.set_balance(self.sender, U256::MAX); self.executor.set_balance(*CALLER, U256::MAX); diff --git a/cli/src/opts/cast.rs b/cli/src/opts/cast.rs index 2e79de584f5df..cc22d9aa3a082 100644 --- a/cli/src/opts/cast.rs +++ b/cli/src/opts/cast.rs @@ -7,7 +7,10 @@ use ethers::{ }; use super::{ClapChain, EthereumOpts, Wallet}; -use crate::{cmd::cast::{find_block::FindBlockArgs, run::RunArgs}, utils::parse_u256}; +use crate::{ + cmd::cast::{find_block::FindBlockArgs, run::RunArgs}, + utils::parse_u256, +}; #[derive(Debug, Subcommand)] #[clap(about = "Perform Ethereum RPC calls from the comfort of your command line.")] diff --git a/cli/test-utils/src/macros.rs b/cli/test-utils/src/macros.rs index 5a2af7ff87219..eaddab3b7ef0f 100644 --- a/cli/test-utils/src/macros.rs +++ b/cli/test-utils/src/macros.rs @@ -45,6 +45,20 @@ macro_rules! forgetest { }; } +#[macro_export] +macro_rules! casttest { + ($test:ident, $fun:expr) => { + $crate::casttest!($test, $crate::ethers_solc::PathStyle::Dapptools, $fun); + }; + ($test:ident, $style:expr, $fun:expr) => { + #[test] + fn $test() { + let (prj, cmd) = $crate::util::setup_cast(stringify!($test), $style); + $fun(prj, cmd); + } + }; +} + /// A helper macro to ignore `forgetest!` that should not run on CI #[macro_export] macro_rules! forgetest_ignore { diff --git a/cli/test-utils/src/util.rs b/cli/test-utils/src/util.rs index ae79b21e2d392..0dd79c1993a66 100644 --- a/cli/test-utils/src/util.rs +++ b/cli/test-utils/src/util.rs @@ -70,6 +70,15 @@ pub fn setup_project(test: TestProject) -> (TestProject, TestCommand) { (test, cmd) } +pub fn setup_cast(name: &str, style: PathStyle) -> (TestProject, TestCommand) { + setup_cast_project(TestProject::new(name, style)) +} + +pub fn setup_cast_project(test: TestProject) -> (TestProject, TestCommand) { + let cmd = test.ccommand(); + (test, cmd) +} + /// `TestProject` represents a temporary project to run tests against. /// /// Test projects are created from a global atomic counter to avoid duplicates. @@ -197,12 +206,31 @@ impl TestProject { } } + /// Creates a new command that is set to use the cast executable for this project + pub fn ccommand(&self) -> TestCommand { + let mut cmd = self.cbin(); + cmd.current_dir(&self.inner.root()); + TestCommand { + project: self.clone(), + cmd, + saved_env_vars: HashMap::new(), + current_dir_lock: None, + saved_cwd: pretty_err(".", std::env::current_dir()), + } + } + /// Returns the path to the forge executable. pub fn bin(&self) -> process::Command { let forge = self.root.join(format!("../forge{}", env::consts::EXE_SUFFIX)); process::Command::new(forge) } + /// Returns the path to the cast executable. + pub fn cbin(&self) -> process::Command { + let cast = self.root.join(format!("../cast{}", env::consts::EXE_SUFFIX)); + process::Command::new(cast) + } + /// Returns the `Config` as spit out by `forge config` pub fn config_from_output(&self, args: I) -> Config where @@ -288,6 +316,10 @@ impl TestCommand { self.set_cmd(self.project.bin()) } + pub fn cfuse(&mut self) -> &mut TestCommand { + self.set_cmd(self.project.cbin()) + } + /// Sets the current working directory pub fn set_current_dir(&mut self, p: impl AsRef) { drop(self.current_dir_lock.take()); diff --git a/cli/tests/cast.rs b/cli/tests/cast.rs new file mode 100644 index 0000000000000..33093aec31a16 --- /dev/null +++ b/cli/tests/cast.rs @@ -0,0 +1,38 @@ +//! Contains various tests for checking cast commands +use foundry_cli_test_utils::{ + casttest, + util::{TestCommand, TestProject}, +}; + +// tests that the `cast run` command works correctly +casttest!(run_bytecode, |_: TestProject, mut cmd: TestCommand| { + // Create executable bytecode + let raw_bytecode = "0x604260005260206000F3".to_string(); + let raw_calldata = + "0x70a08231000000000000000000000000b4c79dab8f259c7aee6e5b2aa729821864227e84".to_string(); + + // Call `cast run` + cmd.arg("run").arg(raw_bytecode).arg(raw_calldata); + let output = cmd.stdout_lossy(); + + // Expect successful bytecode execution + assert!(output.contains("[SUCCESS]")); + assert!(output.contains("Gas consumed:")); + assert!(output.contains("No Output")); + + // Create reverting bytecode + let reverting_bytecode = "0x610101610102016000526001601ff3".to_string(); + let revert_calldata = + "0x70a08231000000000000000000000000b4c79dab8f259c7aee6e5b2aa729821864227e84".to_string(); + + // Call `cast run` + cmd.cfuse().arg("run").arg(reverting_bytecode).arg(revert_calldata); + let revert_output = cmd.stdout_lossy(); + + // Expect bytecode execution to revert + assert!(revert_output.contains("[REVERT]")); + assert!(revert_output.contains("Gas consumed:")); + + // On revert, we don't have an output + assert!(!revert_output.contains("Output")); +}); \ No newline at end of file From 415f692949950e03204f28ea7bc7546523a393d5 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Tue, 22 Mar 2022 14:40:51 -0700 Subject: [PATCH 3/5] :test_tube: cast run tests and cleaning --- cli/src/cmd/cast/run.rs | 2 +- cli/tests/cast.rs | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 0e902837e9bf5..049656f451135 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -32,7 +32,7 @@ pub struct RunArgs { pub calldata: Option, /// Open the bytecode execution in debug mode - #[clap(long, help="debug the bytecode execution")] + #[clap(long, help = "debug the bytecode execution")] pub debug: bool, #[clap(flatten)] diff --git a/cli/tests/cast.rs b/cli/tests/cast.rs index 33093aec31a16..b286a6567b7be 100644 --- a/cli/tests/cast.rs +++ b/cli/tests/cast.rs @@ -35,4 +35,33 @@ casttest!(run_bytecode, |_: TestProject, mut cmd: TestCommand| { // On revert, we don't have an output assert!(!revert_output.contains("Output")); -}); \ No newline at end of file +}); + +// tests that `cast run` works without calldata +casttest!(run_bytecode_absent_calldata, |_: TestProject, mut cmd: TestCommand| { + // Create executable bytecode + let raw_bytecode = "0x604260005260206000F3".to_string(); + + // Call `cast run` + cmd.arg("run").arg(raw_bytecode); + let output = cmd.stdout_lossy(); + + // Expect successful bytecode execution + assert!(output.contains("[SUCCESS]")); + assert!(output.contains("Gas consumed:")); + assert!(output.contains("No Output")); + + // Create reverting bytecode + let reverting_bytecode = "0x610101610102016000526001601ff3".to_string(); + + // Call `cast run` + cmd.cfuse().arg("run").arg(reverting_bytecode); + let revert_output = cmd.stdout_lossy(); + + // Expect bytecode execution to revert + assert!(revert_output.contains("[REVERT]")); + assert!(revert_output.contains("Gas consumed:")); + + // On revert, we don't have an output + assert!(!revert_output.contains("Output")); +}); From 8e0a8811435b525af765c05043a1f7c2c49dd9f6 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Tue, 22 Mar 2022 15:29:33 -0700 Subject: [PATCH 4/5] :construction_worker: cast run clippy fixes --- cli/src/cmd/cast/run.rs | 19 ++++++++++--------- cli/src/opts/cast.rs | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 049656f451135..02a0041e9f246 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -57,13 +57,14 @@ impl Cmd for RunArgs { let parsed_bytecode = Bytes::from(hex::decode(bytecode_vec)?); // Parse Calldata - let calldata: Bytes = - if let Some(calldata) = self.calldata.unwrap_or("0x".to_string()).strip_prefix("0x") { - hex::decode(calldata)?.into() - } else { - let args: Vec = vec![]; - encode_args(&IntoFunction::into("".to_string()), &args)?.into() - }; + let calldata: Bytes = if let Some(calldata) = + self.calldata.unwrap_or_else(|| "0x".to_string()).strip_prefix("0x") + { + hex::decode(calldata)?.into() + } else { + let args: Vec = vec![]; + encode_args(&IntoFunction::into("".to_string()), &args)?.into() + }; // Create executor let mut builder = ExecutorBuilder::new() @@ -124,7 +125,7 @@ impl Cmd for RunArgs { } else { println!("{}", Colour::Green.paint("[SUCCESS]")); let o = rcr.result.encode_hex::(); - if o.len() > 0 { + if !o.is_empty() { println!("Output: {}", o); } else { println!("{}", Colour::Yellow.paint("No Output")); @@ -159,6 +160,6 @@ impl Runner { } pub fn run(&mut self, address: Address, calldata: Bytes) -> eyre::Result { - Ok(self.executor.call_raw(self.sender, address, calldata.0, 0_u64.into())?) + self.executor.call_raw(self.sender, address, calldata.0, 0_u64.into()) } } diff --git a/cli/src/opts/cast.rs b/cli/src/opts/cast.rs index cc22d9aa3a082..d283c411aba31 100644 --- a/cli/src/opts/cast.rs +++ b/cli/src/opts/cast.rs @@ -259,7 +259,7 @@ pub enum Subcommands { to_json: bool, }, #[clap(alias = "r", about = "Executes arbitrary bytecode in hex format")] - Run(RunArgs), + Run(Box), #[clap(name = "publish")] #[clap(about = "Publish a raw transaction to the network")] PublishTx { From e5e963da9b2326f686df04ea38d27e1a0b4f57a7 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Tue, 22 Mar 2022 15:35:52 -0700 Subject: [PATCH 5/5] :gear: cast run fixes --- cli/src/cmd/cast/run.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 02a0041e9f246..d17aacfa9bbb8 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -53,8 +53,7 @@ impl Cmd for RunArgs { let config = Config::from_provider(figment).sanitized(); // Parse bytecode string - let bytecode_vec = self.bytecode.strip_prefix("0x").unwrap_or(&self.bytecode); - let parsed_bytecode = Bytes::from(hex::decode(bytecode_vec)?); + let parsed_bytecode = self.bytecode.parse::()?; // Parse Calldata let calldata: Bytes = if let Some(calldata) =