diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 34cfa99d5..a39ec8804 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -953,22 +953,22 @@ impl Cheatcodes { let mut zk_tx = if self.use_zk_vm { to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS.to_address())); nonce = foundry_zksync_core::nonce(broadcast.new_origin, ecx_inner) as u64; - let contract = self + let init_code = input.init_code(); + let find_contract = self .dual_compiled_contracts - .find_by_evm_bytecode(&input.init_code().0) - .unwrap_or_else(|| { - panic!("failed finding contract for {:?}", input.init_code()) - }); + .find_bytecode(&init_code.0) + .unwrap_or_else(|| panic!("failed finding contract for {init_code:?}")); + + let constructor_args = find_contract.constructor_args(); + let contract = find_contract.contract(); + let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract); - let constructor_input = - call_init_code[contract.evm_bytecode.len()..].to_vec(); - let create_input = foundry_zksync_core::encode_create_params( &input.scheme().unwrap_or(CreateScheme::Create), contract.zk_bytecode_hash, - constructor_input, + constructor_args.to_vec(), ); call_init_code = Bytes::from(create_input); @@ -1107,20 +1107,30 @@ impl Cheatcodes { } } - if input.init_code().0 == DEFAULT_CREATE2_DEPLOYER_CODE { + let init_code = input.init_code(); + if init_code.0 == DEFAULT_CREATE2_DEPLOYER_CODE { info!("running create in EVM, instead of zkEVM (DEFAULT_CREATE2_DEPLOYER_CODE)"); return None } info!("running create in zkEVM"); - let zk_contract = self + let find_contract = self .dual_compiled_contracts - .find_by_evm_bytecode(&input.init_code().0) - .unwrap_or_else(|| panic!("failed finding contract for {:?}", input.init_code())); + .find_bytecode(&init_code.0) + .unwrap_or_else(|| panic!("failed finding contract for {init_code:?}")); + + let constructor_args = find_contract.constructor_args(); + let contract = find_contract.contract(); - let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(zk_contract); - tracing::debug!(contract = zk_contract.name, "using dual compiled contract"); + let zk_create_input = foundry_zksync_core::encode_create_params( + &input.scheme().unwrap_or(CreateScheme::Create), + contract.zk_bytecode_hash, + constructor_args.to_vec(), + ); + + let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract); + tracing::debug!(contract = contract.name, "using dual compiled contract"); let ccx = foundry_zksync_core::vm::CheatcodeTracerContext { mocked_calls: self.mocked_calls.clone(), @@ -1129,22 +1139,15 @@ impl Cheatcodes { persisted_factory_deps: Some(&mut self.persisted_factory_deps), paymaster_data: self.paymaster_params.take(), }; - let create_inputs = CreateInputs { - scheme: input.scheme().unwrap_or(CreateScheme::Create), - init_code: input.init_code(), - value: input.value(), - caller: input.caller(), - gas_limit: input.gas_limit(), + let zk_create = foundry_zksync_core::vm::ZkCreateInputs { + value: input.value().to_u256(), + msg_sender: input.caller(), + create_input: zk_create_input, + factory_deps, }; let mut gas = Gas::new(input.gas_limit()); - match foundry_zksync_core::vm::create::<_, DatabaseError>( - &create_inputs, - zk_contract, - factory_deps, - ecx, - ccx, - ) { + match foundry_zksync_core::vm::create::<_, DatabaseError>(zk_create, ecx, ccx) { Ok(result) => { if let Some(recorded_logs) = &mut self.recorded_logs { recorded_logs.extend(result.logs.clone().into_iter().map(|log| Vm::Log { @@ -1161,8 +1164,8 @@ impl Cheatcodes { state: self, ecx: &mut ecx.inner, precompiles: &mut ecx.precompiles, - gas_limit: create_inputs.gas_limit, - caller: create_inputs.caller, + gas_limit: input.gas_limit(), + caller: input.caller(), }, decoded_log, ); @@ -1436,19 +1439,20 @@ impl Cheatcodes { call.bytecode_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC; let (salt, init_code) = call.input.split_at(32); - let contract = self + let find_contract = self .dual_compiled_contracts - .find_by_evm_bytecode(init_code) + .find_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_args = find_contract.constructor_args(); + let contract = find_contract.contract(); - let constructor_input = init_code[contract.evm_bytecode.len()..].to_vec(); + factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract); let create_input = foundry_zksync_core::encode_create_params( &CreateScheme::Create2 { salt: U256::from_be_slice(salt) }, contract.zk_bytecode_hash, - constructor_input, + constructor_args.to_vec(), ); call.input = create_input.into(); diff --git a/crates/zksync/compiler/src/zksolc/mod.rs b/crates/zksync/compiler/src/zksolc/mod.rs index 704b228c0..52f0c21be 100644 --- a/crates/zksync/compiler/src/zksolc/mod.rs +++ b/crates/zksync/compiler/src/zksolc/mod.rs @@ -43,6 +43,28 @@ pub struct DualCompiledContract { pub evm_bytecode: Vec, } +/// Couple contract type with contract and init code +pub struct FindBytecodeResult<'a> { + r#type: ContractType, + contract: &'a DualCompiledContract, + init_code: &'a [u8], +} + +impl<'a> FindBytecodeResult<'a> { + /// Retrieve the found contract + pub fn contract(self) -> &'a DualCompiledContract { + self.contract + } + + /// Retrieve the correct constructor args + pub fn constructor_args(&self) -> &'a [u8] { + match self.r#type { + ContractType::ZK => &self.init_code[self.contract.zk_deployed_bytecode.len()..], + ContractType::EVM => &self.init_code[self.contract.evm_bytecode.len()..], + } + } +} + /// A collection of `[DualCompiledContract]`s #[derive(Debug, Default, Clone)] pub struct DualCompiledContracts { @@ -195,6 +217,32 @@ impl DualCompiledContracts { self.contracts.iter().find(|contract| code_hash == contract.zk_bytecode_hash) } + /// Find a contract matching the given bytecode, whether it's EVM or ZK. + /// + /// Will prioritize longest match + pub fn find_bytecode<'a: 'b, 'b>( + &'a self, + init_code: &'b [u8], + ) -> Option> { + let evm = self.find_by_evm_bytecode(init_code).map(|evm| (ContractType::EVM, evm)); + let zk = self.find_by_zk_deployed_bytecode(init_code).map(|evm| (ContractType::ZK, evm)); + + match (&evm, &zk) { + (Some((_, evm)), Some((_, zk))) => { + if zk.zk_deployed_bytecode.len() >= evm.evm_bytecode.len() { + Some(FindBytecodeResult { r#type: ContractType::ZK, contract: zk, init_code }) + } else { + Some(FindBytecodeResult { r#type: ContractType::EVM, contract: zk, init_code }) + } + } + _ => evm.or(zk).map(|(r#type, contract)| FindBytecodeResult { + r#type, + contract, + init_code, + }), + } + } + /// Finds a contract own and nested factory deps pub fn fetch_all_factory_deps(&self, root: &DualCompiledContract) -> Vec> { let mut visited = HashSet::new(); diff --git a/crates/zksync/core/Cargo.toml b/crates/zksync/core/Cargo.toml index 49116a36f..e3532b466 100644 --- a/crates/zksync/core/Cargo.toml +++ b/crates/zksync/core/Cargo.toml @@ -15,7 +15,6 @@ exclude.workspace = true foundry-common.workspace = true foundry-evm-abi.workspace = true foundry-cheatcodes-common.workspace = true -foundry-zksync-compiler.workspace = true alloy-primitives.workspace = true alloy-signer.workspace = true alloy-network.workspace = true diff --git a/crates/zksync/core/src/vm/mod.rs b/crates/zksync/core/src/vm/mod.rs index 442937b5c..30fca4ad4 100644 --- a/crates/zksync/core/src/vm/mod.rs +++ b/crates/zksync/core/src/vm/mod.rs @@ -10,5 +10,7 @@ pub use farcall::{SELECTOR_CONTRACT_DEPLOYER_CREATE, SELECTOR_CONTRACT_DEPLOYER_ pub use inspect::{ batch_factory_dependencies, inspect, inspect_as_batch, ZKVMExecutionResult, ZKVMResult, }; -pub use runner::{balance, call, code_hash, create, encode_create_params, nonce, transact}; +pub use runner::{ + balance, call, code_hash, create, encode_create_params, nonce, transact, ZkCreateInputs, +}; pub use tracers::cheatcode::CheatcodeTracerContext; diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index 5bb099765..e7c9dd63a 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -1,8 +1,7 @@ use alloy_primitives::hex; -use foundry_zksync_compiler::DualCompiledContract; use itertools::Itertools; use revm::{ - interpreter::{CallInputs, CallScheme, CallValue, CreateInputs}, + interpreter::{CallInputs, CallScheme, CallValue}, primitives::{Address, CreateScheme, Env, ResultAndState, TransactTo, B256, U256 as rU256}, Database, EvmContext, InnerEvmContext, }; @@ -116,11 +115,23 @@ where ZKVMData::new(ecx).get_tx_nonce(address).0 } -/// Executes a CREATE opcode on the ZK-VM. +/// EraVM equivalent of [`CreateInputs`] +pub struct ZkCreateInputs { + /// The current `msg.sender` + pub msg_sender: Address, + /// The encoded calldata input for `CONTRACT_DEPLOYER` + pub create_input: Vec, + /// Factory deps for the contract we are deploying + pub factory_deps: Vec>, + /// Value specified for the deployment + pub value: U256, +} + +/// Executes a CREATE opcode on the EraVM. +/// +/// * `call.init_code` should be valid EraVM's ContractDeployer input pub fn create( - call: &CreateInputs, - contract: &DualCompiledContract, - factory_deps: Vec>, + inputs: ZkCreateInputs, ecx: &mut EvmContext, mut ccx: CheatcodeTracerContext, ) -> ZKVMResult @@ -128,10 +139,10 @@ where DB: Database, ::Error: Debug, { - info!(?call, "create tx {}", hex::encode(&call.init_code)); - let constructor_input = call.init_code[contract.evm_bytecode.len()..].to_vec(); + let ZkCreateInputs { create_input, factory_deps, value, msg_sender } = inputs; + + info!("create tx {}", hex::encode(&create_input)); let caller = ecx.env.tx.caller; - let calldata = encode_create_params(&call.scheme, contract.zk_bytecode_hash, constructor_input); let nonce = ZKVMData::new(ecx).get_tx_nonce(caller); let paymaster_params = if let Some(paymaster_data) = &ccx.paymaster_data { @@ -148,7 +159,7 @@ where let tx = L2Tx::new( Some(CONTRACT_DEPLOYER_ADDRESS), - calldata, + create_input, nonce, Fee { gas_limit, @@ -157,14 +168,14 @@ where gas_per_pubdata_limit: U256::from(20000), }, caller.to_h160(), - call.value.to_u256(), + value, factory_deps, paymaster_params, ); let call_ctx = CallContext { tx_caller: ecx.env.tx.caller, - msg_sender: call.caller, + msg_sender, contract: CONTRACT_DEPLOYER_ADDRESS.to_address(), delegate_as: None, block_number: ecx.env.block.number,