diff --git a/Cargo.lock b/Cargo.lock index 8a83c505f..848c000df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1857,7 +1857,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.11.0", "lazy_static", "lazycell", "log", @@ -5011,6 +5011,7 @@ dependencies = [ "tracing-subscriber", "tracing-tracy", "yansi 1.0.1", + "zksync-web3-rs", ] [[package]] @@ -5083,7 +5084,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.11.1" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#b2aca7f87e0484d1e202d77a4ada8b46b059da6d" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#e3f44379f6bfb3c50e3a69acbeaa8f6da0cb4aae" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5123,7 +5124,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.11.1" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#b2aca7f87e0484d1e202d77a4ada8b46b059da6d" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#e3f44379f6bfb3c50e3a69acbeaa8f6da0cb4aae" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -5133,7 +5134,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.11.1" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#b2aca7f87e0484d1e202d77a4ada8b46b059da6d" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#e3f44379f6bfb3c50e3a69acbeaa8f6da0cb4aae" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5156,7 +5157,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.11.1" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#b2aca7f87e0484d1e202d77a4ada8b46b059da6d" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#e3f44379f6bfb3c50e3a69acbeaa8f6da0cb4aae" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5170,7 +5171,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-zksolc" version = "0.11.1" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#b2aca7f87e0484d1e202d77a4ada8b46b059da6d" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#e3f44379f6bfb3c50e3a69acbeaa8f6da0cb4aae" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5191,7 +5192,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.11.1" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#b2aca7f87e0484d1e202d77a4ada8b46b059da6d" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.1#e3f44379f6bfb3c50e3a69acbeaa8f6da0cb4aae" dependencies = [ "alloy-primitives", "cfg-if 1.0.0", 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/common/src/compile.rs b/crates/common/src/compile.rs index e0d89f914..47c25844b 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -299,7 +299,7 @@ impl ProjectCompiler { let files = self.files.clone(); { - let zksolc_version = ZkSolc::new(project.compiler.zksolc.clone()).version()?; + let zksolc_version = ZkSolc::get_version_for_path(&project.compiler.zksolc)?; Report::new(SpinnerReporter::spawn_with(format!("Using zksolc-{zksolc_version}"))); } self.zksync_compile_with(&project.paths.root, || { 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/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index 0ce7571a3..84013560b 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -180,7 +180,7 @@ Diagnostics: {diags}", ) -> Result<()> { let solc_version = strip_build_meta(compiler_version.solc.clone()); let zksolc_version = strip_build_meta(compiler_version.zksolc.clone()); - let zksolc = ZkSolc::find_installed_version(&zksolc_version)? + let zksolc_path = ZkSolc::find_installed_version(&zksolc_version)? .unwrap_or(ZkSolc::blocking_install(&solc_version)?); let input = ZkSolcVersionedInput { @@ -202,9 +202,10 @@ Diagnostics: {diags}", SolcCompiler::Specific(solc) }; - let zksolc_compiler = ZkSolcCompiler { zksolc: zksolc.zksolc, solc: solc_compiler }; + let zksolc_compiler = ZkSolcCompiler { zksolc: zksolc_path, solc: solc_compiler }; + let zksolc = zksolc_compiler.zksolc(&input)?; - let out = zksolc_compiler.zksync_compile(&input)?; + let out = zksolc.compile(&input.input)?; if out.has_error() { let mut o = ZkAggregatedCompilerOutput::default(); o.extend(solc_version, raw_build_info_new(&input, &out, false)?, out); diff --git a/crates/verify/src/zk_provider.rs b/crates/verify/src/zk_provider.rs index 38c30944f..674c231c3 100644 --- a/crates/verify/src/zk_provider.rs +++ b/crates/verify/src/zk_provider.rs @@ -8,7 +8,7 @@ use foundry_compilers::{ compilers::CompilerSettings, resolver::parse::SolData, solc::{Solc, SolcCompiler}, - zksolc::{ZkSolc, ZkSolcCompiler}, + zksolc::{self, ZkSolc, ZkSolcCompiler}, zksync::artifact_output::zk::ZkArtifactOutput, Graph, Project, }; @@ -43,13 +43,11 @@ impl ZkVerificationContext { let mut project = foundry_zksync_compiler::config_create_project(&config, config.cache, false)?; project.no_artifacts = true; - let zksolc_version = ZkSolc::new(project.compiler.zksolc.clone()).version()?; - let mut is_zksync_solc = false; + let zksolc_version = ZkSolc::get_version_for_path(&project.compiler.zksolc)?; - let solc_version = if let Some(solc) = &config.zksync.solc_path { - let solc = Solc::new(solc)?; - //TODO: determine if this solc is zksync or not - solc.version + let (solc_version, is_zksync_solc) = if let Some(solc) = &config.zksync.solc_path { + let solc_type_and_version = zksolc::get_solc_version_info(solc)?; + (solc_type_and_version.version, solc_type_and_version.zksync_version.is_some()) } else { //if there's no `solc_path` specified then we use the same // as the project version @@ -64,8 +62,7 @@ impl ZkVerificationContext { let solc = Solc::new_with_version(solc_path, context_solc_version.clone()); project.compiler.solc = SolcCompiler::Specific(solc); - is_zksync_solc = true; - context_solc_version + (context_solc_version, true) }; let compiler_version = diff --git a/crates/zksync/compiler/src/lib.rs b/crates/zksync/compiler/src/lib.rs index 67a0fa89b..194ed318c 100644 --- a/crates/zksync/compiler/src/lib.rs +++ b/crates/zksync/compiler/src/lib.rs @@ -6,15 +6,10 @@ /// ZKSolc specific logic. mod zksolc; -use std::{ - path::{Path, PathBuf}, - process::{Command, Stdio}, - str::FromStr, -}; +use std::path::PathBuf; use foundry_config::{Config, SkipBuildFilters, SolcReq}; use semver::Version; -use tracing::{debug, trace}; pub use zksolc::*; pub mod libraries; @@ -23,7 +18,7 @@ use foundry_compilers::{ artifacts::Severity, error::SolcError, solc::{Solc, SolcCompiler, SolcLanguage}, - zksolc::{ZkSolc, ZkSolcCompiler, ZkSolcSettings}, + zksolc::{get_solc_version_info, ZkSolc, ZkSolcCompiler, ZkSolcSettings}, zksync::artifact_output::zk::ZkArtifactOutput, Project, ProjectBuilder, ProjectPathsConfig, }; @@ -87,9 +82,7 @@ pub fn config_create_project( ZkSolc::blocking_install(&default_version)?; zksolc = ZkSolc::find_installed_version(&default_version)?; } - zksolc - .map(|c| c.zksolc) - .unwrap_or_else(|| panic!("Could not install zksolc v{}", default_version)) + zksolc.unwrap_or_else(|| panic!("Could not install zksolc v{}", default_version)) } else { "zksolc".into() }; @@ -116,7 +109,7 @@ fn config_solc_compiler(config: &Config) -> Result { if !path.is_file() { return Err(SolcError::msg(format!("`solc` {} does not exist", path.display()))) } - let version = solc_version(path)?; + let version = get_solc_version_info(path)?.version; let solc = Solc::new_with_version(path, Version::new(version.major, version.minor, version.patch)); return Ok(SolcCompiler::Specific(solc)) @@ -143,7 +136,7 @@ fn config_solc_compiler(config: &Config) -> Result { if !path.is_file() { return Err(SolcError::msg(format!("`solc` {} does not exist", path.display()))) } - let version = solc_version(path)?; + let version = get_solc_version_info(path)?.version; Solc::new_with_version( path, Version::new(version.major, version.minor, version.patch), @@ -197,7 +190,7 @@ pub fn config_ensure_zksolc( ZkSolc::blocking_install(version)?; zksolc = ZkSolc::find_installed_version(version)?; } - zksolc.map(|commmand| commmand.zksolc) + zksolc } SolcReq::Local(zksolc) => { if !zksolc.is_file() { @@ -215,25 +208,21 @@ pub fn config_ensure_zksolc( Ok(None) } -/// Given a solc path, get the semver. Works for both solc an zkVm solc. -// TODO: Maybe move this to compilers and use it to identify if used binary is zkVm or not -fn solc_version(path: &Path) -> Result { - let mut cmd = Command::new(path); - cmd.arg("--version").stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped()); - debug!(?cmd, "getting Solc version"); - let output = cmd.output().map_err(|e| SolcError::io(e, path))?; - trace!(?output); - if output.status.success() { - let stdout = String::from_utf8_lossy(&output.stdout); - let version = stdout - .lines() - .filter(|l| !l.trim().is_empty()) - .nth(1) - .ok_or_else(|| SolcError::msg("Version not found in Solc output"))?; - debug!(%version); - // NOTE: semver doesn't like `+` in g++ in build metadata which is invalid semver - Ok(Version::from_str(&version.trim_start_matches("Version: ").replace(".g++", ".gcc"))?) - } else { - Err(SolcError::solc_output(&output)) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn zksync_project_has_zksync_solc_when_solc_req_is_a_version() { + let config = + Config { solc: Some(SolcReq::Version(Version::new(0, 8, 26))), ..Default::default() }; + let project = config_create_project(&config, false, true).unwrap(); + let solc_compiler = project.compiler.solc; + if let SolcCompiler::Specific(path) = solc_compiler { + let version = get_solc_version_info(&path.solc).unwrap(); + assert!(version.zksync_version.is_some()); + } else { + panic!("Expected SolcCompiler::Specific"); + } } } 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)