diff --git a/Cargo.lock b/Cargo.lock index 43c7ff61c..21981a525 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4765,6 +4765,7 @@ dependencies = [ "foundry-config", "foundry-evm", "foundry-test-utils", + "foundry-zksync-compiler", "futures 0.3.30", "itertools 0.13.0", "once_cell", diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 1631215ed..d220ceff3 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -286,6 +286,7 @@ impl TestArgs { zk_compiler.zksync_compile(&zk_project, config.zksync.avoid_contracts())?; let dual_compiled_contracts = DualCompiledContracts::new(&output, &zk_output, &project.paths); + (Some(zk_output), Some(dual_compiled_contracts)) } else { (None, None) diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index f308381d4..29cd75770 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -41,6 +41,9 @@ once_cell.workspace = true yansi.workspace = true itertools.workspace = true +# zk +foundry-zksync-compiler.workspace = true + [dev-dependencies] tokio = { workspace = true, features = ["macros"] } foundry-test-utils.workspace = true diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index d7cfd0156..785643315 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -14,6 +14,9 @@ use foundry_compilers::{ input::{ZkSolcInput, ZkSolcVersionedInput}, ZkSolc, }, + zksync::{ + compile::output::AggregatedCompilerOutput as ZkAggregatedCompilerOutput, raw_build_info_new, + }, AggregatedCompilerOutput, }; use semver::{BuildMetadata, Version}; @@ -84,8 +87,12 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { if !args.force { // solc dry run of flattened code - self.check_flattened(source.clone(), &context.compiler_version, &context.target_path) - .map_err(|err| { + self.zk_check_flattened( + source.clone(), + &context.compiler_version, + &context.target_path, + ) + .map_err(|err| { eyre::eyre!( "Failed to compile the flattened code locally: `{}`\ To skip this solc dry, have a look at the `--force` flag of this command.", @@ -178,7 +185,7 @@ Diagnostics: {diags}", sources: BTreeMap::from([("contract.sol".into(), Source::new(content))]), ..Default::default() }, - solc_version: version, + solc_version: version.clone(), allow_paths: Default::default(), base_path: Default::default(), include_paths: Default::default(), @@ -186,9 +193,8 @@ Diagnostics: {diags}", let out = zksolc.compile(&mut input)?; if out.has_error() { - let o = AggregatedCompilerOutput::::default(); - // TODO: RawBuildInfo cannot accept zksolc's CompilerOutput - // o.extend(version.clone(), RawBuildInfo::new(&input, &out, false)?, out); + let mut o = ZkAggregatedCompilerOutput::default(); + o.extend(version.clone(), raw_build_info_new(&input, &out, false)?, out); let diags = o.diagnostics(&[], &[], Default::default()); eyre::bail!( diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index aa9ed2554..25eba2e50 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -373,38 +373,7 @@ impl EtherscanVerificationProvider { return Ok(None); } - //TODO: remove when foundry-compilers zksolc detection is fixed for 1.5.0 - let get_zksolc_compiler_version = |path: &std::path::Path| -> Result { - use std::process::*; - let mut cmd = Command::new(path); - cmd.arg("--version") - .stdin(Stdio::piped()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()); - debug!(?cmd, "getting ZkSolc version"); - let output = cmd.output().wrap_err("error retrieving --version for zksolc")?; - - if output.status.success() { - let stdout = String::from_utf8_lossy(&output.stdout); - let version = stdout - .lines() - .filter(|l| !l.trim().is_empty()) - .last() - .ok_or(eyre!("Version not found in zksolc output"))?; - Ok(Version::from_str( - version - .split_whitespace() - .find(|s| s.starts_with('v')) - .ok_or(eyre!("Unable to retrieve version from zksolc output"))? - .trim_start_matches('v'), - )?) - } else { - Err(eyre!("zkSolc error: {}", String::from_utf8_lossy(&output.stderr))) - .wrap_err("Error retrieving zksolc version with --version") - } - }; - - let zksolc = get_zksolc_compiler_version(context.project.zksync_zksolc.zksolc.as_ref())?; + let zksolc = context.project.zksync_zksolc.version()?; let mut is_zksync_solc = false; let solc = if let Some(solc) = &context.config.zksync.solc_path { @@ -414,7 +383,7 @@ impl EtherscanVerificationProvider { Some(version) } else { //if there's no `solc_path` specified then we use the same - // as the project version, but the zksync forc + // as the project version, but the zksync fork is_zksync_solc = true; Some(context.compiler_version.clone()) }; @@ -473,6 +442,7 @@ impl EtherscanVerificationProvider { &context.config, )?; + //TODO: zk support let creation_data = client.contract_creation_data(args.address).await?; let transaction = provider .get_transaction_by_hash(creation_data.transaction_hash) diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index 67e93c0ca..615f70367 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -2,7 +2,7 @@ use super::{EtherscanSourceProvider, VerifyArgs}; use crate::provider::VerificationContext; use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; -use foundry_compilers::artifacts::StandardJsonCompilerInput; +use foundry_compilers::{artifacts::StandardJsonCompilerInput, ArtifactOutput, Compiler, Project}; #[derive(Debug)] pub struct EtherscanStandardJsonSource; @@ -53,26 +53,9 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { _args: &VerifyArgs, context: &VerificationContext, ) -> Result<(String, String, CodeFormat)> { - let mut input: StandardJsonCompilerInput = context - .project - // TODO this method does not exist on compilers - // .zksync_standard_json_input(&context.target_path) - .standard_json_input(&context.target_path) - .wrap_err("Failed to get zksync standard json input")? - .normalize_evm_version(&context.compiler_version); - - input.settings.libraries.libs = input - .settings - .libraries - .libs - .into_iter() - .map(|(f, libs)| { - (f.strip_prefix(context.project.root()).unwrap_or(&f).to_path_buf(), libs) - }) - .collect(); - - // remove all incompatible settings - input.settings.sanitize(&context.compiler_version); + let input = + foundry_zksync_compiler::standard_json_input(&context.project, &context.target_path) + .wrap_err("failed to get zksolc standard json")?; let source = serde_json::to_string(&input).wrap_err("Failed to parse zksync standard json input")?; diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index a9f7b0d3d..8022607b4 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -310,6 +310,7 @@ impl VerifyArgs { output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())), ); + //TODO: lookup for zksync let Some((artifact_id, _)) = contracts.find_by_deployed_code_exact(&code) else { eyre::bail!(format!( "Bytecode at {} does not match any local contracts", diff --git a/crates/zksync/compiler/src/lib.rs b/crates/zksync/compiler/src/lib.rs index 7d7851619..44d4bcbb1 100644 --- a/crates/zksync/compiler/src/lib.rs +++ b/crates/zksync/compiler/src/lib.rs @@ -6,6 +6,8 @@ /// ZKSolc specific logic. mod zksolc; +use std::path::Path; + use foundry_config::{Config, SkipBuildFilters, SolcReq}; pub use zksolc::*; @@ -13,7 +15,7 @@ pub mod libraries; use foundry_compilers::{ artifacts::Severity, error::SolcError, solc::SolcCompiler, zksolc::ZkSolc, - zksync::config::ZkSolcConfig, Project, ProjectBuilder, + zksync::config::ZkSolcConfig, Compiler, Project, ProjectBuilder, }; /// Ensures that the configured version is installed if explicitly set @@ -113,3 +115,30 @@ pub fn create_project( Ok(project) } + +/// Obtain a standard json input for zksolc +pub fn standard_json_input( + project: &Project, + target_path: impl AsRef, +) -> Result +where + C::Settings: Into, +{ + let mut input = project.standard_json_input(target_path)?; + tracing::debug!(?input.settings.remappings, "standard_json_input for zksync"); + + let mut settings = project.zksync_zksolc_config.settings.clone(); + settings.remappings = std::mem::take(&mut input.settings.remappings); + settings.libraries.libs = settings + .libraries + .libs + .into_iter() + .map(|(f, libs)| (f.strip_prefix(project.root()).unwrap_or(&f).to_path_buf(), libs)) + .collect(); + let settings = serde_json::to_value(settings).expect("able to serialize settings as json"); + + let mut serialized = serde_json::to_value(input).expect("able to serialize input as json"); + serialized["settings"] = settings; + + Ok(serialized) +}