diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index c661729b0..e4a2734b1 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -66,12 +66,19 @@ impl<'a> CowBackend<'a> { env: &mut Env, persisted_factory_deps: &mut HashMap>, factory_deps: Option>>, + paymaster_data: Option, ) -> eyre::Result { // this is a new call to inspect with a new env, so even if we've cloned the backend // already, we reset the initialized state self.is_initialized = false; - foundry_zksync_core::vm::transact(Some(persisted_factory_deps), factory_deps, env, self) + foundry_zksync_core::vm::transact( + Some(persisted_factory_deps), + factory_deps, + paymaster_data, + env, + self, + ) } /// Executes the configured transaction of the `env` without committing state changes diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index fed2ecba6..aa2fdbc76 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -840,10 +840,17 @@ impl Backend { env: &mut EnvWithHandlerCfg, persisted_factory_deps: &mut HashMap>, factory_deps: Option>>, + paymaster_data: Option, ) -> eyre::Result { self.initialize(env); - foundry_zksync_core::vm::transact(Some(persisted_factory_deps), factory_deps, env, self) + foundry_zksync_core::vm::transact( + Some(persisted_factory_deps), + factory_deps, + paymaster_data, + env, + self, + ) } /// Returns true if the address is a precompile diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 8fcd7627d..4e276d823 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -440,6 +440,7 @@ impl Executor { &mut env, &mut self.zk_persisted_factory_deps.clone(), Some(zk_tx.factory_deps.clone()), + zk_tx.paymaster_data.clone(), )? } }; @@ -464,6 +465,7 @@ impl Executor { // no need to commit them later &mut self.zk_persisted_factory_deps, Some(zk_tx.factory_deps), + zk_tx.paymaster_data, )? } }; diff --git a/crates/forge/tests/fixtures/zk/Paymaster.s.sol b/crates/forge/tests/fixtures/zk/Paymaster.s.sol new file mode 100644 index 000000000..e1647040d --- /dev/null +++ b/crates/forge/tests/fixtures/zk/Paymaster.s.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Greeter} from "../src/Greeter.sol"; +import {MyPaymaster} from "../src/MyPaymaster.sol"; + +contract PaymasterScript is Script { + MyPaymaster private paymaster; + bytes private paymaster_encoded_input; + address private alice; + + function run() public { + // random private key to get the address of alice with zero balance + alice = vm.rememberKey(0x60d80818010eb4826dc44d7342076a36978544fc89199061f452ea65d67e99e1); + require(address(alice).balance == 0, "Alice balance is not 0"); + + // We broadcast first to deploy the paymaster and fund it + vm.startBroadcast(); + paymaster = new MyPaymaster(); + (bool transferSuccess,) = address(paymaster).call{value: 10 ether}(""); + require(transferSuccess, "Paymaster funding failed"); + vm.stopBroadcast(); + + // Encode paymaster input + paymaster_encoded_input = abi.encodeWithSelector(bytes4(keccak256("general(bytes)")), bytes("0x")); + + // We broadcast the transaction from alice's account to avoid having balance + vm.startBroadcast(alice); + + (bool success,) = address(vm).call( + abi.encodeWithSignature("zkUsePaymaster(address,bytes)", address(paymaster), paymaster_encoded_input) + ); + require(success, "zkUsePaymaster call failed"); + + Greeter greeter = new Greeter(); + + vm.stopBroadcast(); + } +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 6e2f39075..e11f0a160 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -602,7 +602,6 @@ pub fn run_zk_script_test( let mut script_args = vec![ "--zk-startup", &script_path_contract, - "--broadcast", "--private-key", private_key, "--chain", diff --git a/crates/forge/tests/it/zk/cheats.rs b/crates/forge/tests/it/zk/cheats.rs index 96ca97e8e..b76c98bc1 100644 --- a/crates/forge/tests/it/zk/cheats.rs +++ b/crates/forge/tests/it/zk/cheats.rs @@ -157,7 +157,7 @@ forgetest_async!(test_zk_use_factory_dep, |prj, cmd| { "DeployCounterWithBytecodeHash", Some("transmissions11/solmate@v7 OpenZeppelin/openzeppelin-contracts cyfrin/zksync-contracts"), 2, - Some(&["-vvvvv", "--via-ir", "--system-mode", "true"]), + Some(&["-vvvvv", "--via-ir", "--system-mode", "true", "--broadcast"]), ); }); diff --git a/crates/forge/tests/it/zk/create2.rs b/crates/forge/tests/it/zk/create2.rs index f46c2dcbf..d6d30ee33 100644 --- a/crates/forge/tests/it/zk/create2.rs +++ b/crates/forge/tests/it/zk/create2.rs @@ -15,7 +15,7 @@ forgetest_async!(can_deploy_via_create2, |prj, cmd| { "Create2Script", None, 2, - Some(&["-vvvvv"]), + Some(&["-vvvvv", "--broadcast"]), ); }); diff --git a/crates/forge/tests/it/zk/deploy.rs b/crates/forge/tests/it/zk/deploy.rs index 4af3bb71e..5e298e184 100644 --- a/crates/forge/tests/it/zk/deploy.rs +++ b/crates/forge/tests/it/zk/deploy.rs @@ -11,7 +11,7 @@ forgetest_async!(multiple_deployments_of_the_same_contract, |prj, cmd| { "DeployScript", None, 3, - Some(&["-vvvvv"]), + Some(&["-vvvvv", "--broadcast"]), ); run_zk_script_test( prj.root(), @@ -20,7 +20,7 @@ forgetest_async!(multiple_deployments_of_the_same_contract, |prj, cmd| { "DeployScript", None, 3, - Some(&["-vvvvv"]), + Some(&["-vvvvv", "--broadcast"]), ); }); diff --git a/crates/forge/tests/it/zk/factory.rs b/crates/forge/tests/it/zk/factory.rs index c6940c4ac..58caefe3d 100644 --- a/crates/forge/tests/it/zk/factory.rs +++ b/crates/forge/tests/it/zk/factory.rs @@ -48,7 +48,7 @@ forgetest_async!(script_zk_can_deploy_in_method, |prj, cmd| { "ZkClassicFactoryScript", None, 2, - None, + Some(&["--broadcast"]), ); run_zk_script_test( prj.root(), @@ -57,7 +57,7 @@ forgetest_async!(script_zk_can_deploy_in_method, |prj, cmd| { "ZkNestedFactoryScript", None, 2, - None, + Some(&["--broadcast"]), ); }); @@ -70,7 +70,7 @@ forgetest_async!(script_zk_can_deploy_in_constructor, |prj, cmd| { "ZkConstructorFactoryScript", None, 1, - None, + Some(&["--broadcast"]), ); run_zk_script_test( prj.root(), @@ -79,7 +79,7 @@ forgetest_async!(script_zk_can_deploy_in_constructor, |prj, cmd| { "ZkNestedConstructorFactoryScript", None, 1, - None, + Some(&["--broadcast"]), ); }); @@ -92,7 +92,7 @@ forgetest_async!(script_zk_can_use_predeployed_factory, |prj, cmd| { "ZkUserFactoryScript", None, 3, - None, + Some(&["--broadcast"]), ); run_zk_script_test( prj.root(), @@ -101,7 +101,7 @@ forgetest_async!(script_zk_can_use_predeployed_factory, |prj, cmd| { "ZkUserConstructorFactoryScript", None, 2, - None, + Some(&["--broadcast"]), ); }); diff --git a/crates/forge/tests/it/zk/nft.rs b/crates/forge/tests/it/zk/nft.rs index cb49c99f2..0a07ea0b3 100644 --- a/crates/forge/tests/it/zk/nft.rs +++ b/crates/forge/tests/it/zk/nft.rs @@ -11,7 +11,7 @@ forgetest_async!(script_zk_can_deploy_nft, |prj, cmd| { "MyScript", Some("transmissions11/solmate@v7 OpenZeppelin/openzeppelin-contracts"), 1, - Some(&["-vvvvv"]), + Some(&["-vvvvv", "--broadcast"]), ); }); diff --git a/crates/forge/tests/it/zk/paymaster.rs b/crates/forge/tests/it/zk/paymaster.rs index 4d5ffcad2..efe4bb6be 100644 --- a/crates/forge/tests/it/zk/paymaster.rs +++ b/crates/forge/tests/it/zk/paymaster.rs @@ -1,6 +1,12 @@ //! Forge tests for zksync contracts. -use foundry_test_utils::util::{self, OutputExt}; +use foundry_test_utils::{ + forgetest_async, + util::{self, OutputExt}, + TestProject, +}; + +use crate::test_helpers::run_zk_script_test; #[tokio::test(flavor = "multi_thread")] async fn test_zk_contract_paymaster() { @@ -29,3 +35,24 @@ async fn test_zk_contract_paymaster() { cmd.args(["test", "--zk-startup", "--via-ir", "--match-contract", "TestPaymasterFlow"]); assert!(cmd.assert_success().get_output().stdout_lossy().contains("Suite result: ok")); } + +forgetest_async!(paymaster_script_test, |prj, cmd| { + setup_deploy_prj(&mut prj); + cmd.forge_fuse(); + run_zk_script_test( + prj.root(), + &mut cmd, + "./script/Paymaster.s.sol", + "PaymasterScript", + Some("OpenZeppelin/openzeppelin-contracts cyfrin/zksync-contracts"), + 3, + Some(&["-vvvvv", "--via-ir"]), + ); +}); + +fn setup_deploy_prj(prj: &mut TestProject) { + util::initialize(prj.root()); + prj.add_script("Paymaster.s.sol", include_str!("../../fixtures/zk/Paymaster.s.sol")).unwrap(); + prj.add_source("MyPaymaster.sol", include_str!("../../fixtures/zk/MyPaymaster.sol")).unwrap(); + prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); +} diff --git a/crates/forge/tests/it/zk/proxy.rs b/crates/forge/tests/it/zk/proxy.rs index 2d715ff37..b19995c69 100644 --- a/crates/forge/tests/it/zk/proxy.rs +++ b/crates/forge/tests/it/zk/proxy.rs @@ -11,7 +11,7 @@ forgetest_async!(script_zk_can_deploy_proxy, |prj, cmd| { "ProxyScript", Some("OpenZeppelin/openzeppelin-contracts"), 4, - None, + Some(&["--broadcast"]), ); }); diff --git a/crates/zksync/core/src/lib.rs b/crates/zksync/core/src/lib.rs index 80af94fcd..e371e0d5a 100644 --- a/crates/zksync/core/src/lib.rs +++ b/crates/zksync/core/src/lib.rs @@ -44,7 +44,7 @@ pub use zksync_types::{ }; use zksync_types::{utils::storage_key_for_eth_balance, U256}; pub use zksync_utils::bytecode::hash_bytecode; -use zksync_web3_rs::{ +pub use zksync_web3_rs::{ eip712::{Eip712Meta, Eip712Transaction, Eip712TransactionRequest, PaymasterParams}, zks_provider::types::Fee, zks_utils::EIP712_TX_TYPE, diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index 9b89db8c0..fbba8c1d1 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -28,6 +28,7 @@ use crate::{ pub fn transact<'a, DB>( persisted_factory_deps: Option<&'a mut HashMap>>, factory_deps: Option>>, + paymaster_data: Option, env: &'a mut Env, db: &'a mut DB, ) -> eyre::Result @@ -37,6 +38,13 @@ where { info!(calldata = ?env.tx.data, fdeps = factory_deps.as_ref().map(|deps| deps.iter().map(|dep| dep.len()).join(",")).unwrap_or_default(), "zk transact"); + let paymaster_params = PaymasterParams { + paymaster: paymaster_data.as_ref().map_or_else(Default::default, |data| data.paymaster), + paymaster_input: paymaster_data + .as_ref() + .map_or_else(Vec::new, |data| data.paymaster_input.to_vec()), + }; + let mut ecx = EvmContext::new_with_env(db, Box::new(env.clone())); let caller = env.tx.caller; let nonce = ZKVMData::new(&mut ecx).get_tx_nonce(caller); @@ -45,7 +53,7 @@ where TransactTo::Create => (CONTRACT_DEPLOYER_ADDRESS, true), }; - let (gas_limit, max_fee_per_gas) = gas_params(&mut ecx, caller, &PaymasterParams::default()); + let (gas_limit, max_fee_per_gas) = gas_params(&mut ecx, caller, &paymaster_params); debug!(?gas_limit, ?max_fee_per_gas, "tx gas parameters"); let tx = L2Tx::new( Some(transact_to), @@ -60,7 +68,7 @@ where caller.to_h160(), env.tx.value.to_u256(), factory_deps.unwrap_or_default(), - PaymasterParams::default(), + paymaster_params, ); let call_ctx = CallContext {