Skip to content

Commit 7f41280

Browse files
jsvisaklkvr
andauthored
feat(script): support custom create2 deployer (#9278)
* script: add --create2-deployer Signed-off-by: jsvisa <[email protected]> * script: add create2 deployer Signed-off-by: jsvisa <[email protected]> * evm/constants: add get_create2_deployer from env or default Signed-off-by: jsvisa <[email protected]> * evm/core: use env's create2 Signed-off-by: jsvisa <[email protected]> * script: fetch create2_deployer from env or default Signed-off-by: jsvisa <[email protected]> * fmt Signed-off-by: jsvisa <[email protected]> * docs Signed-off-by: jsvisa <[email protected]> * evm/constants: use sync::LazyLock Signed-off-by: jsvisa <[email protected]> * evm/inspector: add fn create2_deployer Signed-off-by: jsvisa <[email protected]> * config: add create2_deployer Signed-off-by: jsvisa <[email protected]> * evm/inpector: set create2 deployer Signed-off-by: jsvisa <[email protected]> * evm-opts: add create2_deployer Signed-off-by: jsvisa <[email protected]> * script: pass deployer2-creater from cli or config Signed-off-by: jsvisa <[email protected]> * script: use create2 address to fill tx meta Signed-off-by: jsvisa <[email protected]> * config: create2 address ,no Option Signed-off-by: jsvisa <[email protected]> * script/runner: set inspector.create2_deployer with evm_opts Signed-off-by: jsvisa <[email protected]> * clippy Signed-off-by: jsvisa <[email protected]> * doc typo Signed-off-by: jsvisa <[email protected]> * fix/evm-opts: default value of create2_deployer Signed-off-by: jsvisa <[email protected]> * evm/core: no need to extract create2 deployer from env Signed-off-by: jsvisa <[email protected]> * evm/core: implement Default for EvmOpts.create2_deployer Signed-off-by: jsvisa <[email protected]> * evm/core: use constants::DEFAULT create2 deployer Signed-off-by: jsvisa <[email protected]> * evm/core: output create2 deployer Signed-off-by: jsvisa <[email protected]> unit test Signed-off-by: jsvisa <[email protected]> * evm/evm: set create2 deployer for trace and stack Signed-off-by: jsvisa <[email protected]> * cast/{run,call}: set create2 deployer Signed-off-by: jsvisa <[email protected]> * forge/runner: set create2 deployer Signed-off-by: jsvisa <[email protected]> * script: set create2 deployer for stack Signed-off-by: jsvisa <[email protected]> * verify: set create2 deployer Signed-off-by: jsvisa <[email protected]> * clipy Signed-off-by: jsvisa <[email protected]> * fmt Signed-off-by: jsvisa <[email protected]> * script: use executor's create2 deployer Signed-off-by: jsvisa <[email protected]> * script: wrap create2_deployer inside executor Signed-off-by: jsvisa <[email protected]> * script: add custom create2 test Signed-off-by: jsvisa <[email protected]> * script: add nonexist create2 Signed-off-by: jsvisa <[email protected]> * all: set EvmOpts.create2_deployer Signed-off-by: jsvisa <[email protected]> * script: no need to pass create2_deployer in fill_metadata Signed-off-by: jsvisa <[email protected]> * evm/executor: duplicate set create2's deployer address Signed-off-by: jsvisa <[email protected]> * evm: check create2 codehash Signed-off-by: jsvisa <[email protected]> * tests/script: test with notmatched create2 deployer Signed-off-by: jsvisa <[email protected]> * clipy Signed-off-by: jsvisa <[email protected]> * evm: skip serialize create2_deployer if none Signed-off-by: jsvisa <[email protected]> * test: add test of deployer2 address Signed-off-by: jsvisa <[email protected]> * Update crates/script/src/lib.rs --------- Signed-off-by: jsvisa <[email protected]> Co-authored-by: Arsenii Kulikov <[email protected]>
1 parent 4527475 commit 7f41280

File tree

23 files changed

+281
-51
lines changed

23 files changed

+281
-51
lines changed

crates/cast/bin/cmd/call.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ use foundry_config::{
1818
},
1919
Config,
2020
};
21-
use foundry_evm::{executors::TracingExecutor, opts::EvmOpts};
21+
use foundry_evm::{
22+
executors::TracingExecutor,
23+
opts::EvmOpts,
24+
traces::{InternalTraceMode, TraceMode},
25+
};
2226
use std::str::FromStr;
2327

2428
/// CLI arguments for `cast call`.
@@ -175,21 +179,29 @@ impl CallArgs {
175179
config.fork_block_number = Some(block_number);
176180
}
177181

182+
let create2_deployer = evm_opts.create2_deployer;
178183
let (mut env, fork, chain, alphanet) =
179184
TracingExecutor::get_fork_material(&config, evm_opts).await?;
180185

181186
// modify settings that usually set in eth_call
182187
env.cfg.disable_block_gas_limit = true;
183188
env.block.gas_limit = U256::MAX;
184189

190+
let trace_mode = TraceMode::Call
191+
.with_debug(debug)
192+
.with_decode_internal(if decode_internal {
193+
InternalTraceMode::Full
194+
} else {
195+
InternalTraceMode::None
196+
})
197+
.with_state_changes(shell::verbosity() > 4);
185198
let mut executor = TracingExecutor::new(
186199
env,
187200
fork,
188201
evm_version,
189-
debug,
190-
decode_internal,
191-
shell::verbosity() > 4,
202+
trace_mode,
192203
alphanet,
204+
create2_deployer,
193205
);
194206

195207
let value = tx.value.unwrap_or_default();

crates/cast/bin/cmd/run.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use foundry_config::{
2323
use foundry_evm::{
2424
executors::{EvmError, TracingExecutor},
2525
opts::EvmOpts,
26+
traces::{InternalTraceMode, TraceMode},
2627
utils::configure_tx_env,
2728
};
2829

@@ -136,6 +137,7 @@ impl RunArgs {
136137
// we need to fork off the parent block
137138
config.fork_block_number = Some(tx_block_number - 1);
138139

140+
let create2_deployer = evm_opts.create2_deployer;
139141
let (mut env, fork, chain, alphanet) =
140142
TracingExecutor::get_fork_material(&config, evm_opts).await?;
141143

@@ -161,14 +163,21 @@ impl RunArgs {
161163
}
162164
}
163165

166+
let trace_mode = TraceMode::Call
167+
.with_debug(self.debug)
168+
.with_decode_internal(if self.decode_internal {
169+
InternalTraceMode::Full
170+
} else {
171+
InternalTraceMode::None
172+
})
173+
.with_state_changes(shell::verbosity() > 4);
164174
let mut executor = TracingExecutor::new(
165175
env.clone(),
166176
fork,
167177
evm_version,
168-
self.debug,
169-
self.decode_internal,
170-
shell::verbosity() > 4,
178+
trace_mode,
171179
alphanet,
180+
create2_deployer,
172181
);
173182
let mut env =
174183
EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id());

crates/cheatcodes/src/inspector.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,10 @@ impl InspectorExt for Cheatcodes {
15871587
false
15881588
}
15891589
}
1590+
1591+
fn create2_deployer(&self) -> Address {
1592+
self.config.evm_opts.create2_deployer
1593+
}
15901594
}
15911595

15921596
impl Cheatcodes {

crates/common/src/evm.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ pub struct EvmArgs {
103103
#[serde(skip)]
104104
pub always_use_create_2_factory: bool,
105105

106+
/// The CREATE2 deployer address to use, this will override the one in the config.
107+
#[arg(long, value_name = "ADDRESS")]
108+
#[serde(skip_serializing_if = "Option::is_none")]
109+
pub create2_deployer: Option<Address>,
110+
106111
/// Sets the number of assumed available compute units per second for this provider
107112
///
108113
/// default value: 330

crates/config/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,9 @@ pub struct Config {
454454
/// CREATE2 salt to use for the library deployment in scripts.
455455
pub create2_library_salt: B256,
456456

457+
/// The CREATE2 deployer address to use.
458+
pub create2_deployer: Address,
459+
457460
/// Configuration for Vyper compiler
458461
pub vyper: VyperConfig,
459462

@@ -567,6 +570,10 @@ impl Config {
567570
/// Default salt for create2 library deployments
568571
pub const DEFAULT_CREATE2_LIBRARY_SALT: FixedBytes<32> = FixedBytes::<32>::ZERO;
569572

573+
/// Default create2 deployer
574+
pub const DEFAULT_CREATE2_DEPLOYER: Address =
575+
address!("4e59b44847b379578588920ca78fbf26c0b4956c");
576+
570577
/// Docker image with eof-enabled solc binary
571578
pub const EOF_SOLC_IMAGE: &'static str = "ghcr.io/paradigmxyz/forge-eof@sha256:46f868ce5264e1190881a3a335d41d7f42d6f26ed20b0c823609c715e38d603f";
572579

@@ -2390,6 +2397,7 @@ impl Default for Config {
23902397
labels: Default::default(),
23912398
unchecked_cheatcode_artifacts: false,
23922399
create2_library_salt: Self::DEFAULT_CREATE2_LIBRARY_SALT,
2400+
create2_deployer: Self::DEFAULT_CREATE2_DEPLOYER,
23932401
skip: vec![],
23942402
dependencies: Default::default(),
23952403
soldeer: Default::default(),

crates/evm/core/src/constants.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920c
4949
pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3");
5050
/// The runtime code of the default CREATE2 deployer.
5151
pub const DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE: &[u8] = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3");
52+
/// The hash of the default CREATE2 deployer code.
53+
///
54+
/// This is calculated as `keccak256([`DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE`])`.
55+
pub const DEFAULT_CREATE2_DEPLOYER_CODEHASH: B256 =
56+
b256!("2fa86add0aed31f33a762c9d88e807c475bd51d0f52bd0955754b2608f7e4989");
5257

5358
#[cfg(test)]
5459
mod tests {

crates/evm/core/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
66
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
77

8+
use crate::constants::DEFAULT_CREATE2_DEPLOYER;
9+
use alloy_primitives::Address;
810
use auto_impl::auto_impl;
911
use backend::DatabaseExt;
1012
use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, EvmContext, Inspector};
@@ -54,6 +56,11 @@ pub trait InspectorExt: for<'a> Inspector<&'a mut dyn DatabaseExt> {
5456
fn is_alphanet(&self) -> bool {
5557
false
5658
}
59+
60+
/// Returns the CREATE2 deployer address.
61+
fn create2_deployer(&self) -> Address {
62+
DEFAULT_CREATE2_DEPLOYER
63+
}
5764
}
5865

5966
impl InspectorExt for NoOpInspector {}

crates/evm/core/src/opts.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::fork::environment;
2-
use crate::fork::CreateFork;
2+
use crate::{constants::DEFAULT_CREATE2_DEPLOYER, fork::CreateFork};
33
use alloy_primitives::{Address, B256, U256};
44
use alloy_provider::{network::AnyRpcBlock, Provider};
55
use eyre::WrapErr;
@@ -9,7 +9,7 @@ use revm::primitives::{BlockEnv, CfgEnv, TxEnv};
99
use serde::{Deserialize, Serialize};
1010
use url::Url;
1111

12-
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
12+
#[derive(Clone, Debug, Serialize, Deserialize)]
1313
pub struct EvmOpts {
1414
/// The EVM environment configuration.
1515
#[serde(flatten)]
@@ -66,6 +66,34 @@ pub struct EvmOpts {
6666

6767
/// whether to enable Alphanet features.
6868
pub alphanet: bool,
69+
70+
/// The CREATE2 deployer's address.
71+
pub create2_deployer: Address,
72+
}
73+
74+
impl Default for EvmOpts {
75+
fn default() -> Self {
76+
Self {
77+
env: Env::default(),
78+
fork_url: None,
79+
fork_block_number: None,
80+
fork_retries: None,
81+
fork_retry_backoff: None,
82+
compute_units_per_second: None,
83+
no_rpc_rate_limit: false,
84+
no_storage_caching: false,
85+
initial_balance: U256::default(),
86+
sender: Address::default(),
87+
ffi: false,
88+
always_use_create_2_factory: false,
89+
verbosity: 0,
90+
memory_limit: 0,
91+
isolate: false,
92+
disable_block_gas_limit: false,
93+
alphanet: false,
94+
create2_deployer: DEFAULT_CREATE2_DEPLOYER,
95+
}
96+
}
6997
}
7098

7199
impl EvmOpts {

crates/evm/core/src/utils.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub use crate::ic::*;
22
use crate::{
3-
backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256,
3+
backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, precompiles::ALPHANET_P256,
44
InspectorExt,
55
};
66
use alloy_consensus::BlockHeader;
@@ -149,12 +149,16 @@ pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 {
149149
spent - (refunded).min(spent / refund_quotient)
150150
}
151151

152-
fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInputs {
152+
fn get_create2_factory_call_inputs(
153+
salt: U256,
154+
inputs: CreateInputs,
155+
deployer: Address,
156+
) -> CallInputs {
153157
let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat();
154158
CallInputs {
155159
caller: inputs.caller,
156-
bytecode_address: DEFAULT_CREATE2_DEPLOYER,
157-
target_address: DEFAULT_CREATE2_DEPLOYER,
160+
bytecode_address: deployer,
161+
target_address: deployer,
158162
scheme: CallScheme::Call,
159163
value: CallValue::Transfer(inputs.value),
160164
input: calldata.into(),
@@ -165,7 +169,7 @@ fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInpu
165169
}
166170
}
167171

168-
/// Used for routing certain CREATE2 invocations through [DEFAULT_CREATE2_DEPLOYER].
172+
/// Used for routing certain CREATE2 invocations through CREATE2_DEPLOYER.
169173
///
170174
/// Overrides create hook with CALL frame if [InspectorExt::should_use_create2_factory] returns
171175
/// true. Keeps track of overridden frames and handles outcome in the overridden insert_call_outcome
@@ -190,8 +194,10 @@ pub fn create2_handler_register<I: InspectorExt>(
190194

191195
let gas_limit = inputs.gas_limit;
192196

197+
// Get CREATE2 deployer.
198+
let create2_deployer = ctx.external.create2_deployer();
193199
// Generate call inputs for CREATE2 factory.
194-
let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs);
200+
let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs, create2_deployer);
195201

196202
// Call inspector to change input or return outcome.
197203
let outcome = ctx.external.call(&mut ctx.evm, &mut call_inputs);
@@ -202,12 +208,21 @@ pub fn create2_handler_register<I: InspectorExt>(
202208
.push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
203209

204210
// Sanity check that CREATE2 deployer exists.
205-
let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.info.code_hash;
211+
let code_hash = ctx.evm.load_account(create2_deployer)?.info.code_hash;
206212
if code_hash == KECCAK_EMPTY {
207213
return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome {
208214
result: InterpreterResult {
209215
result: InstructionResult::Revert,
210-
output: "missing CREATE2 deployer".into(),
216+
output: format!("missing CREATE2 deployer: {create2_deployer}").into(),
217+
gas: Gas::new(gas_limit),
218+
},
219+
memory_offset: 0..0,
220+
})))
221+
} else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
222+
return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome {
223+
result: InterpreterResult {
224+
result: InstructionResult::Revert,
225+
output: "invalid CREATE2 deployer bytecode".into(),
211226
gas: Gas::new(gas_limit),
212227
},
213228
memory_offset: 0..0,

crates/evm/evm/src/executors/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use foundry_evm_core::{
2424
},
2525
decode::{RevertDecoder, SkipReason},
2626
utils::StateChangeset,
27+
InspectorExt,
2728
};
2829
use foundry_evm_coverage::HitMaps;
2930
use foundry_evm_traces::{SparsedTraceArena, TraceMode};
@@ -240,6 +241,11 @@ impl Executor {
240241
self
241242
}
242243

244+
#[inline]
245+
pub fn create2_deployer(&self) -> Address {
246+
self.inspector().create2_deployer()
247+
}
248+
243249
/// Deploys a contract and commits the new state to the underlying database.
244250
///
245251
/// Executes a CREATE transaction with the contract `code` and persistent database state

0 commit comments

Comments
 (0)