Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use foundry_compilers::{
solc::SolcSettings,
};
use num_format::{Locale, ToFormattedString};
use revm::primitives::{eip170, eip3860, eip7954, hardfork::SpecId};
use solar::{
ast::{Arena, ContractKind, ItemKind},
interface::{Session, source_map::FileName},
Expand Down Expand Up @@ -357,21 +358,21 @@ impl ProjectCompiler {

sh_println!("{size_report}")?;

let runtime_eip = if size_report.limits.runtime == CONTRACT_RUNTIME_SIZE_LIMIT {
"EIP-170: "
} else {
""
let runtime_eip = match size_report.limits.runtime {
CONTRACT_RUNTIME_SIZE_LIMIT => "EIP-170: ",
eip7954::MAX_CODE_SIZE => "EIP-7954: ",
_ => "",
};
eyre::ensure!(
!size_report.exceeds_runtime_size_limit(),
"some contracts exceed the runtime size limit ({runtime_eip}{} bytes)",
size_report.limits.runtime
);
// Check size limits only if not ignoring EIP-3860
let initcode_eip = if size_report.limits.initcode == CONTRACT_INITCODE_SIZE_LIMIT {
"EIP-3860: "
} else {
""
let initcode_eip = match size_report.limits.initcode {
CONTRACT_INITCODE_SIZE_LIMIT => "EIP-3860: ",
eip7954::MAX_INITCODE_SIZE => "EIP-7954: ",
_ => "",
};
eyre::ensure!(
self.ignore_eip_3860 || !size_report.exceeds_initcode_size_limit(),
Expand All @@ -385,10 +386,10 @@ impl ProjectCompiler {
}

// https://eips.ethereum.org/EIPS/eip-170
const CONTRACT_RUNTIME_SIZE_LIMIT: usize = 24576;
const CONTRACT_RUNTIME_SIZE_LIMIT: usize = eip170::MAX_CODE_SIZE;

// https://eips.ethereum.org/EIPS/eip-3860
const CONTRACT_INITCODE_SIZE_LIMIT: usize = 49152;
const CONTRACT_INITCODE_SIZE_LIMIT: usize = eip3860::MAX_INITCODE_SIZE;

const CONTRACT_RUNTIME_SIZE_WARN_THRESHOLD: usize = 18_000;
const CONTRACT_INITCODE_SIZE_WARN_THRESHOLD: usize = 36_000;
Expand All @@ -413,6 +414,15 @@ impl ContractSizeLimits {
Self { runtime, initcode: runtime.saturating_mul(2) }
}

/// Returns the protocol limits active for an EVM specification.
pub const fn for_spec_id(spec_id: SpecId) -> Self {
if spec_id.is_enabled_in(SpecId::AMSTERDAM) {
Self::new(eip7954::MAX_CODE_SIZE, eip7954::MAX_INITCODE_SIZE)
} else {
Self::new(CONTRACT_RUNTIME_SIZE_LIMIT, CONTRACT_INITCODE_SIZE_LIMIT)
}
}

const fn runtime_warning_threshold(self) -> usize {
scaled_threshold(
self.runtime,
Expand Down Expand Up @@ -898,4 +908,13 @@ mod tests {
ContractSizeLimits::new(50_000, 100_000)
);
}

#[test]
fn contract_size_limits_follow_evm_spec() {
assert_eq!(ContractSizeLimits::for_spec_id(SpecId::OSAKA), ContractSizeLimits::default());
assert_eq!(
ContractSizeLimits::for_spec_id(SpecId::AMSTERDAM),
ContractSizeLimits::new(eip7954::MAX_CODE_SIZE, eip7954::MAX_INITCODE_SIZE)
);
}
}
2 changes: 1 addition & 1 deletion crates/forge/src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl BuildArgs {
config
.code_size_limit
.map(ContractSizeLimits::with_runtime_limit)
.unwrap_or_default(),
.unwrap_or_else(|| ContractSizeLimits::for_spec_id(config.evm_spec_id())),
)
.bail(!format_json)
.compile(&project)?;
Expand Down
22 changes: 22 additions & 0 deletions crates/forge/tests/cli/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::utils::generate_large_init_contract;
use foundry_compilers::artifacts::EvmVersion;
use foundry_test_utils::{forgetest, forgetest_init, snapbox::IntoData, str};
use globset::Glob;
use std::fs;
Expand Down Expand Up @@ -189,6 +190,27 @@ forgetest!(build_sizes_respects_configured_code_size_limit, |prj, cmd| {
);
});

forgetest!(build_sizes_respects_amsterdam_code_size_limits, |prj, cmd| {
prj.add_source("LargeContract.sol", generate_large_init_contract(50_000).as_str());
prj.update_config(|config| {
config.evm_version = EvmVersion::Amsterdam;
});

cmd.args(["build", "--sizes", "--json"]).assert_success().stdout_eq(
str![[r#"
{
"LargeContract": {
"runtime_size": 62,
"init_size": 50125,
"runtime_margin": 32706,
"init_margin": 15411
}
}
"#]]
.is_json(),
);
});

// tests build output is as expected
forgetest_init!(exact_build_output, |prj, cmd| {
prj.initialize_default_contracts();
Expand Down
27 changes: 27 additions & 0 deletions crates/forge/tests/cli/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use alloy_network::Ethereum;
use alloy_primitives::{Address, Bytes, U256, address, hex};
use anvil::{NodeConfig, spawn};
use forge_script_sequence::ScriptSequence;
use foundry_compilers::artifacts::EvmVersion;
use foundry_test_utils::{
ScriptOutcome, ScriptTester,
rpc::{self, next_http_archive_rpc_url},
Expand Down Expand Up @@ -4054,6 +4055,32 @@ Error: `LargeRuntime` is above the contract size limit ([..] > 24576).
"#]]);
});

forgetest_async!(script_check_contract_sizes_uses_amsterdam_code_size_limit, |prj, cmd| {
foundry_test_utils::util::initialize(prj.root());
write_large_runtime_deploy_script(&prj, 25_000);
prj.update_config(|config| {
config.evm_version = EvmVersion::Amsterdam;
});

let (_api, handle) = spawn(NodeConfig::test().with_gas_limit(Some(1_000_000_000))).await;
cmd.set_current_dir(prj.root());
for var in ["FOUNDRY_CODE_SIZE_LIMIT", "DAPP_CODE_SIZE_LIMIT", "DAPP_TEST_CODE_SIZE_LIMIT"] {
cmd.unset_env(var);
}
let assert = cmd
.args([
"script",
"DeployLarge",
"--rpc-url",
&handle.http_endpoint(),
"--gas-limit",
"1000000000",
])
.assert_success();
let stderr = String::from_utf8_lossy(&assert.get_output().stderr);
assert!(!stderr.contains("above the contract size limit"), "{stderr}");
});

// Tests that `forge script` honors `code_size_limit` configured via foundry.toml
// (the bug fix: previously only the CLI flag was honored).
forgetest_async!(script_check_contract_sizes_honors_config_limit, |prj, cmd| {
Expand Down
10 changes: 7 additions & 3 deletions crates/script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,11 @@ impl ScriptArgs {
.code_size_limit
.or(pre_simulation.script_config.config.code_size_limit)
.map(ContractSizeLimits::with_runtime_limit)
.unwrap_or_default();
.unwrap_or_else(|| {
ContractSizeLimits::for_spec_id(
pre_simulation.script_config.config.evm_spec_id(),
)
});
pre_simulation.args.check_contract_sizes(
size_limits,
&pre_simulation.execution_result,
Expand Down Expand Up @@ -565,8 +569,8 @@ impl ScriptArgs {
Ok((func.clone(), data.into()))
}

/// Checks if the transaction is a deployment with either a size above the default contract size
/// limit or specified `code_size_limit`.
/// Checks if the transaction is a deployment with a size above the active EVM specification's
/// contract size limit or the specified `code_size_limit`.
///
/// If `self.broadcast` is enabled, it asks confirmation of the user. Otherwise, it just warns
/// the user.
Expand Down
Loading