From 54ea38d189bf192f689aed4c6f231a27f1def316 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:36:15 +0200 Subject: [PATCH] fix(forge): always report deployment size in gas reports (#9308) --- crates/forge/src/gas_report.rs | 29 ++++---- crates/forge/tests/cli/cmd.rs | 126 +++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 14 deletions(-) diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 1b6e4bd63bc0..e3e44994b91c 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -94,31 +94,32 @@ impl GasReport { return; } - // Only include top-level calls which account for calldata and base (21.000) cost. - // Only include Calls and Creates as only these calls are isolated in inspector. - if trace.depth > 1 && - (trace.kind == CallKind::Call || - trace.kind == CallKind::Create || - trace.kind == CallKind::Create2 || - trace.kind == CallKind::EOFCreate) - { - return; - } - let Some(name) = decoder.contracts.get(&node.trace.address) else { return }; let contract_name = name.rsplit(':').next().unwrap_or(name); if !self.should_report(contract_name) { return; } + let contract_info = self.contracts.entry(name.to_string()).or_default(); + let is_create_call = trace.kind.is_any_create(); + + // Record contract deployment size. + if is_create_call { + trace!(contract_name, "adding create size info"); + contract_info.size = trace.data.len(); + } + + // Only include top-level calls which account for calldata and base (21.000) cost. + // Only include Calls and Creates as only these calls are isolated in inspector. + if trace.depth > 1 && (trace.kind == CallKind::Call || is_create_call) { + return; + } let decoded = || decoder.decode_function(&node.trace); - let contract_info = self.contracts.entry(name.to_string()).or_default(); - if trace.kind.is_any_create() { + if is_create_call { trace!(contract_name, "adding create gas info"); contract_info.gas = trace.gas_used; - contract_info.size = trace.data.len(); } else if let Some(DecodedCallData { signature, .. }) = decoded().await.call_data { let name = signature.split('(').next().unwrap(); // ignore any test/setup functions diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 9e9e1fda1c1f..d4f3973a337a 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -2544,6 +2544,132 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] ); }); +// +forgetest_init!(gas_report_size_for_nested_create, |prj, cmd| { + prj.add_test( + "NestedDeployTest.sol", + r#" +import {Test} from "forge-std/Test.sol"; +contract Child { + AnotherChild public child; + constructor() { + child = new AnotherChild(); + } + function w() external { + child.w(); + } +} +contract AnotherChild { + function w() external {} +} +contract Parent { + Child public immutable child; + constructor() { + child = new Child(); + } +} +contract NestedDeploy is Test { + function test_nested_create_gas_report() external { + Parent p = new Parent(); + p.child().child().w(); + } +} +"#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "test_nested_create_gas_report", "--gas-report"]) + .assert_success() + .stdout_eq(str![[r#" +... +Ran 1 test for test/NestedDeployTest.sol:NestedDeploy +[PASS] test_nested_create_gas_report() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] +| test/NestedDeployTest.sol:AnotherChild contract | | | | | | +|-------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 0 | 130 | | | | | +| Function Name | min | avg | median | max | # calls | +| w | 21162 | 21162 | 21162 | 21162 | 1 | + + +| test/NestedDeployTest.sol:Child contract | | | | | | +|------------------------------------------|-----------------|-----|--------|-----|---------| +| Deployment Cost | Deployment Size | | | | | +| 0 | 498 | | | | | +| Function Name | min | avg | median | max | # calls | +| child | 325 | 325 | 325 | 325 | 1 | + + +| test/NestedDeployTest.sol:Parent contract | | | | | | +|-------------------------------------------|-----------------|-----|--------|-----|---------| +| Deployment Cost | Deployment Size | | | | | +| 254857 | 770 | | | | | +| Function Name | min | avg | median | max | # calls | +| child | 182 | 182 | 182 | 182 | 1 | +... +"#]]); + + cmd.forge_fuse() + .args(["test", "--mt", "test_nested_create_gas_report", "--gas-report", "--json"]) + .assert_success() + .stdout_eq( + str![[r#" +[ + { + "contract": "test/NestedDeployTest.sol:AnotherChild", + "deployment": { + "gas": 0, + "size": 130 + }, + "functions": { + "w()": { + "calls": 1, + "min": 21162, + "mean": 21162, + "median": 21162, + "max": 21162 + } + } + }, + { + "contract": "test/NestedDeployTest.sol:Child", + "deployment": { + "gas": 0, + "size": 498 + }, + "functions": { + "child()": { + "calls": 1, + "min": 325, + "mean": 325, + "median": 325, + "max": 325 + } + } + }, + { + "contract": "test/NestedDeployTest.sol:Parent", + "deployment": { + "gas": 254857, + "size": 770 + }, + "functions": { + "child()": { + "calls": 1, + "min": 182, + "mean": 182, + "median": 182, + "max": 182 + } + } + } +] +"#]] + .is_json(), + ); +}); + forgetest_init!(can_use_absolute_imports, |prj, cmd| { let remapping = prj.paths().libraries[0].join("myDependency"); let config = Config {