From 7f41280ee071193557f73f16bae9aee9a5548ee8 Mon Sep 17 00:00:00 2001 From: Delweng Date: Sat, 30 Nov 2024 08:10:35 +0800 Subject: [PATCH] feat(script): support custom create2 deployer (#9278) * script: add --create2-deployer Signed-off-by: jsvisa * script: add create2 deployer Signed-off-by: jsvisa * evm/constants: add get_create2_deployer from env or default Signed-off-by: jsvisa * evm/core: use env's create2 Signed-off-by: jsvisa * script: fetch create2_deployer from env or default Signed-off-by: jsvisa * fmt Signed-off-by: jsvisa * docs Signed-off-by: jsvisa * evm/constants: use sync::LazyLock Signed-off-by: jsvisa * evm/inspector: add fn create2_deployer Signed-off-by: jsvisa * config: add create2_deployer Signed-off-by: jsvisa * evm/inpector: set create2 deployer Signed-off-by: jsvisa * evm-opts: add create2_deployer Signed-off-by: jsvisa * script: pass deployer2-creater from cli or config Signed-off-by: jsvisa * script: use create2 address to fill tx meta Signed-off-by: jsvisa * config: create2 address ,no Option Signed-off-by: jsvisa * script/runner: set inspector.create2_deployer with evm_opts Signed-off-by: jsvisa * clippy Signed-off-by: jsvisa * doc typo Signed-off-by: jsvisa * fix/evm-opts: default value of create2_deployer Signed-off-by: jsvisa * evm/core: no need to extract create2 deployer from env Signed-off-by: jsvisa * evm/core: implement Default for EvmOpts.create2_deployer Signed-off-by: jsvisa * evm/core: use constants::DEFAULT create2 deployer Signed-off-by: jsvisa * evm/core: output create2 deployer Signed-off-by: jsvisa unit test Signed-off-by: jsvisa * evm/evm: set create2 deployer for trace and stack Signed-off-by: jsvisa * cast/{run,call}: set create2 deployer Signed-off-by: jsvisa * forge/runner: set create2 deployer Signed-off-by: jsvisa * script: set create2 deployer for stack Signed-off-by: jsvisa * verify: set create2 deployer Signed-off-by: jsvisa * clipy Signed-off-by: jsvisa * fmt Signed-off-by: jsvisa * script: use executor's create2 deployer Signed-off-by: jsvisa * script: wrap create2_deployer inside executor Signed-off-by: jsvisa * script: add custom create2 test Signed-off-by: jsvisa * script: add nonexist create2 Signed-off-by: jsvisa * all: set EvmOpts.create2_deployer Signed-off-by: jsvisa * script: no need to pass create2_deployer in fill_metadata Signed-off-by: jsvisa * evm/executor: duplicate set create2's deployer address Signed-off-by: jsvisa * evm: check create2 codehash Signed-off-by: jsvisa * tests/script: test with notmatched create2 deployer Signed-off-by: jsvisa * clipy Signed-off-by: jsvisa * evm: skip serialize create2_deployer if none Signed-off-by: jsvisa * test: add test of deployer2 address Signed-off-by: jsvisa * Update crates/script/src/lib.rs --------- Signed-off-by: jsvisa Co-authored-by: Arsenii Kulikov --- crates/cast/bin/cmd/call.rs | 20 ++++++-- crates/cast/bin/cmd/run.rs | 15 ++++-- crates/cheatcodes/src/inspector.rs | 4 ++ crates/common/src/evm.rs | 5 ++ crates/config/src/lib.rs | 8 +++ crates/evm/core/src/constants.rs | 5 ++ crates/evm/core/src/lib.rs | 7 +++ crates/evm/core/src/opts.rs | 32 +++++++++++- crates/evm/core/src/utils.rs | 31 +++++++++--- crates/evm/evm/src/executors/mod.rs | 6 +++ crates/evm/evm/src/executors/trace.rs | 23 ++++----- crates/evm/evm/src/inspectors/stack.rs | 25 ++++++++++ crates/forge/src/multi_runner.rs | 1 + crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/cli/script.rs | 66 ++++++++++++++++++++++++- crates/script/src/build.rs | 7 +-- crates/script/src/lib.rs | 11 +++-- crates/script/src/runner.rs | 10 ++-- crates/script/src/simulate.rs | 6 ++- crates/script/src/transaction.rs | 8 +-- crates/test-utils/src/script.rs | 4 ++ crates/verify/src/utils.rs | 11 +++-- testdata/default/cheats/Broadcast.t.sol | 26 ++++++++++ 23 files changed, 281 insertions(+), 51 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 2d5692efe4c1..1704247dc706 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -18,7 +18,11 @@ use foundry_config::{ }, Config, }; -use foundry_evm::{executors::TracingExecutor, opts::EvmOpts}; +use foundry_evm::{ + executors::TracingExecutor, + opts::EvmOpts, + traces::{InternalTraceMode, TraceMode}, +}; use std::str::FromStr; /// CLI arguments for `cast call`. @@ -175,6 +179,7 @@ impl CallArgs { config.fork_block_number = Some(block_number); } + let create2_deployer = evm_opts.create2_deployer; let (mut env, fork, chain, alphanet) = TracingExecutor::get_fork_material(&config, evm_opts).await?; @@ -182,14 +187,21 @@ impl CallArgs { env.cfg.disable_block_gas_limit = true; env.block.gas_limit = U256::MAX; + let trace_mode = TraceMode::Call + .with_debug(debug) + .with_decode_internal(if decode_internal { + InternalTraceMode::Full + } else { + InternalTraceMode::None + }) + .with_state_changes(shell::verbosity() > 4); let mut executor = TracingExecutor::new( env, fork, evm_version, - debug, - decode_internal, - shell::verbosity() > 4, + trace_mode, alphanet, + create2_deployer, ); let value = tx.value.unwrap_or_default(); diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index bb5c505b11c3..62a41ca6c998 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -23,6 +23,7 @@ use foundry_config::{ use foundry_evm::{ executors::{EvmError, TracingExecutor}, opts::EvmOpts, + traces::{InternalTraceMode, TraceMode}, utils::configure_tx_env, }; @@ -136,6 +137,7 @@ impl RunArgs { // we need to fork off the parent block config.fork_block_number = Some(tx_block_number - 1); + let create2_deployer = evm_opts.create2_deployer; let (mut env, fork, chain, alphanet) = TracingExecutor::get_fork_material(&config, evm_opts).await?; @@ -161,14 +163,21 @@ impl RunArgs { } } + let trace_mode = TraceMode::Call + .with_debug(self.debug) + .with_decode_internal(if self.decode_internal { + InternalTraceMode::Full + } else { + InternalTraceMode::None + }) + .with_state_changes(shell::verbosity() > 4); let mut executor = TracingExecutor::new( env.clone(), fork, evm_version, - self.debug, - self.decode_internal, - shell::verbosity() > 4, + trace_mode, alphanet, + create2_deployer, ); let mut env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id()); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 6f3acb58c273..447a9e7473ec 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1587,6 +1587,10 @@ impl InspectorExt for Cheatcodes { false } } + + fn create2_deployer(&self) -> Address { + self.config.evm_opts.create2_deployer + } } impl Cheatcodes { diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index c4dfccae1641..dbcaf02aff0a 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -103,6 +103,11 @@ pub struct EvmArgs { #[serde(skip)] pub always_use_create_2_factory: bool, + /// The CREATE2 deployer address to use, this will override the one in the config. + #[arg(long, value_name = "ADDRESS")] + #[serde(skip_serializing_if = "Option::is_none")] + pub create2_deployer: Option
, + /// Sets the number of assumed available compute units per second for this provider /// /// default value: 330 diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index bcdeb04a9952..b88f134d2cc2 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -454,6 +454,9 @@ pub struct Config { /// CREATE2 salt to use for the library deployment in scripts. pub create2_library_salt: B256, + /// The CREATE2 deployer address to use. + pub create2_deployer: Address, + /// Configuration for Vyper compiler pub vyper: VyperConfig, @@ -567,6 +570,10 @@ impl Config { /// Default salt for create2 library deployments pub const DEFAULT_CREATE2_LIBRARY_SALT: FixedBytes<32> = FixedBytes::<32>::ZERO; + /// Default create2 deployer + pub const DEFAULT_CREATE2_DEPLOYER: Address = + address!("4e59b44847b379578588920ca78fbf26c0b4956c"); + /// Docker image with eof-enabled solc binary pub const EOF_SOLC_IMAGE: &'static str = "ghcr.io/paradigmxyz/forge-eof@sha256:46f868ce5264e1190881a3a335d41d7f42d6f26ed20b0c823609c715e38d603f"; @@ -2390,6 +2397,7 @@ impl Default for Config { labels: Default::default(), unchecked_cheatcode_artifacts: false, create2_library_salt: Self::DEFAULT_CREATE2_LIBRARY_SALT, + create2_deployer: Self::DEFAULT_CREATE2_DEPLOYER, skip: vec![], dependencies: Default::default(), soldeer: Default::default(), diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index cebbdcb87c79..2e4fdb52617b 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -49,6 +49,11 @@ pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920c pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); /// The runtime code of the default CREATE2 deployer. pub const DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE: &[u8] = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); +/// The hash of the default CREATE2 deployer code. +/// +/// This is calculated as `keccak256([`DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE`])`. +pub const DEFAULT_CREATE2_DEPLOYER_CODEHASH: B256 = + b256!("2fa86add0aed31f33a762c9d88e807c475bd51d0f52bd0955754b2608f7e4989"); #[cfg(test)] mod tests { diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 1a2ac4c4a0f4..0bdc34cc92ce 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -5,6 +5,8 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +use crate::constants::DEFAULT_CREATE2_DEPLOYER; +use alloy_primitives::Address; use auto_impl::auto_impl; use backend::DatabaseExt; use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, EvmContext, Inspector}; @@ -54,6 +56,11 @@ pub trait InspectorExt: for<'a> Inspector<&'a mut dyn DatabaseExt> { fn is_alphanet(&self) -> bool { false } + + /// Returns the CREATE2 deployer address. + fn create2_deployer(&self) -> Address { + DEFAULT_CREATE2_DEPLOYER + } } impl InspectorExt for NoOpInspector {} diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 6f4448ae482c..c5817e483c25 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,5 +1,5 @@ use super::fork::environment; -use crate::fork::CreateFork; +use crate::{constants::DEFAULT_CREATE2_DEPLOYER, fork::CreateFork}; use alloy_primitives::{Address, B256, U256}; use alloy_provider::{network::AnyRpcBlock, Provider}; use eyre::WrapErr; @@ -9,7 +9,7 @@ use revm::primitives::{BlockEnv, CfgEnv, TxEnv}; use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct EvmOpts { /// The EVM environment configuration. #[serde(flatten)] @@ -66,6 +66,34 @@ pub struct EvmOpts { /// whether to enable Alphanet features. pub alphanet: bool, + + /// The CREATE2 deployer's address. + pub create2_deployer: Address, +} + +impl Default for EvmOpts { + fn default() -> Self { + Self { + env: Env::default(), + fork_url: None, + fork_block_number: None, + fork_retries: None, + fork_retry_backoff: None, + compute_units_per_second: None, + no_rpc_rate_limit: false, + no_storage_caching: false, + initial_balance: U256::default(), + sender: Address::default(), + ffi: false, + always_use_create_2_factory: false, + verbosity: 0, + memory_limit: 0, + isolate: false, + disable_block_gas_limit: false, + alphanet: false, + create2_deployer: DEFAULT_CREATE2_DEPLOYER, + } + } } impl EvmOpts { diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index be9660c72ac4..2257709e5e91 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,6 +1,6 @@ pub use crate::ic::*; use crate::{ - backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, + backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, precompiles::ALPHANET_P256, InspectorExt, }; use alloy_consensus::BlockHeader; @@ -149,12 +149,16 @@ pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 { spent - (refunded).min(spent / refund_quotient) } -fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInputs { +fn get_create2_factory_call_inputs( + salt: U256, + inputs: CreateInputs, + deployer: Address, +) -> CallInputs { let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat(); CallInputs { caller: inputs.caller, - bytecode_address: DEFAULT_CREATE2_DEPLOYER, - target_address: DEFAULT_CREATE2_DEPLOYER, + bytecode_address: deployer, + target_address: deployer, scheme: CallScheme::Call, value: CallValue::Transfer(inputs.value), input: calldata.into(), @@ -165,7 +169,7 @@ fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInpu } } -/// Used for routing certain CREATE2 invocations through [DEFAULT_CREATE2_DEPLOYER]. +/// Used for routing certain CREATE2 invocations through CREATE2_DEPLOYER. /// /// Overrides create hook with CALL frame if [InspectorExt::should_use_create2_factory] returns /// true. Keeps track of overridden frames and handles outcome in the overridden insert_call_outcome @@ -190,8 +194,10 @@ pub fn create2_handler_register( let gas_limit = inputs.gas_limit; + // Get CREATE2 deployer. + let create2_deployer = ctx.external.create2_deployer(); // Generate call inputs for CREATE2 factory. - let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs); + let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs, create2_deployer); // Call inspector to change input or return outcome. let outcome = ctx.external.call(&mut ctx.evm, &mut call_inputs); @@ -202,12 +208,21 @@ pub fn create2_handler_register( .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); // Sanity check that CREATE2 deployer exists. - let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.info.code_hash; + let code_hash = ctx.evm.load_account(create2_deployer)?.info.code_hash; if code_hash == KECCAK_EMPTY { return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { result: InstructionResult::Revert, - output: "missing CREATE2 deployer".into(), + output: format!("missing CREATE2 deployer: {create2_deployer}").into(), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + }))) + } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { + return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 deployer bytecode".into(), gas: Gas::new(gas_limit), }, memory_offset: 0..0, diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 2ccfad9e2a58..8146cec82d44 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -24,6 +24,7 @@ use foundry_evm_core::{ }, decode::{RevertDecoder, SkipReason}, utils::StateChangeset, + InspectorExt, }; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::{SparsedTraceArena, TraceMode}; @@ -240,6 +241,11 @@ impl Executor { self } + #[inline] + pub fn create2_deployer(&self) -> Address { + self.inspector().create2_deployer() + } + /// Deploys a contract and commits the new state to the underlying database. /// /// Executes a CREATE transaction with the contract `code` and persistent database state diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index ceea6e67248a..214e7c28ae16 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -1,8 +1,9 @@ use crate::executors::{Executor, ExecutorBuilder}; +use alloy_primitives::Address; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{utils::evm_spec_id, Chain, Config}; use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; -use foundry_evm_traces::{InternalTraceMode, TraceMode}; +use foundry_evm_traces::TraceMode; use revm::primitives::{Env, SpecId}; use std::ops::{Deref, DerefMut}; @@ -16,25 +17,21 @@ impl TracingExecutor { env: revm::primitives::Env, fork: Option, version: Option, - debug: bool, - decode_internal: bool, - with_state_changes: bool, + trace_mode: TraceMode, alphanet: bool, + create2_deployer: Address, ) -> Self { let db = Backend::spawn(fork); - let trace_mode = TraceMode::Call - .with_debug(debug) - .with_decode_internal(if decode_internal { - InternalTraceMode::Full - } else { - InternalTraceMode::None - }) - .with_state_changes(with_state_changes); Self { // configures a bare version of the evm executor: no cheatcode inspector is enabled, // tracing will be enabled only for the targeted transaction executor: ExecutorBuilder::new() - .inspectors(|stack| stack.trace_mode(trace_mode).alphanet(alphanet)) + .inspectors(|stack| { + stack + .trace_mode(trace_mode) + .alphanet(alphanet) + .create2_deployer(create2_deployer) + }) .spec(evm_spec_id(&version.unwrap_or_default(), alphanet)) .build(env, db), } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 403e906f6323..accbca4fd59c 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -59,6 +59,8 @@ pub struct InspectorStackBuilder { pub alphanet: bool, /// The wallets to set in the cheatcodes context. pub wallets: Option, + /// The CREATE2 deployer address. + pub create2_deployer: Address, } impl InspectorStackBuilder { @@ -156,6 +158,12 @@ impl InspectorStackBuilder { self } + #[inline] + pub fn create2_deployer(mut self, create2_deployer: Address) -> Self { + self.create2_deployer = create2_deployer; + self + } + /// Builds the stack of inspectors to use when transacting/committing on the EVM. pub fn build(self) -> InspectorStack { let Self { @@ -171,6 +179,7 @@ impl InspectorStackBuilder { enable_isolation, alphanet, wallets, + create2_deployer, } = self; let mut stack = InspectorStack::new(); @@ -197,6 +206,7 @@ impl InspectorStackBuilder { stack.enable_isolation(enable_isolation); stack.alphanet(alphanet); + stack.set_create2_deployer(create2_deployer); // environment, must come after all of the inspectors if let Some(block) = block { @@ -282,6 +292,7 @@ pub struct InspectorStackInner { pub tracer: Option, pub enable_isolation: bool, pub alphanet: bool, + pub create2_deployer: Address, /// Flag marking if we are in the inner EVM context. pub in_inner_context: bool, @@ -398,6 +409,12 @@ impl InspectorStack { self.alphanet = yes; } + /// Set the CREATE2 deployer address. + #[inline] + pub fn set_create2_deployer(&mut self, deployer: Address) { + self.create2_deployer = deployer; + } + /// Set whether to enable the log collector. #[inline] pub fn collect_logs(&mut self, yes: bool) { @@ -1022,6 +1039,10 @@ impl InspectorExt for InspectorStackRefMut<'_> { fn is_alphanet(&self) -> bool { self.inner.alphanet } + + fn create2_deployer(&self) -> Address { + self.inner.create2_deployer + } } impl Inspector<&mut dyn DatabaseExt> for InspectorStack { @@ -1124,6 +1145,10 @@ impl InspectorExt for InspectorStack { fn is_alphanet(&self) -> bool { self.alphanet } + + fn create2_deployer(&self) -> Address { + self.create2_deployer + } } impl<'a> Deref for InspectorStackRefMut<'a> { diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index dfb498c060e6..572c3d4fe984 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -260,6 +260,7 @@ impl MultiContractRunner { .coverage(self.coverage) .enable_isolation(self.isolation) .alphanet(self.alphanet) + .create2_deployer(self.evm_opts.create2_deployer) }) .spec(self.evm_spec) .gas_limit(self.evm_opts.gas_limit()) diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index aa38a0b77c65..9a7e61c6a4e3 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -146,6 +146,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { isolate: true, unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, + create2_deployer: Config::DEFAULT_CREATE2_DEPLOYER, vyper: Default::default(), skip: vec![], dependencies: Default::default(), diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index d7776eee2e87..e52bd2fef4a7 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -855,6 +855,70 @@ forgetest_async!(can_deploy_with_create2, |prj, cmd| { .run(ScriptOutcome::ScriptFailed); }); +forgetest_async!(can_deploy_with_custom_create2, |prj, cmd| { + let (api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + let create2 = Address::from_str("0x0000000000000000000000000000000000b4956c").unwrap(); + + // Prepare CREATE2 Deployer + api.anvil_set_code( + create2, + Bytes::from_static(foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE), + ) + .await + .unwrap(); + + tester + .add_deployer(0) + .load_private_keys(&[0]) + .await + .add_create2_deployer(create2) + .add_sig("BroadcastTestNoLinking", "deployCreate2(address)") + .arg(&create2.to_string()) + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(&[(0, 2)]) + .await; +}); + +forgetest_async!(can_deploy_with_custom_create2_notmatched_bytecode, |prj, cmd| { + let (api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + let create2 = Address::from_str("0x0000000000000000000000000000000000b4956c").unwrap(); + + // Prepare CREATE2 Deployer + api.anvil_set_code( + create2, + Bytes::from_static(&hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cef")), + ) + .await + .unwrap(); + + tester + .add_deployer(0) + .load_private_keys(&[0]) + .await + .add_create2_deployer(create2) + .add_sig("BroadcastTestNoLinking", "deployCreate2()") + .simulate(ScriptOutcome::ScriptFailed) + .broadcast(ScriptOutcome::ScriptFailed); +}); + +forgetest_async!(canot_deploy_with_nonexist_create2, |prj, cmd| { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + let create2 = Address::from_str("0x0000000000000000000000000000000000b4956c").unwrap(); + + tester + .add_deployer(0) + .load_private_keys(&[0]) + .await + .add_create2_deployer(create2) + .add_sig("BroadcastTestNoLinking", "deployCreate2()") + .simulate(ScriptOutcome::ScriptFailed) + .broadcast(ScriptOutcome::ScriptFailed); +}); + forgetest_async!(can_deploy_and_simulate_25_txes_concurrently, |prj, cmd| { let (_api, handle) = spawn(NodeConfig::test()).await; let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); @@ -2002,7 +2066,7 @@ contract SimpleScript is Script { ]); cmd.assert_failure().stderr_eq(str![[r#" -Error: script failed: missing CREATE2 deployer +Error: script failed: missing CREATE2 deployer: 0x4e59b44847b379578588920cA78FbF26c0B4956C "#]]); }); diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index ef42740841b2..052e78e10781 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -17,7 +17,7 @@ use foundry_compilers::{ utils::source_files_iter, ArtifactId, ProjectCompileOutput, }; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::debug::ContractSources}; +use foundry_evm::traces::debug::ContractSources; use foundry_linking::Linker; use std::{path::PathBuf, str::FromStr, sync::Arc}; @@ -40,9 +40,10 @@ impl BuildData { /// Links contracts. Uses CREATE2 linking when possible, otherwise falls back to /// default linking with sender nonce and address. pub async fn link(self, script_config: &ScriptConfig) -> Result { + let create2_deployer = script_config.evm_opts.create2_deployer; let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { let provider = try_get_http_provider(fork_url)?; - let deployer_code = provider.get_code_at(DEFAULT_CREATE2_DEPLOYER).await?; + let deployer_code = provider.get_code_at(create2_deployer).await?; !deployer_code.is_empty() } else { @@ -57,7 +58,7 @@ impl BuildData { self.get_linker() .link_with_create2( known_libraries.clone(), - DEFAULT_CREATE2_DEPLOYER, + create2_deployer, script_config.config.create2_library_salt, &self.target, ) diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 6ccc818410a4..ccf5eab2ad0d 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -46,7 +46,6 @@ use foundry_config::{ }; use foundry_evm::{ backend::Backend, - constants::DEFAULT_CREATE2_DEPLOYER, executors::ExecutorBuilder, inspectors::{ cheatcodes::{BroadcastableTransactions, Wallets}, @@ -239,7 +238,9 @@ impl ScriptArgs { pub async fn run_script(self) -> Result<()> { trace!(target: "script", "executing script command"); - let compiled = self.preprocess().await?.compile()?; + let state = self.preprocess().await?; + let create2_deployer = state.script_config.evm_opts.create2_deployer; + let compiled = state.compile()?; // Move from `CompiledState` to `BundledState` either by resuming or executing and // simulating script. @@ -294,6 +295,7 @@ impl ScriptArgs { pre_simulation.args.check_contract_sizes( &pre_simulation.execution_result, &pre_simulation.build_data.known_contracts, + create2_deployer, )?; pre_simulation.fill_metadata().await?.bundle().await? @@ -384,6 +386,7 @@ impl ScriptArgs { &self, result: &ScriptResult, known_contracts: &ContractsByArtifact, + create2_deployer: Address, ) -> Result<()> { // (name, &init, &deployed)[] let mut bytecodes: Vec<(String, &[u8], &[u8])> = vec![]; @@ -428,7 +431,7 @@ impl ScriptArgs { // Find if it's a CREATE or CREATE2. Otherwise, skip transaction. if let Some(TxKind::Call(to)) = to { - if to == DEFAULT_CREATE2_DEPLOYER { + if to == create2_deployer { // Size of the salt prefix. offset = 32; } else { @@ -553,6 +556,7 @@ impl ScriptConfig { // dapptools compatibility 1 }; + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default() }) } @@ -612,6 +616,7 @@ impl ScriptConfig { stack .trace_mode(if debug { TraceMode::Debug } else { TraceMode::Call }) .alphanet(self.evm_opts.alphanet) + .create2_deployer(self.evm_opts.create2_deployer) }) .spec(self.config.evm_spec_id()) .gas_limit(self.evm_opts.gas_limit()) diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index fa8ff19d9a9d..2d58e381f635 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -7,7 +7,7 @@ use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; use foundry_config::Config; use foundry_evm::{ - constants::{CALLER, DEFAULT_CREATE2_DEPLOYER}, + constants::CALLER, executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, opts::EvmOpts, revm::interpreter::{return_ok, InstructionResult}, @@ -83,9 +83,9 @@ impl ScriptRunner { }) }), ScriptPredeployLibraries::Create2(libraries, salt) => { + let create2_deployer = self.executor.create2_deployer(); for library in libraries { - let address = - DEFAULT_CREATE2_DEPLOYER.create2_from_code(salt, library.as_ref()); + let address = create2_deployer.create2_from_code(salt, library.as_ref()); // Skip if already deployed if !self.executor.is_empty_code(address)? { continue; @@ -95,7 +95,7 @@ impl ScriptRunner { .executor .transact_raw( self.evm_opts.sender, - DEFAULT_CREATE2_DEPLOYER, + create2_deployer, calldata.clone().into(), U256::from(0), ) @@ -111,7 +111,7 @@ impl ScriptRunner { from: Some(self.evm_opts.sender), input: calldata.into(), nonce: Some(sender_nonce + library_transactions.len() as u64), - to: Some(TxKind::Call(DEFAULT_CREATE2_DEPLOYER)), + to: Some(TxKind::Call(create2_deployer)), ..Default::default() } .into(), diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index e833073ac00f..0d7990591cd1 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -64,7 +64,11 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); if let Some(TxKind::Call(_)) = to { - builder.set_call(&address_to_abi, &self.execution_artifacts.decoder)?; + builder.set_call( + &address_to_abi, + &self.execution_artifacts.decoder, + self.script_config.evm_opts.create2_deployer, + )?; } else { builder.set_create(false, sender.create(nonce), &address_to_abi)?; } diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index ca6a6226949c..2cf480b9bd0a 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -4,7 +4,7 @@ use alloy_primitives::{hex, Address, TxKind, B256}; use eyre::Result; use forge_script_sequence::TransactionWithMetadata; use foundry_common::{fmt::format_token_raw, ContractData, TransactionMaybeSigned, SELECTOR_LEN}; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; +use foundry_evm::traces::CallTraceDecoder; use itertools::Itertools; use revm_inspectors::tracing::types::CallKind; use std::collections::BTreeMap; @@ -29,16 +29,16 @@ impl ScriptTransactionBuilder { &mut self, local_contracts: &BTreeMap, decoder: &CallTraceDecoder, + create2_deployer: Address, ) -> Result<()> { if let Some(TxKind::Call(to)) = self.transaction.transaction.to() { - if to == DEFAULT_CREATE2_DEPLOYER { + if to == create2_deployer { if let Some(input) = self.transaction.transaction.input() { let (salt, init_code) = input.split_at(32); self.set_create( true, - DEFAULT_CREATE2_DEPLOYER - .create2_from_code(B256::from_slice(salt), init_code), + create2_deployer.create2_from_code(B256::from_slice(salt), init_code), local_contracts, )?; } diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index f15e91d5af78..b82126d2db28 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -171,6 +171,10 @@ impl ScriptTester { self.args(&["--tc", contract_name, "--sig", sig]) } + pub fn add_create2_deployer(&mut self, create2_deployer: Address) -> &mut Self { + self.args(&["--create2-deployer", create2_deployer.to_string().as_str()]) + } + /// Adds the `--unlocked` flag pub fn unlocked(&mut self) -> &mut Self { self.arg("--unlocked") diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 56ec035abcea..824e78443599 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -12,7 +12,10 @@ use foundry_block_explorers::{ use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::RetryProvider, shell}; use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}; use foundry_config::Config; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, opts::EvmOpts}; +use foundry_evm::{ + constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, opts::EvmOpts, + traces::TraceMode, +}; use reqwest::Url; use revm_primitives::{ db::Database, @@ -325,6 +328,7 @@ pub async fn get_tracing_executor( fork_config.fork_block_number = Some(fork_blk_num); fork_config.evm_version = evm_version; + let create2_deployer = evm_opts.create2_deployer; let (env, fork, _chain, is_alphanet) = TracingExecutor::get_fork_material(fork_config, evm_opts).await?; @@ -332,10 +336,9 @@ pub async fn get_tracing_executor( env.clone(), fork, Some(fork_config.evm_version), - false, - false, - false, + TraceMode::Call, is_alphanet, + create2_deployer, ); Ok((env, executor)) diff --git a/testdata/default/cheats/Broadcast.t.sol b/testdata/default/cheats/Broadcast.t.sol index bca8cc2eee8f..97b9d52752d7 100644 --- a/testdata/default/cheats/Broadcast.t.sol +++ b/testdata/default/cheats/Broadcast.t.sol @@ -219,6 +219,32 @@ contract BroadcastTestNoLinking is DSTest { vm.stopBroadcast(); } + function deployCreate2(address deployer) public { + vm.startBroadcast(); + bytes32 salt = bytes32(uint256(1338)); + NoLink test_c2 = new NoLink{salt: salt}(); + assert(test_c2.view_me() == 1337); + + address expectedAddress = address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + deployer, + salt, + keccak256(abi.encodePacked(type(NoLink).creationCode, abi.encode())) + ) + ) + ) + ) + ); + require(address(test_c2) == expectedAddress, "Create2 address mismatch"); + + NoLink test2 = new NoLink(); + vm.stopBroadcast(); + } + function errorStaticCall() public { vm.broadcast(); NoLink test11 = new NoLink();