diff --git a/Cargo.lock b/Cargo.lock index 0bcc73c0e..848c000df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5011,6 +5011,7 @@ dependencies = [ "tracing-subscriber", "tracing-tracy", "yansi 1.0.1", + "zksync-web3-rs", ] [[package]] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index e8690a0a7..aed78433e 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,6 +19,7 @@ foundry-config.workspace = true foundry-debugger.workspace = true foundry-evm.workspace = true foundry-wallets.workspace = true +zksync-web3-rs.workspace = true foundry-compilers = { workspace = true, features = ["full"] } diff --git a/crates/cli/src/opts/build/zksync.rs b/crates/cli/src/opts/build/zksync.rs index de2c44c7b..acb4891a9 100644 --- a/crates/cli/src/opts/build/zksync.rs +++ b/crates/cli/src/opts/build/zksync.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use clap::Parser; use foundry_config::ZkSyncConfig; use serde::Serialize; +use zksync_web3_rs::types::{Address, Bytes}; #[derive(Clone, Debug, Default, Serialize, Parser)] #[clap(next_help_heading = "ZKSync configuration")] @@ -104,6 +105,22 @@ pub struct ZkSyncArgs { /// Contracts to avoid compiling on zkSync #[clap(long = "zk-avoid-contracts", visible_alias = "avoid-contracts", value_delimiter = ',')] pub avoid_contracts: Option>, + + /// Paymaster address + #[clap( + long = "zk-paymaster-address", + value_name = "PAYMASTER_ADDRESS", + visible_alias = "paymaster-address" + )] + pub paymaster_address: Option
, + + /// Paymaster input + #[clap( + long = "zk-paymaster-input", + value_name = "PAYMASTER_INPUT", + visible_alias = "paymaster-input" + )] + pub paymaster_input: Option, } impl ZkSyncArgs { diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 155c7c932..3c0a77e7f 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -43,6 +43,7 @@ use std::{ sync::Arc, }; use zksync_types::H256; +use zksync_web3_rs::eip712::PaymasterParams; merge_impl_figment_convert!(CreateArgs, opts, eth); @@ -114,6 +115,7 @@ pub struct ZkSyncData { bytecode: Vec, bytecode_hash: H256, factory_deps: Vec>, + paymaster_params: Option, } impl CreateArgs { @@ -125,6 +127,22 @@ impl CreateArgs { let zksync = self.opts.compiler.zk.enabled(); if zksync { + let paymaster_params = + if let Some(paymaster_address) = self.opts.compiler.zk.paymaster_address { + Some(PaymasterParams { + paymaster: paymaster_address, + paymaster_input: self + .opts + .compiler + .zk + .paymaster_input + .clone() + .unwrap_or_default() + .to_vec(), + }) + } else { + None + }; let target_path = if let Some(ref mut path) = self.contract.path { canonicalize(project.root().join(path))? } else { @@ -223,7 +241,7 @@ impl CreateArgs { visited_bytecodes.insert(bytecode.clone()); visited_bytecodes.into_iter().collect() }; - let zk_data = ZkSyncData { bytecode, bytecode_hash, factory_deps }; + let zk_data = ZkSyncData { bytecode, bytecode_hash, factory_deps, paymaster_params }; let result = if self.unlocked { // Deploy with unlocked account @@ -555,7 +573,7 @@ impl CreateArgs { let is_args_empty = args.is_empty(); let mut deployer = factory.deploy_tokens_zk(args.clone(), &zk_data).context("failed to deploy contract") - .map(|deployer| deployer.set_zk_factory_deps(zk_data.factory_deps.clone())).map_err(|e| { + .map(|deployer| deployer.set_zk_factory_deps(zk_data.factory_deps.clone()).set_zk_paymaster_params(zk_data.paymaster_params.clone())).map_err(|e| { if is_args_empty { e.wrap_err("no arguments provided for contract constructor; consider --constructor-args or --constructor-args-path") } else { @@ -777,6 +795,7 @@ pub struct Deployer { confs: usize, timeout: u64, zk_factory_deps: Option>>, + zk_paymaster_params: Option, _p: PhantomData

, _t: PhantomData, } @@ -793,6 +812,7 @@ where confs: self.confs, timeout: self.timeout, zk_factory_deps: self.zk_factory_deps.clone(), + zk_paymaster_params: self.zk_paymaster_params.clone(), _p: PhantomData, _t: PhantomData, } @@ -811,6 +831,12 @@ where self } + /// Set zksync's paymaster params. + pub fn set_zk_paymaster_params(mut self, params: Option) -> Self { + self.zk_paymaster_params = params; + self + } + /// Broadcasts the zk contract deployment transaction and after waiting for it to /// be sufficiently confirmed (default: 1), it returns a tuple with /// the [`Contract`](crate::Contract) struct at the deployed contract's address @@ -823,12 +849,12 @@ where let tx = foundry_zksync_core::new_eip712_transaction( self.tx, factory_deps, + self.zk_paymaster_params.clone(), self.client.borrow(), signer.expect("No signer was found"), ) .await .map_err(|_| ContractDeploymentError::ContractNotDeployed)?; - let receipt = self .client .borrow() @@ -975,6 +1001,7 @@ where confs: 1, timeout: self.timeout, zk_factory_deps: None, + zk_paymaster_params: None, _p: PhantomData, _t: PhantomData, }) @@ -1012,7 +1039,6 @@ where .to(foundry_zksync_core::CONTRACT_DEPLOYER_ADDRESS.to_address()) .input(data.into()), ); - Ok(Deployer { client: self.client.clone(), abi: self.abi, @@ -1020,6 +1046,7 @@ where confs: 1, timeout: self.timeout, zk_factory_deps: Some(vec![zk_data.bytecode.clone()]), + zk_paymaster_params: zk_data.paymaster_params.clone(), _p: PhantomData, _t: PhantomData, }) diff --git a/crates/zksync/core/src/lib.rs b/crates/zksync/core/src/lib.rs index a0d8a689b..1c64a4a1d 100644 --- a/crates/zksync/core/src/lib.rs +++ b/crates/zksync/core/src/lib.rs @@ -110,6 +110,7 @@ pub async fn new_eip712_transaction< >( tx: WithOtherFields, factory_deps: Vec>, + paymaster_data: Option, provider: P, signer: S, ) -> Result { @@ -126,7 +127,10 @@ pub async fn new_eip712_transaction< let gas_price = tx.gas_price.ok_or_eyre("`gas_price` cannot be empty")?; let data = tx.input.clone().into_input().unwrap_or_default(); - let custom_data = Eip712Meta::new().factory_deps(factory_deps); + let mut custom_data = Eip712Meta::new().factory_deps(factory_deps); + if let Some(params) = paymaster_data { + custom_data = custom_data.paymaster_params(params); + } let mut deploy_request = Eip712TransactionRequest::new() .r#type(EIP712_TX_TYPE)