diff --git a/Cargo.lock b/Cargo.lock index 4978d7b..5b14b92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4451,6 +4451,7 @@ dependencies = [ "clap", "color-eyre", "forge-script", + "foundry-common", "futures", "op-alloy-rpc-types", "op-test-vectors", diff --git a/Cargo.toml b/Cargo.toml index c64bdd4..4d269fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ alloy = { version = "0.1.4", features = [ "rpc-types-trace", "providers" ] } +foundry-common = { git = "https://github.com/foundry-rs/foundry", default-features = true, rev = "7c4482fc9541f11b57575e2d8bf7bd190b61bda6" } anvil = { git = "https://github.com/foundry-rs/foundry", default-features = true, rev = "7c4482fc9541f11b57575e2d8bf7bd190b61bda6" } anvil-core = { git = "https://github.com/foundry-rs/foundry", default-features = true, rev = "7c4482fc9541f11b57575e2d8bf7bd190b61bda6" } cast = { git = "https://github.com/foundry-rs/foundry", rev = "7c4482fc9541f11b57575e2d8bf7bd190b61bda6" } diff --git a/crates/opt8n/Cargo.toml b/crates/opt8n/Cargo.toml index ed8f233..27cecf2 100644 --- a/crates/opt8n/Cargo.toml +++ b/crates/opt8n/Cargo.toml @@ -12,6 +12,7 @@ shellwords.workspace = true tokio.workspace = true futures.workspace = true color-eyre.workspace = true +foundry-common.workspace = true anvil.workspace = true anvil-core.workspace = true cast.workspace = true diff --git a/crates/opt8n/src/main.rs b/crates/opt8n/src/main.rs index e893d9e..8b15b13 100644 --- a/crates/opt8n/src/main.rs +++ b/crates/opt8n/src/main.rs @@ -46,24 +46,20 @@ async fn main() -> eyre::Result<()> { }); let node_config = args.node_args.into_node_config(); - let mut opt8n = Opt8n::new(Some(node_config), forking, args.output, args.genesis).await; + let mut opt8n = Opt8n::new(Some(node_config), forking, args.output, args.genesis).await?; match args.command { Commands::Repl {} => { opt8n.repl().await?; } - Commands::Script { - script_args: _script_args, - } => { - // TODO: Run foundry script, pass the opt8n anvil instance endpoint to the script + Commands::Script { mut script_args } => { + foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( + script_args.opts.silent, + script_args.json, + ))?; - // foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( - // cmd.opts.silent, - // cmd.json, - // ))?; - // utils::block_on(cmd.run_script()) - - opt8n.mine_block().await; + script_args.evm_opts.fork_url = Some(opt8n.node_handle.http_endpoint()); + opt8n.run_script(script_args).await?; } } diff --git a/crates/opt8n/src/opt8n.rs b/crates/opt8n/src/opt8n.rs index 2952df8..6ce426e 100644 --- a/crates/opt8n/src/opt8n.rs +++ b/crates/opt8n/src/opt8n.rs @@ -11,14 +11,16 @@ use anvil::{cmd::NodeArgs, eth::EthApi, NodeConfig, NodeHandle}; use anvil_core::eth::block::Block; use anvil_core::eth::transaction::PendingTransaction; use cast::traces::{GethTraceBuilder, TracingInspectorConfig}; +use forge_script::ScriptArgs; use std::{ fs::{self, File}, + future::IntoFuture, path::PathBuf, }; use clap::{CommandFactory, FromArgMatches, Parser}; -use color_eyre::eyre::Result; -use futures::StreamExt; +use color_eyre::eyre::{Error, Result}; +use futures::{join, StreamExt, TryFutureExt}; use op_test_vectors::execution::{ExecutionFixture, ExecutionReceipt, ExecutionResult}; use revm::{ db::{AlloyDB, CacheDB}, @@ -43,7 +45,7 @@ impl Opt8n { fork: Option, output_file: PathBuf, genesis: Option, - ) -> Self { + ) -> Result { let genesis = genesis.as_ref().map(|path| { serde_json::from_reader(File::open(path).expect("TODO: handle error Invalid path")) .expect("TODO: handle error Invalid genesis") @@ -56,15 +58,15 @@ impl Opt8n { .with_genesis(genesis); let (eth_api, node_handle) = anvil::spawn(node_config.clone()).await; - - Self { + eth_api.anvil_set_logging(false).await?; + Ok(Self { eth_api, node_handle, execution_fixture: ExecutionFixture::default(), fork, node_config, output_file, - } + }) } /// Listens for commands, and new blocks from the block stream. @@ -97,6 +99,30 @@ impl Opt8n { Ok(()) } + /// Run a Forge script with the given arguments, and generate an execution fixture + /// from the broadcasted transactions. + pub async fn run_script(&mut self, script_args: Box) -> Result<()> { + let mut new_block = self.eth_api.backend.new_block_notifications(); + self.eth_api.anvil_set_interval_mining(12)?; + + let f = async { + let new_block = new_block.next(); + join!( + new_block.into_future(), + script_args.run_script().map_err(Error::from) + ) + }; + + if let (Some(new_block), _) = f.await { + tracing::info!("New block: {:?}", new_block); + if let Some(block) = self.eth_api.backend.get_block_by_hash(new_block.hash) { + self.generate_execution_fixture(block).await?; + } + } + + Ok(()) + } + async fn receive_command(&self) -> Result { let line = BufReader::new(tokio::io::stdin()) .lines() @@ -169,8 +195,6 @@ impl Opt8n { )?; db.commit(result.state); - println!("Pre state: {:?}", pre_state_frame); - if let PreStateFrame::Diff(diff) = pre_state_frame { diff.pre.into_iter().for_each(|(account, state)| { self.execution_fixture.alloc.entry(account).or_insert(state);