-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(cast): simulate published transaction locally #1358
Conversation
cli/src/cmd/forge/sim.rs
Outdated
#[clap(long)] | ||
pub rpc: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redundant, but how can i make the one from evm_opts
mandatory?
This is awesome, I was excited for this functionality! Though I think this should be part of |
I thought about including it on use forge::{
debug::DebugArena,
decode::decode_console_logs,
executor::{builder::Backend, opts::EvmOpts, DeployResult, ExecutorBuilder, RawCallResult},
trace::{identifier::EtherscanIdentifier, CallTraceArena, CallTraceDecoderBuilder, TraceKind},
}; The dependencies are mostly in the |
All of those are re-exports of the evm crate |
Should we call it Also @joshieDo no pressure at all to implement all of the below in this PR, but figured this is a good spot to document the list of features I liked from
|
+1 on "cast run" and using the foundry_evm exports |
Moved it and simplified some stuff Thanks @mds1 , i like those suggestions
default behaviour has tracing, so I didn't include the flag. What does
Added the
I went for the format -- All the other suggestions, should probably done in other issues, since it touches on how traces are displayed, if I'm not mistaken. |
Example on a block with 17 transactions, but with a lot of storage accesses I guess Not cached: time cargo run --bin cast -- run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/cast run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC
Executing previous transactions from the block.
Traces:
[0] 0x165c…5e14::fallback{value: 750000000000000000000}()
└─ ← ()
Script ran successfully.
Gas used: 21000
cargo run --bin cast -- run --rpc-url 2.61s user 0.25s system 4% cpu 1:01.39 total Cached: time cargo run --bin cast -- run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC
Finished dev [unoptimized + debuginfo] target(s) in 0.37s
Running `target/debug/cast run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC
Executing previous transactions from the block.
Traces:
[0] 0x165c…5e14::fallback{value: 750000000000000000000}()
└─ ← ()
Script ran successfully.
Gas used: 21000
cargo run --bin cast -- run --rpc-url 1.24s user 0.13s system 40% cpu 3.399 total Not cached + time cargo run --bin cast -- run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC --label 0x165CD37b4C644C2921454429E7F9358d18A45e14:ukraine --quick
Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/cast run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC --label '0x165CD37b4C644C2921454429E7F9358d18A45e14:ukraine' --quick`
Traces:
[0] ukraine::fallback{value: 750000000000000000000}()
└─ ← ()
Script ran successfully.
Gas used: 21000
cargo run --bin cast -- run --rpc-url --label --quick 0.68s user 0.13s system 11% cpu 7.245 total cached + time cargo run --bin cast -- run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC --label 0x165CD37b4C644C2921454429E7F9358d18A45e14:ukraine --quick
Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/cast run 0xa8faf11f4e4c0a93fe9df333cc18d15e854d94b9f19d2237899213cf73c4699a --rpc-url $RPC --label '0x165CD37b4C644C2921454429E7F9358d18A45e14:ukraine' --quick`
Traces:
[0] ukraine::fallback{value: 750000000000000000000}()
└─ ← ()
Script ran successfully.
Gas used: 21000
cargo run --bin cast -- run --rpc-url --label --quick 0.52s user 0.11s system 24% cpu 2.562 total |
Defaulting to trace seems good to me! seth's default is to run the tx and then print any return data. Here was my test to confirm I'm remembering correctly:
That return data decodes to So to confirm I took a random ordinary DAI transfer, which returns a bool, and you can see it returns true (encoded):
All of this sounds great, I'll pull that set of features into a separate issue
Looks great 😍 Lmk when you think this PR is in a good state (maybe once out of draft) and I can help test it |
opening for review I was thinking of maybe looking into debugging with the source code mapping in another PR, but open to include it in this one |
Working great!! Thanks a lot @joshieDo 🔥 Only issue I can find is that if a tx fails in the constructor, you get a panic instead of a trace, but this is present in forge too, so not a problem caused by your PR. @onbjerg I recall we've briefly discussed this in an issue before, but not sure if there's a dedicated issue for this? If so let me know and I can create one
|
cli/src/cmd/cast/run.rs
Outdated
let builder = ExecutorBuilder::new() | ||
.with_config(env) | ||
.with_spec(crate::utils::evm_spec(&config.evm_version)) | ||
.with_gas_limit(evm_opts.gas_limit()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably want to use the txs gas limit with tx.gas
here instead?
cli/src/opts/cast.rs
Outdated
@@ -619,6 +619,8 @@ If an address is specified, then the ABI is fetched from Etherscan."# | |||
#[clap(arg_enum)] | |||
shell: clap_complete::Shell, | |||
}, | |||
#[clap(name = "run", about = "Runs a published transaction in a local environment.")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[clap(name = "run", about = "Runs a published transaction in a local environment.")] | |
#[clap(name = "run", about = "Runs a published transaction in a local environment and prints the trace.")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some nits
impl Cmd for RunArgs { | ||
type Output = (); | ||
fn run(self) -> eyre::Result<Self::Output> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's make this a simple async function instead and not implement the Cmd trait here, so that we don't need to deal with the runtime here
decoder.identify(trace, ðerscan_identifier); | ||
} | ||
|
||
if self.debug { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we move this to a separate function?
evm/src/executor/mod.rs
Outdated
pub fn enable_tracing(&mut self) { | ||
self.inspector_config.tracing = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these look like builder functions,
there are two common patterns for these kinds of functions, either fn set(mut self, val) -> Self
or fn set(&mut self, val)-> &mut Self
seems like we can apply the latter here and return &mut Self
?
Co-authored-by: Matt Solomon <matt@mattsolomon.dev>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Let's ship it and if there's any bugs iterate from there?
@joshieDo this is a fun enough feature, feel free to tweet it out to the world :)
Motivation
Run a published transaction locally with trace and/or debug.
Solution
Example (BEAN exploit):
too long, didn't fit lol