From 18d52cd43bfc56c6d7f80ed8d65149bf87462f87 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 00:16:40 +0100 Subject: [PATCH 01/17] Adds init code size limit check & column to table. --- crates/common/src/compile.rs | 137 +++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 32 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index a75ac0819dde..c5bdff34452f 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -44,6 +44,9 @@ pub struct ProjectCompiler { /// Whether to bail on compiler errors. bail: Option, + /// Whether to ignore the contract initcode size limit introduced by EIP-3860. + ignore_eip_3860: Option, + /// Extra files to include, that are not necessarily in the project's source dir. files: Vec, } @@ -65,6 +68,7 @@ impl ProjectCompiler { print_sizes: None, quiet: Some(crate::shell::verbosity().is_silent()), bail: None, + ignore_eip_3860: None, files: Vec::new(), } } @@ -114,6 +118,13 @@ impl ProjectCompiler { self } + /// Sets whether to ignore EIP-3860 initcode size limits. + #[inline] + pub fn ignore_eip_3860(mut self, yes: bool) -> Self { + self.ignore_eip_3860 = Some(yes); + self + } + /// Sets extra files to include, that are not necessarily in the project's source dir. #[inline] pub fn files(mut self, files: impl IntoIterator) -> Self { @@ -232,26 +243,34 @@ impl ProjectCompiler { .collect(); for (name, artifact) in artifacts { - let size = deployed_contract_size(artifact).unwrap_or_default(); + let runtime_size = deployed_contract_size(artifact).unwrap_or_default(); + let init_size = initcode_size(artifact).unwrap_or_default(); let is_dev_contract = artifact .abi .as_ref() .map(|abi| { abi.functions().any(|f| { - f.test_function_kind().is_known() || - matches!(f.name.as_str(), "IS_TEST" | "IS_SCRIPT") + f.test_function_kind().is_known() + || matches!(f.name.as_str(), "IS_TEST" | "IS_SCRIPT") }) }) .unwrap_or(false); - size_report.contracts.insert(name, ContractInfo { size, is_dev_contract }); + size_report + .contracts + .insert(name, ContractInfo { runtime_size, init_size, is_dev_contract }); } println!("{size_report}"); // TODO: avoid process::exit // exit with error if any contract exceeds the size limit, excluding test contracts. - if size_report.exceeds_size_limit() { + if size_report.exceeds_runtime_size_limit() { + std::process::exit(1); + } + + // Check size limits only if not ignoring EIP-3860 + if !self.ignore_eip_3860.unwrap_or(false) && size_report.exceeds_initcode_size_limit() { std::process::exit(1); } } @@ -259,7 +278,10 @@ impl ProjectCompiler { } // https://eips.ethereum.org/EIPS/eip-170 -const CONTRACT_SIZE_LIMIT: usize = 24576; +const CONTRACT_RUNTIME_SIZE_LIMIT: usize = 24576; + +// https://eips.ethereum.org/EIPS/eip-3860 +const CONTRACT_INITCODE_SIZE_LIMIT: usize = 49152; /// Contracts with info about their size pub struct SizeReport { @@ -268,20 +290,34 @@ pub struct SizeReport { } impl SizeReport { - /// Returns the size of the largest contract, excluding test contracts. - pub fn max_size(&self) -> usize { - let mut max_size = 0; - for contract in self.contracts.values() { - if !contract.is_dev_contract && contract.size > max_size { - max_size = contract.size; - } - } - max_size + /// Returns the maximum runtime code size, excluding dev contracts. + pub fn max_runtime_size(&self) -> usize { + self.contracts + .values() + .filter(|c| !c.is_dev_contract) + .map(|c| c.runtime_size) + .max() + .unwrap_or(0) + } + + /// Returns the maximum initcode size, excluding dev contracts. + pub fn max_init_size(&self) -> usize { + self.contracts + .values() + .filter(|c| !c.is_dev_contract) + .map(|c| c.init_size) + .max() + .unwrap_or(0) } - /// Returns true if any contract exceeds the size limit, excluding test contracts. - pub fn exceeds_size_limit(&self) -> bool { - self.max_size() > CONTRACT_SIZE_LIMIT + /// Returns true if any contract exceeds the runtime size limit, excluding dev contracts. + pub fn exceeds_runtime_size_limit(&self) -> bool { + self.max_runtime_size() > CONTRACT_RUNTIME_SIZE_LIMIT + } + + /// Returns true if any contract exceeds the initcode size limit, excluding dev contracts. + pub fn exceeds_initcode_size_limit(&self) -> bool { + self.max_init_size() > CONTRACT_INITCODE_SIZE_LIMIT } } @@ -291,29 +327,49 @@ impl Display for SizeReport { table.load_preset(ASCII_MARKDOWN); table.set_header([ Cell::new("Contract").add_attribute(Attribute::Bold).fg(Color::Blue), - Cell::new("Size (B)").add_attribute(Attribute::Bold).fg(Color::Blue), - Cell::new("Margin (B)").add_attribute(Attribute::Bold).fg(Color::Blue), + Cell::new("Runtime Size (B)").add_attribute(Attribute::Bold).fg(Color::Blue), + Cell::new("Initcode Size (B)").add_attribute(Attribute::Bold).fg(Color::Blue), + Cell::new("Runtime Margin (B)").add_attribute(Attribute::Bold).fg(Color::Blue), + Cell::new("Initcode Margin (B)").add_attribute(Attribute::Bold).fg(Color::Blue), ]); - // filters out non dev contracts (Test or Script) - let contracts = self.contracts.iter().filter(|(_, c)| !c.is_dev_contract && c.size > 0); + // Filters out dev contracts + let contracts = self + .contracts + .iter() + .filter(|(_, c)| !c.is_dev_contract && (c.runtime_size > 0 || c.init_size > 0)); for (name, contract) in contracts { - let margin = CONTRACT_SIZE_LIMIT as isize - contract.size as isize; - let color = match contract.size { + let runtime_margin = + CONTRACT_RUNTIME_SIZE_LIMIT as isize - contract.runtime_size as isize; + let init_margin = CONTRACT_INITCODE_SIZE_LIMIT as isize - contract.init_size as isize; + + let runtime_color = match contract.runtime_size { 0..=17999 => Color::Reset, - 18000..=CONTRACT_SIZE_LIMIT => Color::Yellow, + 18000..=CONTRACT_RUNTIME_SIZE_LIMIT => Color::Yellow, + _ => Color::Red, + }; + + let init_color = match contract.init_size { + 0..=35999 => Color::Reset, + 36000..=CONTRACT_INITCODE_SIZE_LIMIT => Color::Yellow, _ => Color::Red, }; let locale = &Locale::en; table.add_row([ - Cell::new(name).fg(color), - Cell::new(contract.size.to_formatted_string(locale)) + Cell::new(name).fg(Color::Blue), + Cell::new(contract.runtime_size.to_formatted_string(locale)) + .set_alignment(CellAlignment::Right) + .fg(runtime_color), + Cell::new(contract.init_size.to_formatted_string(locale)) + .set_alignment(CellAlignment::Right) + .fg(init_color), + Cell::new(runtime_margin.to_formatted_string(locale)) .set_alignment(CellAlignment::Right) - .fg(color), - Cell::new(margin.to_formatted_string(locale)) + .fg(runtime_color), + Cell::new(init_margin.to_formatted_string(locale)) .set_alignment(CellAlignment::Right) - .fg(color), + .fg(init_color), ]); } @@ -341,11 +397,28 @@ pub fn deployed_contract_size(artifact: &T) -> Option { Some(size) } +pub fn initcode_size(artifact: &T) -> Option { + let initcode = artifact.get_bytecode_object()?; + let size = match initcode.as_ref() { + BytecodeObject::Bytecode(bytes) => bytes.len(), + BytecodeObject::Unlinked(unlinked) => { + let mut size = unlinked.as_bytes().len(); + if unlinked.starts_with("0x") { + size -= 2; + } + size / 2 + } + }; + Some(size) +} + /// How big the contract is and whether it is a dev contract where size limits can be neglected #[derive(Clone, Copy, Debug)] pub struct ContractInfo { - /// size of the contract in bytes - pub size: usize, + /// Size of the runtime code in bytes + pub runtime_size: usize, + /// Size of the initcode in bytes + pub init_size: usize, /// A development contract is either a Script or a Test contract. pub is_dev_contract: bool, } From 8a70f921993f715b9aa69c210fc569024a3b61ca Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 00:17:55 +0100 Subject: [PATCH 02/17] Adds option to ignore init code size check during --size output. --- crates/forge/bin/cmd/build.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 53bc5bc2001b..07e894ba4e12 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -60,6 +60,11 @@ pub struct BuildArgs { #[serde(skip)] pub sizes: bool, + /// Ignore initcode contract bytecode size limit introduced by EIP-3860 + #[arg(long)] + #[serde(skip)] + pub ignore_eip_3860: bool, + #[command(flatten)] #[serde(flatten)] pub args: CoreBuildArgs, @@ -79,8 +84,8 @@ impl BuildArgs { pub fn run(self) -> Result { let mut config = self.try_load_config_emit_warnings()?; - if install::install_missing_dependencies(&mut config, self.args.silent) && - config.auto_detect_remappings + if install::install_missing_dependencies(&mut config, self.args.silent) + && config.auto_detect_remappings { // need to re-configure here to also catch additional remappings config = self.load_config(); @@ -102,6 +107,7 @@ impl BuildArgs { .files(files) .print_names(self.names) .print_sizes(self.sizes) + .ignore_eip_3860(self.ignore_eip_3860) .quiet(self.format_json) .bail(!self.format_json); @@ -158,6 +164,10 @@ impl Provider for BuildArgs { dict.insert("sizes".to_string(), true.into()); } + if self.ignore_eip_3860 { + dict.insert("ignore_eip_3860".to_string(), true.into()); + } + Ok(Map::from([(Config::selected_profile(), dict)])) } } From 9c72b63bf186830b59192129dbc056138f65207f Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 00:18:29 +0100 Subject: [PATCH 03/17] Updates tests with new column for --sizes table. --- crates/forge/tests/cli/build.rs | 6 +++--- crates/forge/tests/cli/cmd.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index d9861f19e392..0545515c63ef 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -57,9 +57,9 @@ forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { cmd.args(["build", "--sizes"]).assert_success().stdout_eq(str![ r#" ... -| Contract | Size (B) | Margin (B) | -|----------|----------|------------| -| Counter | 247 | 24,329 | +| Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | +|----------|------------------|-------------------|--------------------|---------------------| +| Counter | 247 | 277 | 24,329 | 48,875 | ... "# ]); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index d82600b6251a..78c18bbaf65d 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -2186,9 +2186,9 @@ forgetest_init!(can_build_sizes_repeatedly, |prj, cmd| { [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! -| Contract | Size (B) | Margin (B) | -|----------|----------|------------| -| Counter | 247 | 24,329 | +| Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | +|----------|------------------|-------------------|--------------------|---------------------| +| Counter | 247 | 277 | 24,329 | 48,875 | "#]]); From 34d615ce49784139b7dd5b491989c5788273e285 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 02:57:59 +0100 Subject: [PATCH 04/17] Adds test helpers for forge CLI. --- crates/forge/tests/cli/main.rs | 1 + crates/forge/tests/cli/test_helpers.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 crates/forge/tests/cli/test_helpers.rs diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index 6ad29ca48a93..6f7c7d453b2f 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -21,6 +21,7 @@ mod script; mod soldeer; mod svm; mod test_cmd; +mod test_helpers; mod verify; mod verify_bytecode; diff --git a/crates/forge/tests/cli/test_helpers.rs b/crates/forge/tests/cli/test_helpers.rs new file mode 100644 index 000000000000..690a3d8d5648 --- /dev/null +++ b/crates/forge/tests/cli/test_helpers.rs @@ -0,0 +1,26 @@ +//! Test helpers for Forge CLI tests. + +// This function generates a string containing the code of a Solidity contract +// with a variable init code size. +pub fn generate_large_contract(num_elements: usize) -> String { + let mut contract_code = String::new(); + + contract_code.push_str( + "// Auto-generated Solidity contract to inflate initcode size\ncontract HugeContract {\n uint256 public number;\n" + ); + + contract_code.push_str(" uint256[] public largeArray;\n\n constructor() {\n"); + contract_code.push_str(" largeArray = ["); + + for i in 0..num_elements { + if i != 0 { + contract_code.push_str(", "); + } + contract_code.push_str(&i.to_string()); + } + + contract_code.push_str("];\n"); + contract_code.push_str(" }\n}"); + + contract_code +} From c423a9552180cc47015ec45b3592350022499a14 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 02:58:25 +0100 Subject: [PATCH 05/17] Implements test for init code size limit as per EIP-3860 --- crates/forge/tests/cli/build.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 0545515c63ef..5649b25660a8 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,3 +1,5 @@ +use crate::test_helpers::generate_large_contract; + use foundry_config::Config; use foundry_test_utils::{forgetest, snapbox::IntoData, str}; use globset::Glob; @@ -42,6 +44,11 @@ contract Dummy { "#]].is_json()); }); +forgetest!(initcode_size_exceeds_limit, |prj, cmd| { + prj.add_source("LargeContract", generate_large_contract(5450).as_str()).unwrap(); + cmd.args(["build", "--sizes"]).assert_failure(); +}); + // tests build output is as expected forgetest_init!(exact_build_output, |prj, cmd| { cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" From 53b856d2a375b9fc8e98744720722edef7d7d0ba Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 03:22:02 +0100 Subject: [PATCH 06/17] Adds test for --ignore-eip-3860 --- crates/forge/tests/cli/build.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 5649b25660a8..52e30e78cc95 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -49,6 +49,11 @@ forgetest!(initcode_size_exceeds_limit, |prj, cmd| { cmd.args(["build", "--sizes"]).assert_failure(); }); +forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { + prj.add_source("LargeContract", generate_large_contract(5450).as_str()).unwrap(); + cmd.args(["build", "--sizes", "--ignore-eip-3860"]).assert_success(); +}); + // tests build output is as expected forgetest_init!(exact_build_output, |prj, cmd| { cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" From ab8af7e84ceb251c044b1768bcf3c341365ab58c Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 03:32:06 +0100 Subject: [PATCH 07/17] Fixes for Cargo +nightly fmt warnings. --- crates/common/src/compile.rs | 4 ++-- crates/forge/bin/cmd/build.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index c5bdff34452f..5e73f6df8dca 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -251,8 +251,8 @@ impl ProjectCompiler { .as_ref() .map(|abi| { abi.functions().any(|f| { - f.test_function_kind().is_known() - || matches!(f.name.as_str(), "IS_TEST" | "IS_SCRIPT") + f.test_function_kind().is_known() || + matches!(f.name.as_str(), "IS_TEST" | "IS_SCRIPT") }) }) .unwrap_or(false); diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 07e894ba4e12..46ef85a2d178 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -84,8 +84,8 @@ impl BuildArgs { pub fn run(self) -> Result { let mut config = self.try_load_config_emit_warnings()?; - if install::install_missing_dependencies(&mut config, self.args.silent) - && config.auto_detect_remappings + if install::install_missing_dependencies(&mut config, self.args.silent) && + config.auto_detect_remappings { // need to re-configure here to also catch additional remappings config = self.load_config(); From 7ed3992faf01ab67063aa0cacdf535e820fe4aa2 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 15:19:44 +0100 Subject: [PATCH 08/17] Refactors both contract size functions into one with a boolean arg. --- crates/common/src/compile.rs | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 5e73f6df8dca..5bc767dd453d 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -243,8 +243,8 @@ impl ProjectCompiler { .collect(); for (name, artifact) in artifacts { - let runtime_size = deployed_contract_size(artifact).unwrap_or_default(); - let init_size = initcode_size(artifact).unwrap_or_default(); + let runtime_size = contract_size(artifact, false).unwrap_or_default(); + let init_size = contract_size(artifact, true).unwrap_or_default(); let is_dev_contract = artifact .abi @@ -378,28 +378,15 @@ impl Display for SizeReport { } } -/// Returns the size of the deployed contract -pub fn deployed_contract_size(artifact: &T) -> Option { - let bytecode = artifact.get_deployed_bytecode_object()?; - let size = match bytecode.as_ref() { - BytecodeObject::Bytecode(bytes) => bytes.len(), - BytecodeObject::Unlinked(unlinked) => { - // we don't need to account for placeholders here, because library placeholders take up - // 40 characters: `__$$__` which is the same as a 20byte address in hex. - let mut size = unlinked.as_bytes().len(); - if unlinked.starts_with("0x") { - size -= 2; - } - // hex -> bytes - size / 2 - } +/// Returns the deployed or init size of the contract. +fn contract_size(artifact: &T, initcode: bool) -> Option { + let bytecode = if initcode { + artifact.get_bytecode_object()? + } else { + artifact.get_deployed_bytecode_object()? }; - Some(size) -} -pub fn initcode_size(artifact: &T) -> Option { - let initcode = artifact.get_bytecode_object()?; - let size = match initcode.as_ref() { + let size = match bytecode.as_ref() { BytecodeObject::Bytecode(bytes) => bytes.len(), BytecodeObject::Unlinked(unlinked) => { let mut size = unlinked.as_bytes().len(); @@ -409,6 +396,7 @@ pub fn initcode_size(artifact: &T) -> Option { size / 2 } }; + Some(size) } From dc1589346b7b4d991a3c6ac628887df42ab29fa5 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 15:28:30 +0100 Subject: [PATCH 09/17] Adds alias for --ignore-eip-3860 to --ignore-initcode-size. --- crates/forge/bin/cmd/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 46ef85a2d178..0ea7fbef9140 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -61,7 +61,7 @@ pub struct BuildArgs { pub sizes: bool, /// Ignore initcode contract bytecode size limit introduced by EIP-3860 - #[arg(long)] + #[arg(long, alias = "ignore-initcode-size")] #[serde(skip)] pub ignore_eip_3860: bool, From 70d1c76f46ab451f1ae0115fd1aeb105bf5039f0 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Tue, 15 Oct 2024 16:23:19 +0100 Subject: [PATCH 10/17] Brings back the original comments. --- crates/common/src/compile.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 5bc767dd453d..eb0aca810342 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -389,10 +389,13 @@ fn contract_size(artifact: &T, initcode: bool) -> Option { let size = match bytecode.as_ref() { BytecodeObject::Bytecode(bytes) => bytes.len(), BytecodeObject::Unlinked(unlinked) => { + // we don't need to account for placeholders here, because library placeholders take up + // 40 characters: `__$$__` which is the same as a 20byte address in hex. let mut size = unlinked.as_bytes().len(); if unlinked.starts_with("0x") { size -= 2; } + // hex -> bytes size / 2 } }; From f41b08f6378238ea82c9b6fc66a3b430706cffb9 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:34:56 +0200 Subject: [PATCH 11/17] Update compile.rs --- crates/common/src/compile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index eb0aca810342..873fcb8d9bc4 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -333,7 +333,7 @@ impl Display for SizeReport { Cell::new("Initcode Margin (B)").add_attribute(Attribute::Bold).fg(Color::Blue), ]); - // Filters out dev contracts + // Filters out dev contracts (Test or Script) let contracts = self .contracts .iter() From eca4e1a00d5acb6c5edb6380146b65f80be32406 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Wed, 16 Oct 2024 11:41:06 +0100 Subject: [PATCH 12/17] Changes --ignore-eip-3860 to be a boolean field. --- crates/common/src/compile.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 873fcb8d9bc4..a91888e6143b 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -45,7 +45,7 @@ pub struct ProjectCompiler { bail: Option, /// Whether to ignore the contract initcode size limit introduced by EIP-3860. - ignore_eip_3860: Option, + ignore_eip_3860: bool, /// Extra files to include, that are not necessarily in the project's source dir. files: Vec, @@ -68,7 +68,7 @@ impl ProjectCompiler { print_sizes: None, quiet: Some(crate::shell::verbosity().is_silent()), bail: None, - ignore_eip_3860: None, + ignore_eip_3860: false, files: Vec::new(), } } @@ -121,7 +121,7 @@ impl ProjectCompiler { /// Sets whether to ignore EIP-3860 initcode size limits. #[inline] pub fn ignore_eip_3860(mut self, yes: bool) -> Self { - self.ignore_eip_3860 = Some(yes); + self.ignore_eip_3860 = yes; self } @@ -270,7 +270,7 @@ impl ProjectCompiler { } // Check size limits only if not ignoring EIP-3860 - if !self.ignore_eip_3860.unwrap_or(false) && size_report.exceeds_initcode_size_limit() { + if !self.ignore_eip_3860 && size_report.exceeds_initcode_size_limit() { std::process::exit(1); } } From ab4751d1f4f4bbc98c11e4feca3127d969562581 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Wed, 16 Oct 2024 12:27:07 +0100 Subject: [PATCH 13/17] Fixes ranges in table display code and comment punctuation. --- crates/common/src/compile.rs | 4 ++-- crates/forge/bin/cmd/build.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index a91888e6143b..a7b3c5dbf49a 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -350,8 +350,8 @@ impl Display for SizeReport { }; let init_color = match contract.init_size { - 0..=35999 => Color::Reset, - 36000..=CONTRACT_INITCODE_SIZE_LIMIT => Color::Yellow, + ..=36_000 => Color::Reset, + ..=CONTRACT_INITCODE_SIZE_LIMIT => Color::Yellow, _ => Color::Red, }; diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 0ea7fbef9140..e539bfaeed69 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -60,7 +60,7 @@ pub struct BuildArgs { #[serde(skip)] pub sizes: bool, - /// Ignore initcode contract bytecode size limit introduced by EIP-3860 + /// Ignore initcode contract bytecode size limit introduced by EIP-3860. #[arg(long, alias = "ignore-initcode-size")] #[serde(skip)] pub ignore_eip_3860: bool, From d5b9928b9ef557946a33e480baff966013d6a672 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Wed, 16 Oct 2024 12:27:45 +0100 Subject: [PATCH 14/17] Moves testing helper to existing utils module. --- crates/forge/tests/cli/build.rs | 6 +++--- crates/forge/tests/cli/main.rs | 1 - crates/forge/tests/cli/test_helpers.rs | 26 ----------------------- crates/forge/tests/cli/utils.rs | 29 ++++++++++++++++++++++++-- 4 files changed, 30 insertions(+), 32 deletions(-) delete mode 100644 crates/forge/tests/cli/test_helpers.rs diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 52e30e78cc95..3bd7ddebc4d7 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,4 +1,4 @@ -use crate::test_helpers::generate_large_contract; +use crate::utils::generate_large_contract; use foundry_config::Config; use foundry_test_utils::{forgetest, snapbox::IntoData, str}; @@ -46,12 +46,12 @@ contract Dummy { forgetest!(initcode_size_exceeds_limit, |prj, cmd| { prj.add_source("LargeContract", generate_large_contract(5450).as_str()).unwrap(); - cmd.args(["build", "--sizes"]).assert_failure(); + cmd.args(["build", "--sizes"]).assert_failure().stdout_eq(str![[r#""#]]); }); forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { prj.add_source("LargeContract", generate_large_contract(5450).as_str()).unwrap(); - cmd.args(["build", "--sizes", "--ignore-eip-3860"]).assert_success(); + cmd.args(["build", "--sizes", "--ignore-eip-3860"]).assert_success().stdout_eq(str![[r#""#]]); }); // tests build output is as expected diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index 6f7c7d453b2f..6ad29ca48a93 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -21,7 +21,6 @@ mod script; mod soldeer; mod svm; mod test_cmd; -mod test_helpers; mod verify; mod verify_bytecode; diff --git a/crates/forge/tests/cli/test_helpers.rs b/crates/forge/tests/cli/test_helpers.rs deleted file mode 100644 index 690a3d8d5648..000000000000 --- a/crates/forge/tests/cli/test_helpers.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Test helpers for Forge CLI tests. - -// This function generates a string containing the code of a Solidity contract -// with a variable init code size. -pub fn generate_large_contract(num_elements: usize) -> String { - let mut contract_code = String::new(); - - contract_code.push_str( - "// Auto-generated Solidity contract to inflate initcode size\ncontract HugeContract {\n uint256 public number;\n" - ); - - contract_code.push_str(" uint256[] public largeArray;\n\n constructor() {\n"); - contract_code.push_str(" largeArray = ["); - - for i in 0..num_elements { - if i != 0 { - contract_code.push_str(", "); - } - contract_code.push_str(&i.to_string()); - } - - contract_code.push_str("];\n"); - contract_code.push_str(" }\n}"); - - contract_code -} diff --git a/crates/forge/tests/cli/utils.rs b/crates/forge/tests/cli/utils.rs index 094255195c1e..2b0bb627314a 100644 --- a/crates/forge/tests/cli/utils.rs +++ b/crates/forge/tests/cli/utils.rs @@ -126,7 +126,7 @@ impl EnvExternalities { pub fn parse_deployed_address(out: &str) -> Option { for line in out.lines() { if line.starts_with("Deployed to") { - return Some(line.trim_start_matches("Deployed to: ").to_string()) + return Some(line.trim_start_matches("Deployed to: ").to_string()); } } None @@ -135,8 +135,33 @@ pub fn parse_deployed_address(out: &str) -> Option { pub fn parse_verification_guid(out: &str) -> Option { for line in out.lines() { if line.contains("GUID") { - return Some(line.replace("GUID:", "").replace('`', "").trim().to_string()) + return Some(line.replace("GUID:", "").replace('`', "").trim().to_string()); } } None } + +// Generates a string containing the code of a Solidity contract +// with a variable init code size. +pub fn generate_large_contract(num_elements: usize) -> String { + let mut contract_code = String::new(); + + contract_code.push_str( + "// Auto-generated Solidity contract to inflate initcode size\ncontract HugeContract {\n uint256 public number;\n" + ); + + contract_code.push_str(" uint256[] public largeArray;\n\n constructor() {\n"); + contract_code.push_str(" largeArray = ["); + + for i in 0..num_elements { + if i != 0 { + contract_code.push_str(", "); + } + contract_code.push_str(&i.to_string()); + } + + contract_code.push_str("];\n"); + contract_code.push_str(" }\n}"); + + contract_code +} From 3682274644c8b3f5bd45d90ac922bbd9851c16ec Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Wed, 16 Oct 2024 13:02:46 +0100 Subject: [PATCH 15/17] Improve ranges in table display code. --- crates/common/src/compile.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index a7b3c5dbf49a..26266bfc1e8f 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -344,8 +344,8 @@ impl Display for SizeReport { let init_margin = CONTRACT_INITCODE_SIZE_LIMIT as isize - contract.init_size as isize; let runtime_color = match contract.runtime_size { - 0..=17999 => Color::Reset, - 18000..=CONTRACT_RUNTIME_SIZE_LIMIT => Color::Yellow, + ..=18_000 => Color::Reset, + ..=CONTRACT_RUNTIME_SIZE_LIMIT => Color::Yellow, _ => Color::Red, }; From 24133b6188fd33ee1fdf18365eb089852f66aee3 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Wed, 16 Oct 2024 14:10:24 +0100 Subject: [PATCH 16/17] Adds output assertions to initcode size check tests. --- crates/forge/tests/cli/build.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 3bd7ddebc4d7..81919241fa00 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,5 +1,4 @@ use crate::utils::generate_large_contract; - use foundry_config::Config; use foundry_test_utils::{forgetest, snapbox::IntoData, str}; use globset::Glob; @@ -46,12 +45,28 @@ contract Dummy { forgetest!(initcode_size_exceeds_limit, |prj, cmd| { prj.add_source("LargeContract", generate_large_contract(5450).as_str()).unwrap(); - cmd.args(["build", "--sizes"]).assert_failure().stdout_eq(str![[r#""#]]); + cmd.args(["build", "--sizes"]).assert_failure().stdout_eq(str![ + r#" +... +| Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | +|--------------|------------------|-------------------|--------------------|---------------------| +| HugeContract | 202 | 49,359 | 24,374 | -207 | +... +"# + ]); }); forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { prj.add_source("LargeContract", generate_large_contract(5450).as_str()).unwrap(); - cmd.args(["build", "--sizes", "--ignore-eip-3860"]).assert_success().stdout_eq(str![[r#""#]]); + cmd.args(["build", "--sizes", "--ignore-eip-3860"]).assert_success().stdout_eq(str![ + r#" +... +| Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | +|--------------|------------------|-------------------|--------------------|---------------------| +| HugeContract | 202 | 49,359 | 24,374 | -207 | +... +"# + ]); }); // tests build output is as expected From cae5767013bd3417fdabba1d20d20d0058cbf993 Mon Sep 17 00:00:00 2001 From: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Date: Wed, 16 Oct 2024 23:10:39 +0100 Subject: [PATCH 17/17] Minor change to ranges in display logic for sizes table. --- crates/common/src/compile.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 26266bfc1e8f..39998b3a61c1 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -344,14 +344,14 @@ impl Display for SizeReport { let init_margin = CONTRACT_INITCODE_SIZE_LIMIT as isize - contract.init_size as isize; let runtime_color = match contract.runtime_size { - ..=18_000 => Color::Reset, - ..=CONTRACT_RUNTIME_SIZE_LIMIT => Color::Yellow, + ..18_000 => Color::Reset, + 18_000..=CONTRACT_RUNTIME_SIZE_LIMIT => Color::Yellow, _ => Color::Red, }; let init_color = match contract.init_size { - ..=36_000 => Color::Reset, - ..=CONTRACT_INITCODE_SIZE_LIMIT => Color::Yellow, + ..36_000 => Color::Reset, + 36_000..=CONTRACT_INITCODE_SIZE_LIMIT => Color::Yellow, _ => Color::Red, };