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

fix: Create2Factory on scripts #577

Merged
merged 15 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ use foundry_config::Config;
use foundry_evm_core::{
abi::{Vm::stopExpectSafeMemoryCall, HARDHAT_CONSOLE_ADDRESS},
backend::{DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic},
constants::{CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER_CODE},
constants::{
CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_ZKSYNC,
},
decode::decode_console_log,
utils::new_evm_with_existing_context,
InspectorExt,
Expand Down Expand Up @@ -1250,6 +1253,31 @@ impl Cheatcodes {
return None;
}

let mut factory_deps = Vec::new();

if call.target_address == DEFAULT_CREATE2_DEPLOYER && self.use_zk_vm {
call.target_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC;
call.bytecode_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC;

let (salt, init_code) = call.input.split_at(32);
let contract = self
.dual_compiled_contracts
.find_by_evm_bytecode(init_code)
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));

factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);

let constructor_input = init_code[contract.evm_bytecode.len()..].to_vec();

let create_input = foundry_zksync_core::encode_create_params(
&CreateScheme::Create2 { salt: U256::from_be_slice(salt) },
contract.zk_bytecode_hash,
constructor_input,
);

call.input = create_input.into();
}

// Handle expected calls

// Grab the different calldatas expected.
Expand Down Expand Up @@ -1374,7 +1402,11 @@ impl Cheatcodes {

let zk_tx = if self.use_zk_vm {
// We shouldn't need factory_deps for CALLs
Some(ZkTransactionMetadata { factory_deps: Default::default() })
if call.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC {
Some(ZkTransactionMetadata { factory_deps: factory_deps.clone() })
} else {
Some(ZkTransactionMetadata { factory_deps: Default::default() })
}
} else {
None
};
Expand Down Expand Up @@ -1467,7 +1499,7 @@ impl Cheatcodes {
}

if self.use_zk_vm {
if let Some(result) = self.try_call_in_zk(ecx, call, executor) {
if let Some(result) = self.try_call_in_zk(ecx, call, executor, factory_deps) {
Jrigada marked this conversation as resolved.
Show resolved Hide resolved
return Some(result);
}
}
Expand All @@ -1483,6 +1515,7 @@ impl Cheatcodes {
ecx: &mut EvmContext<DB>,
call: &mut CallInputs,
executor: &mut impl CheatcodesExecutor,
factory_deps: Vec<Vec<u8>>,
) -> Option<CallOutcome>
where
DB: DatabaseExt,
Expand Down Expand Up @@ -1515,7 +1548,7 @@ impl Cheatcodes {
// We currently exhaust the entire gas for the call as zkEVM returns a very high amount
// of gas that OOGs revm.
let gas = Gas::new(call.gas_limit);
match foundry_zksync_core::vm::call::<_, DatabaseError>(call, ecx, ccx) {
match foundry_zksync_core::vm::call::<_, DatabaseError>(call, factory_deps, ecx, ccx) {
Ok(result) => {
// append console logs from zkEVM to the current executor's LogTracer
result.logs.iter().filter_map(decode_console_log).for_each(|decoded_log| {
Expand Down
6 changes: 6 additions & 0 deletions crates/evm/core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ pub const DEFAULT_CREATE2_DEPLOYER_DEPLOYER: Address =
address!("3fAB184622Dc19b6109349B94811493BF2a45362");
/// The default CREATE2 deployer.
pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920ca78fbf26c0b4956c");

/// The default CREATE2 deployer for zkSync (0x0000000000000000000000000000000000010000)
/// See: https://github.com/zkSync-Community-Hub/zksync-developers/discussions/519
pub const DEFAULT_CREATE2_DEPLOYER_ZKSYNC: Address =
address!("0000000000000000000000000000000000010000");

/// The initcode of the default CREATE2 deployer.
pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3");
/// The runtime code of the default CREATE2 deployer.
Expand Down
34 changes: 23 additions & 11 deletions crates/evm/core/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
pub use crate::ic::*;
use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt};
use crate::{
constants::{DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_ZKSYNC},
InspectorExt,
};
use alloy_json_abi::{Function, JsonAbi};
use alloy_primitives::{Address, Selector, TxKind, U256};
use alloy_rpc_types::{Block, Transaction};
Expand Down Expand Up @@ -142,7 +145,13 @@ pub fn create2_handler_register<DB: revm::Database, I: InspectorExt<DB>>(
.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)?.0.info.code_hash;
// We check which deployer we are using to separate the logic for zkSync and original
// foundry.
let code_hash = if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER {
ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash
} else {
ctx.evm.load_account(call_inputs.target_address)?.0.info.code_hash
};
Jrigada marked this conversation as resolved.
Show resolved Hide resolved
if code_hash == KECCAK_EMPTY {
return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome {
result: InterpreterResult {
Expand Down Expand Up @@ -184,17 +193,20 @@ pub fn create2_handler_register<DB: revm::Database, I: InspectorExt<DB>>(

// Decode address from output.
let address = match outcome.instruction_result() {
return_ok!() => Address::try_from(outcome.output().as_ref())
.map_err(|_| {
outcome.result = InterpreterResult {
result: InstructionResult::Revert,
output: "invalid CREATE2 factory output".into(),
gas: Gas::new(call_inputs.gas_limit),
};
})
.ok(),
return_ok!() => {
let output = outcome.output();

if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC {
// ZkSync: Address in the last 20 bytes of a 32-byte word
Some(Address::from_slice(&output[12..32]))
Jrigada marked this conversation as resolved.
Show resolved Hide resolved
} else {
// Standard EVM: Full output as address
Some(Address::from_slice(output))
}
}
_ => None,
};

frame
.frame_data_mut()
.interpreter
Expand Down
27 changes: 27 additions & 0 deletions crates/forge/tests/fixtures/zk/Create2.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import {Script} from "forge-std/Script.sol";
import {Greeter} from "../src/Greeter.sol";

contract Create2Script is Script {
function run() external {
(bool success,) = address(vm).call(abi.encodeWithSignature("zkVm(bool)", true));
require(success, "zkVm() call failed");

vm.startBroadcast();

// Deploy Greeter using create2 with a salt
bytes32 greeterSalt = bytes32("12345");
Greeter greeter = new Greeter{salt: greeterSalt}();

// Verify Greeter deployment
require(address(greeter) != address(0), "Greeter deployment failed");
Jrigada marked this conversation as resolved.
Show resolved Hide resolved

// Test Greeter functionality
string memory greeting = greeter.greeting("Alice");
require(bytes(greeting).length > 0, "Greeter greeting failed");

vm.stopBroadcast();
}
}
22 changes: 22 additions & 0 deletions crates/forge/tests/it/zk/create2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use foundry_test_utils::{forgetest_async, util, TestProject};

use crate::test_helpers::run_zk_script_test;

forgetest_async!(can_deploy_via_create2, |prj, cmd| {
setup_create2_prj(&mut prj);
run_zk_script_test(
prj.root(),
&mut cmd,
"./script/Create2.s.sol",
"Create2Script",
None,
2,
Some(&["-vvvvv"]),
);
});

fn setup_create2_prj(prj: &mut TestProject) {
util::initialize(prj.root());
prj.add_script("Create2.s.sol", include_str!("../../fixtures/zk/Create2.s.sol")).unwrap();
prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap();
}
3 changes: 2 additions & 1 deletion crates/forge/tests/it/zk/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ forgetest_async!(multiple_deployments_of_the_same_contract, |prj, cmd| {
fn setup_deploy_prj(prj: &mut TestProject) {
Jrigada marked this conversation as resolved.
Show resolved Hide resolved
util::initialize(prj.root());
prj.add_script("Deploy.s.sol", include_str!("../../fixtures/zk/Deploy.s.sol")).unwrap();
prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap();
prj.add_source("CustomNumber.sol", include_str!("../../../../../testdata/zk/CustomNumber.sol"))
Jrigada marked this conversation as resolved.
Show resolved Hide resolved
.unwrap();
}
1 change: 1 addition & 0 deletions crates/forge/tests/it/zk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod basic;
mod cheats;
mod contracts;
mod create;
mod create2;
mod deploy;
mod factory;
mod factory_deps;
Expand Down
3 changes: 2 additions & 1 deletion crates/zksync/core/src/vm/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ where
/// Executes a CALL opcode on the ZK-VM.
pub fn call<DB, E>(
call: &CallInputs,
factory_deps: Vec<Vec<u8>>,
ecx: &mut EvmContext<DB>,
mut ccx: CheatcodeTracerContext,
) -> ZKVMResult<E>
Expand Down Expand Up @@ -200,7 +201,7 @@ where
CallValue::Transfer(value) => value.to_u256(),
_ => U256::zero(),
},
Default::default(),
factory_deps,
PaymasterParams::default(),
);

Expand Down
Loading