Skip to content

Commit

Permalink
feat: add assemblycreate for warning suppression for zksolc 1.5.10 (#840
Browse files Browse the repository at this point in the history
)

* feat: add assemblycreate for error suppression for zksolc 1.5.9

* chore: remove warning and error enums in favour of era-solc dep

* chore: bump default version to 1.5.9 and set flag

* feat: bumps to 1.5.10 and updates assemblycreate to warningtype

* fix: remove missing libs logic from handling output

* chore: make use of commit 7000890 for object_format to get proper is_unlinked

* chore: run make pr for lint / formatting

* chore: make use of commit 0314731 to resolve null value error
  • Loading branch information
dutterbutter authored Jan 24, 2025
1 parent b993907 commit 62af6f9
Show file tree
Hide file tree
Showing 16 changed files with 445 additions and 189 deletions.
308 changes: 270 additions & 38 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ zksync_vm_interface = { git = "https://github.com/matter-labs/zksync-era.git", r
zksync_multivm = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
zksync_utils = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
zksync_contracts = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity.git", tag = "1.5.10", package = "era-solc" }

# macros
proc-macro2 = "1.0"
Expand Down Expand Up @@ -303,7 +304,7 @@ regex = { version = "1", default-features = false }
reqwest = { version = "0.12", default-features = false }
semver = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
serde_json = { version = "=1.0.128", features = ["arbitrary_precision"] }
similar-asserts = "1.6"
soldeer-commands = "=0.5.2"
strum = "0.26"
Expand Down
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ tracing-tracy = { version = "0.11", optional = true }

# zk
foundry-zksync-compilers.workspace = true
era-solc.workspace = true

[dev-dependencies]
tempfile.workspace = true
Expand Down
14 changes: 8 additions & 6 deletions crates/cli/src/opts/build/zksync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::{collections::HashSet, path::PathBuf};

use alloy_primitives::{hex, Address, Bytes};
use clap::Parser;
use era_solc::standard_json::input::settings::{error_type::ErrorType, warning_type::WarningType};
use foundry_config::zksync::ZkSyncConfig;
use foundry_zksync_compilers::compilers::zksolc::settings::{ZkSolcError, ZkSolcWarning};
use serde::Serialize;

#[derive(Clone, Debug, Default, Serialize, Parser)]
Expand Down Expand Up @@ -117,22 +117,24 @@ pub struct ZkSyncArgs {
/// Set the warnings to suppress for zksolc.
#[clap(
long = "zk-suppressed-warnings",
visible_alias = "suppressed-warnings",
alias = "suppressed-warnings",
visible_alias = "suppress-warnings",
value_delimiter = ',',
help = "Set the warnings to suppress for zksolc, possible values: [txorigin]"
help = "Set the warnings to suppress for zksolc, possible values: [txorigin, assemblycreate]"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub suppressed_warnings: Option<Vec<ZkSolcWarning>>,
pub suppressed_warnings: Option<Vec<WarningType>>,

/// Set the errors to suppress for zksolc.
#[clap(
long = "zk-suppressed-errors",
visible_alias = "suppressed-errors",
alias = "suppressed-errors",
visible_alias = "suppress-errors",
value_delimiter = ',',
help = "Set the errors to suppress for zksolc, possible values: [sendtransfer]"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub suppressed_errors: Option<Vec<ZkSolcError>>,
pub suppressed_errors: Option<Vec<ErrorType>>,
}

impl ZkSyncArgs {
Expand Down
76 changes: 6 additions & 70 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ use foundry_compilers::{
solc::SolcSettings,
Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, SolcConfig,
};
use foundry_zksync_compilers::{
compilers::{
artifact_output::zk::ZkArtifactOutput,
zksolc::{ZkSolc, ZkSolcCompiler},
},
libraries::{self, ZkMissingLibrary},
use foundry_zksync_compilers::compilers::{
artifact_output::zk::ZkArtifactOutput,
zksolc::{ZkSolc, ZkSolcCompiler},
};

use num_format::{Locale, ToFormattedString};
use std::{
collections::{BTreeMap, HashSet},
collections::BTreeMap,
fmt::Display,
io::IsTerminal,
path::{Path, PathBuf},
Expand Down Expand Up @@ -336,7 +333,7 @@ impl ProjectCompiler {
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, || {
self.zksync_compile_with(|| {
let files_to_compile =
if !files.is_empty() { files } else { project.paths.input_files() };
let sources = Source::read_all(files_to_compile)?;
Expand All @@ -349,7 +346,6 @@ impl ProjectCompiler {
#[instrument(target = "forge::compile", skip_all)]
fn zksync_compile_with<F>(
self,
root_path: impl AsRef<Path>,
f: F,
) -> Result<ProjectCompileOutput<ZkSolcCompiler, ZkArtifactOutput>>
where
Expand Down Expand Up @@ -394,7 +390,7 @@ impl ProjectCompiler {
sh_println!("{output}")?;
}

self.zksync_handle_output(root_path, &output)?;
self.zksync_handle_output(&output)?;
}

Ok(output)
Expand All @@ -403,71 +399,11 @@ impl ProjectCompiler {
/// If configured, this will print sizes or names
fn zksync_handle_output(
&self,
root_path: impl AsRef<Path>,
output: &ProjectCompileOutput<ZkSolcCompiler, ZkArtifactOutput>,
) -> Result<()> {
let print_names = self.print_names.unwrap_or(false);
let print_sizes = self.print_sizes.unwrap_or(false);

// Process missing libraries
// TODO: skip this if project was not compiled using --detect-missing-libraries
let mut missing_libs_unique: HashSet<String> = HashSet::new();
for (artifact_id, artifact) in output.artifact_ids() {
// TODO: when compiling specific files, the output might still add cached artifacts
// that are not part of the file list to the output, which may cause missing libraries
// error to trigger for files that were not intended to be compiled.
// This behaviour needs to be investigated better on the foundry-compilers side.
// For now we filter, checking only the files passed to compile.
let is_target_file =
self.files.is_empty() || self.files.iter().any(|f| artifact_id.path == *f);
if is_target_file {
if let Some(mls) = artifact.missing_libraries() {
missing_libs_unique.extend(mls.clone());
}
}
}

let missing_libs: Vec<ZkMissingLibrary> = missing_libs_unique
.into_iter()
.map(|ml| {
let mut split = ml.split(':');
let contract_path =
split.next().expect("Failed to extract contract path for missing library");
let contract_name =
split.next().expect("Failed to extract contract name for missing library");

let mut abs_path_buf = PathBuf::new();
abs_path_buf.push(root_path.as_ref());
abs_path_buf.push(contract_path);

let art = output.find(abs_path_buf.as_path(), contract_name).unwrap_or_else(|| {
panic!(
"Could not find contract {contract_name} at path {contract_path} for compilation output"
)
});

ZkMissingLibrary {
contract_path: contract_path.to_string(),
contract_name: contract_name.to_string(),
missing_libraries: art.missing_libraries().cloned().unwrap_or_default(),
}
})
.collect();

if !missing_libs.is_empty() {
libraries::add_dependencies_to_missing_libraries_cache(
root_path,
missing_libs.as_slice(),
)
.expect("Error while adding missing libraries");
let missing_libs_list = missing_libs
.iter()
.map(|ml| format!("{}:{}", ml.contract_path, ml.contract_name))
.collect::<Vec<String>>()
.join(", ");
eyre::bail!("Missing libraries detected: {missing_libs_list}\n\nRun the following command in order to deploy each missing library:\n\nforge create <LIBRARY> --private-key <PRIVATE_KEY> --rpc-url <RPC_URL> --chain <CHAIN_ID> --zksync\n\nThen pass the library addresses using the --libraries option");
}

// print any sizes or names
if print_names {
let mut artifacts: BTreeMap<_, Vec<_>> = BTreeMap::new();
Expand Down
1 change: 1 addition & 0 deletions crates/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ yansi.workspace = true

# zksync
foundry-zksync-compilers.workspace = true
era-solc.workspace = true

[target.'cfg(target_os = "windows")'.dependencies]
path-slash = "0.2"
Expand Down
12 changes: 6 additions & 6 deletions crates/config/src/zksync.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use era_solc::standard_json::input::settings::{error_type::ErrorType, warning_type::WarningType};
use foundry_compilers::{
artifacts::{EvmVersion, Libraries, Severity},
error::SolcError,
Expand All @@ -11,14 +12,13 @@ use foundry_zksync_compilers::{
zksolc::{
get_solc_version_info,
settings::{
BytecodeHash, Codegen, Optimizer, OptimizerDetails, SettingsMetadata, ZkSolcError,
ZkSolcSettings, ZkSolcWarning,
BytecodeHash, Codegen, Optimizer, OptimizerDetails, SettingsMetadata,
ZkSolcSettings,
},
ZkSettings, ZkSolc, ZkSolcCompiler,
},
},
};

use semver::Version;
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, path::PathBuf};
Expand Down Expand Up @@ -70,10 +70,10 @@ pub struct ZkSyncConfig {
pub optimizer_details: Option<OptimizerDetails>,

// zksolc suppressed warnings.
pub suppressed_warnings: HashSet<ZkSolcWarning>,
pub suppressed_warnings: HashSet<WarningType>,

// zksolc suppressed errors.
pub suppressed_errors: HashSet<ZkSolcError>,
pub suppressed_errors: HashSet<ErrorType>,
}

impl Default for ZkSyncConfig {
Expand Down Expand Up @@ -198,7 +198,7 @@ pub fn config_create_project(
{
zksolc
} else if !config.offline {
let default_version = semver::Version::new(1, 5, 7);
let default_version = semver::Version::new(1, 5, 10);
let mut zksolc = ZkSolc::find_installed_version(&default_version)?;
if zksolc.is_none() {
ZkSolc::blocking_install(&default_version)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/forge/tests/it/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ impl ForgeTestProfile {
zk_config.zksync.startup = true;
zk_config.zksync.fallback_oz = true;
zk_config.zksync.optimizer_mode = '3';
zk_config.zksync.zksolc = Some(foundry_config::SolcReq::Version(Version::new(1, 5, 7)));
zk_config.zksync.zksolc = Some(foundry_config::SolcReq::Version(Version::new(1, 5, 10)));
zk_config.fuzz.no_zksync_reserved_addresses = true;
zk_config.invariant.depth = 15;

Expand Down
1 change: 1 addition & 0 deletions crates/zksync/compilers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ path-slash = "0.2"

# zk
zksync_types.workspace = true
era-solc.workspace = true

[dev-dependencies]
similar-asserts.workspace = true
Expand Down
49 changes: 48 additions & 1 deletion crates/zksync/compilers/src/artifacts/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,45 @@ use foundry_compilers_artifacts_solc::{
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::BTreeMap};

/// zksolc: Binary object format.
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(try_from = "String", into = "String")]
pub enum ObjectFormat {
/// Linked
#[default]
Raw,
/// Unlinked
Elf,
}

impl From<ObjectFormat> for String {
fn from(val: ObjectFormat) -> Self {
match val {
ObjectFormat::Raw => "raw",
ObjectFormat::Elf => "elf",
}
.to_string()
}
}

impl TryFrom<String> for ObjectFormat {
type Error = String;
fn try_from(s: String) -> Result<Self, Self::Error> {
match s.as_str() {
"raw" => Ok(Self::Raw),
"elf" => Ok(Self::Elf),
s => Err(format!("Unknown zksolc object format: {s}")),
}
}
}

impl ObjectFormat {
/// Returns `true` if the bytecode is unlinked
pub fn is_unlinked(&self) -> bool {
matches!(self, Self::Elf)
}
}

/// Represents a compiled solidity contract
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -44,6 +83,14 @@ pub struct Contract {
/// The contract's unlinked libraries
#[serde(default)]
pub missing_libraries: Vec<String>,
/// zksolc's binary object format
///
/// Tells whether the bytecode has been linked.
///
/// Introduced in 1.5.8, beforehand we can assume the bytecode
/// was always fully linked
#[serde(default)]
pub object_format: Option<ObjectFormat>,
}

fn storage_layout_is_empty(storage_layout: &StorageLayout) -> bool {
Expand All @@ -53,7 +100,7 @@ fn storage_layout_is_empty(storage_layout: &StorageLayout) -> bool {
impl Contract {
/// Returns true if contract is not linked
pub fn is_unlinked(&self) -> bool {
self.hash.is_none() || !self.missing_libraries.is_empty()
self.object_format.as_ref().map(|format| format.is_unlinked()).unwrap_or_default()
}

/// takes missing libraries output and transforms into link references
Expand Down
15 changes: 11 additions & 4 deletions crates/zksync/compilers/src/compilers/artifact_output/zk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ impl ZkContractArtifact {
pub fn missing_libraries(&self) -> Option<&Vec<String>> {
self.bytecode.as_ref().map(|bc| &bc.missing_libraries)
}

/// Returns true if contract is not linked
pub fn is_unlinked(&self) -> bool {
self.bytecode.as_ref().map(|bc| bc.is_unlinked()).unwrap_or(false)
}
}

// CompactContract variants
Expand Down Expand Up @@ -118,7 +123,6 @@ impl ArtifactOutput for ZkArtifactOutput {
contract: Self::CompilerContract,
source_file: Option<&SourceFile>,
) -> Self::Artifact {
let is_unlinked = contract.is_unlinked();
let Contract {
abi,
metadata,
Expand All @@ -131,14 +135,17 @@ impl ArtifactOutput for ZkArtifactOutput {
hash,
factory_dependencies,
missing_libraries,
object_format,
} = contract;
let object_format = object_format.unwrap_or_default();

let (bytecode, assembly) = eravm
.map(|eravm| (eravm.bytecode(is_unlinked), eravm.assembly))
.map(|eravm| (eravm.bytecode(object_format.is_unlinked()), eravm.assembly))
.or_else(|| evm.map(|evm| (evm.bytecode.map(|bc| bc.object), evm.assembly)))
.unwrap_or_else(|| (None, None));
let bytecode = bytecode
.map(|object| ZkArtifactBytecode::with_object(object, is_unlinked, missing_libraries));
let bytecode = bytecode.map(|object| {
ZkArtifactBytecode::with_object(object, object_format, missing_libraries)
});

ZkContractArtifact {
abi,
Expand Down
Loading

0 comments on commit 62af6f9

Please sign in to comment.