Skip to content

Commit

Permalink
feat(script): support custom create2 deployer (#9278)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
jsvisa and klkvr authored Nov 30, 2024
1 parent 4527475 commit 7f41280
Show file tree
Hide file tree
Showing 23 changed files with 281 additions and 51 deletions.
20 changes: 16 additions & 4 deletions crates/cast/bin/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -175,21 +179,29 @@ 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?;

// modify settings that usually set in eth_call
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();
Expand Down
15 changes: 12 additions & 3 deletions crates/cast/bin/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use foundry_config::{
use foundry_evm::{
executors::{EvmError, TracingExecutor},
opts::EvmOpts,
traces::{InternalTraceMode, TraceMode},
utils::configure_tx_env,
};

Expand Down Expand Up @@ -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?;

Expand All @@ -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());
Expand Down
4 changes: 4 additions & 0 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,10 @@ impl InspectorExt for Cheatcodes {
false
}
}

fn create2_deployer(&self) -> Address {
self.config.evm_opts.create2_deployer
}
}

impl Cheatcodes {
Expand Down
5 changes: 5 additions & 0 deletions crates/common/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Address>,

/// Sets the number of assumed available compute units per second for this provider
///
/// default value: 330
Expand Down
8 changes: 8 additions & 0 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down Expand Up @@ -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";

Expand Down Expand Up @@ -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(),
Expand Down
5 changes: 5 additions & 0 deletions crates/evm/core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions crates/evm/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {}
Expand Down
32 changes: 30 additions & 2 deletions crates/evm/core/src/opts.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down
31 changes: 23 additions & 8 deletions crates/evm/core/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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(),
Expand All @@ -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
Expand All @@ -190,8 +194,10 @@ pub fn create2_handler_register<I: InspectorExt>(

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);
Expand All @@ -202,12 +208,21 @@ pub fn create2_handler_register<I: InspectorExt>(
.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,
Expand Down
6 changes: 6 additions & 0 deletions crates/evm/evm/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down
23 changes: 10 additions & 13 deletions crates/evm/evm/src/executors/trace.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -16,25 +17,21 @@ impl TracingExecutor {
env: revm::primitives::Env,
fork: Option<CreateFork>,
version: Option<EvmVersion>,
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),
}
Expand Down
Loading

0 comments on commit 7f41280

Please sign in to comment.