Skip to content
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(script): support custom create2 deployer #9278

Open
wants to merge 22 commits into
base: master
Choose a base branch
from

Conversation

jsvisa
Copy link
Contributor

@jsvisa jsvisa commented Nov 7, 2024

Motivation

Currently when deploy contracts using CREATE2 method, it is default to

pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920ca78fbf26c0b4956c");

But in some circumstances, we can't access or deploy the 0x4e59b44847b379578588920ca78fbf26c0b4956c contract, may be caused of not able to send the pre EIP155 raw transaction. So try to introduce a env to change the default create2 deployer.

Solution

If user run forge with env FOUNDRY_CREATE2_DEPLOYER, it will use this address instead of the default one.

@grandizzy
Copy link
Collaborator

@jsvisa thank you! there's #2638 I think your PR could be closing it, can you pls confirm?

@jsvisa
Copy link
Contributor Author

jsvisa commented Nov 7, 2024

@jsvisa thank you! there's #2638 I think your PR could be closing it, can you pls confirm?

I think we can close the later one 😀

let user define create2 factory

@grandizzy
Copy link
Collaborator

I think we can close the later one 😀

Makes sense, waiting for a follow up project to close it then 😀

@jsvisa
Copy link
Contributor Author

jsvisa commented Nov 8, 2024

Makes sense, waiting for a follow up project to close it then 😀

Haha, so how about this one, is there anything I need to adjust

Copy link
Collaborator

@grandizzy grandizzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I see the PR is only for script, but it won't be applied in other scopes, as for labels in traces, contract verification, (default) computeCreate2Address cheatcode and other places, which I think could lead to inconsistencies in deployment process? I'd say a consistent experience would be preferred here, that is when setting the address as env var then to be reflected in all places.

crates/evm/core/src/constants.rs Outdated Show resolved Hide resolved
@jsvisa
Copy link
Contributor Author

jsvisa commented Nov 8, 2024

@grandizzy thanks for the advice, I use forge to deploy contracts in a private evm chain, so only tested the script yet. Thanks for the scope you mentioned, I'll implement them later(maybe after the Devcon) :)

And If the changes are large, I'd prefer to split them into small PRs to include them all, else just in this one PR, WDYT?

@grandizzy
Copy link
Collaborator

@grandizzy thanks for the advice, I use forge to deploy contracts in a private evm chain, so only tested the script yet. Thanks for the scope you mentioned, I'll implement them later(maybe after the Devcon) :)

And If the changes are large, I'd prefer to split them into small PRs to include them all, else just in this one PR, WDYT?

ah, I see, then maybe should be done as a new --create2-address script arg instead env var... @zerosnacks @mattsse @yash-atreya could you please share your thoughts on this approach? thank you!

@jsvisa
Copy link
Contributor Author

jsvisa commented Nov 8, 2024

ah, I see, then maybe should be done as a new --create2-address script arg instead env var

I've tried this approach, but failed, we need to register the create2 handler here

pub fn create2_handler_register<I: InspectorExt>(
handler: &mut EvmHandler<'_, I, &mut dyn DatabaseExt>,
) {
let create2_overrides = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));
let create2_overrides_inner = create2_overrides.clone();
let old_handle = handler.execution.create.clone();
handler.execution.create =
Arc::new(move |ctx, mut inputs| -> Result<FrameOrResult, EVMError<DatabaseError>> {
let CreateScheme::Create2 { salt } = inputs.scheme else {
return old_handle(ctx, inputs);
};
if !ctx.external.should_use_create2_factory(&mut ctx.evm, &mut inputs) {
return old_handle(ctx, inputs);
}
let gas_limit = inputs.gas_limit;
// Get CREATE2 deployer.
let create2_deployer = get_create2_deployer();
// Generate call inputs for CREATE2 factory.
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);
// Push data about current override to the stack.
create2_overrides_inner
.borrow_mut()
.push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
// Sanity check that CREATE2 deployer exists.
let code_hash = ctx.evm.load_account(create2_deployer)?.info.code_hash;
if code_hash == KECCAK_EMPTY {

seems it's hard to pass the cli argument into create2 schema, else maybe we need to adjust it in revm's codebase.

Comment on lines 53 to 61
pub static CREATE2_DEPLOYER: LazyLock<Address> = LazyLock::new(|| {
match std::env::var("FOUNDRY_CREATE2_DEPLOYER") {
Ok(addr) => Address::from_hex(addr).unwrap_or_else(|_| {
error!("env FOUNDRY_CREATE2_DEPLOYER is set, but not a valid Ethereum address, use {DEFAULT_CREATE2_DEPLOYER} instead");
DEFAULT_CREATE2_DEPLOYER
}),
Err(_) => DEFAULT_CREATE2_DEPLOYER,
}
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grandizzy wdyt on making this a Config field? so that you can configure it through foundry.toml as well

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, yep, would be great to have such

@klkvr
Copy link
Member

klkvr commented Nov 8, 2024

ah, I see, then maybe should be done as a new --create2-address script arg instead env var

I've tried this approach, but failed, we need to register the create2 handler here

pub fn create2_handler_register<I: InspectorExt>(
handler: &mut EvmHandler<'_, I, &mut dyn DatabaseExt>,
) {
let create2_overrides = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));
let create2_overrides_inner = create2_overrides.clone();
let old_handle = handler.execution.create.clone();
handler.execution.create =
Arc::new(move |ctx, mut inputs| -> Result<FrameOrResult, EVMError<DatabaseError>> {
let CreateScheme::Create2 { salt } = inputs.scheme else {
return old_handle(ctx, inputs);
};
if !ctx.external.should_use_create2_factory(&mut ctx.evm, &mut inputs) {
return old_handle(ctx, inputs);
}
let gas_limit = inputs.gas_limit;
// Get CREATE2 deployer.
let create2_deployer = get_create2_deployer();
// Generate call inputs for CREATE2 factory.
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);
// Push data about current override to the stack.
create2_overrides_inner
.borrow_mut()
.push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
// Sanity check that CREATE2 deployer exists.
let code_hash = ctx.evm.load_account(create2_deployer)?.info.code_hash;
if code_hash == KECCAK_EMPTY {

seems it's hard to pass the cli argument into create2 schema, else maybe we need to adjust it in revm's codebase.

we can add a helper to InspectorExt to get the deployer address and then just configureInspectorStack with it

@jsvisa
Copy link
Contributor Author

jsvisa commented Nov 8, 2024

ah, I see, then maybe should be done as a new --create2-address script arg instead env var

I've tried this approach, but failed, we need to register the create2 handler here

pub fn create2_handler_register<I: InspectorExt>(
handler: &mut EvmHandler<'_, I, &mut dyn DatabaseExt>,
) {
let create2_overrides = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));
let create2_overrides_inner = create2_overrides.clone();
let old_handle = handler.execution.create.clone();
handler.execution.create =
Arc::new(move |ctx, mut inputs| -> Result<FrameOrResult, EVMError<DatabaseError>> {
let CreateScheme::Create2 { salt } = inputs.scheme else {
return old_handle(ctx, inputs);
};
if !ctx.external.should_use_create2_factory(&mut ctx.evm, &mut inputs) {
return old_handle(ctx, inputs);
}
let gas_limit = inputs.gas_limit;
// Get CREATE2 deployer.
let create2_deployer = get_create2_deployer();
// Generate call inputs for CREATE2 factory.
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);
// Push data about current override to the stack.
create2_overrides_inner
.borrow_mut()
.push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
// Sanity check that CREATE2 deployer exists.
let code_hash = ctx.evm.load_account(create2_deployer)?.info.code_hash;
if code_hash == KECCAK_EMPTY {

seems it's hard to pass the cli argument into create2 schema, else maybe we need to adjust it in revm's codebase.

we can add a helper to InspectorExt to get the deployer address and then just configureInspectorStack with it

Thanks for the advice, I'll have a try, cli argument is much more implicit than environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants