Skip to content

Commit c6e86a6

Browse files
committed
Display gas report table for fork contracts
1 parent 7aef68f commit c6e86a6

File tree

4 files changed

+112
-20
lines changed

4 files changed

+112
-20
lines changed

crates/forge-runner/src/gas.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use starknet_api::transaction::fields::GasVectorComputationMode;
1818

1919
pub mod report;
2020
pub mod stats;
21+
mod utils;
2122

2223
#[tracing::instrument(skip_all, level = "debug")]
2324
pub fn calculate_used_gas(

crates/forge-runner/src/gas/report.rs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::gas::stats::GasStats;
2+
use crate::gas::utils::shorten_felt;
23
use cheatnet::trace_data::{CallTrace, CallTraceNode};
34
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
45
use comfy_table::{Attribute, Cell, Color, Table};
@@ -12,14 +13,20 @@ use std::fmt::Display;
1213
type ContractName = String;
1314
type Selector = String;
1415

16+
#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)]
17+
pub enum ContractId {
18+
TestContract(ContractName),
19+
ForkedContract(ClassHash),
20+
}
21+
1522
#[derive(Debug, Clone)]
1623
pub struct SingleTestGasInfo {
1724
pub gas_used: GasVector,
1825
pub report_data: Option<ReportData>,
1926
}
2027

2128
#[derive(Debug, Clone, Default)]
22-
pub struct ReportData(BTreeMap<ContractName, ContractInfo>);
29+
pub struct ReportData(BTreeMap<ContractId, ContractInfo>);
2330

2431
#[derive(Debug, Clone, Default)]
2532
pub struct ContractInfo {
@@ -58,15 +65,15 @@ impl SingleTestGasInfo {
5865
"class_hash should be set in `fn execute_call_entry_point` in cheatnet",
5966
);
6067

61-
let contract_name = get_contract_name(contracts_data, class_hash);
68+
let contract_id = get_contract_id(contracts_data, class_hash);
6269
let selector = get_selector(contracts_data, call.entry_point.entry_point_selector);
6370
let gas = call
6471
.gas_report_data
6572
.as_ref()
6673
.expect("Gas report data must be updated after test execution")
6774
.get_gas();
6875

69-
report_data.update_entry(contract_name, selector, gas);
76+
report_data.update_entry(contract_id, selector, gas);
7077
stack.extend(call.nested_calls.clone());
7178
}
7279
}
@@ -80,13 +87,8 @@ impl SingleTestGasInfo {
8087
}
8188

8289
impl ReportData {
83-
fn update_entry(
84-
&mut self,
85-
contract_name: ContractName,
86-
selector: Selector,
87-
gas_used: GasVector,
88-
) {
89-
let contract_info = self.0.entry(contract_name).or_default();
90+
fn update_entry(&mut self, contract_id: ContractId, selector: Selector, gas_used: GasVector) {
91+
let contract_info = self.0.entry(contract_id).or_default();
9092

9193
let current_gas = contract_info.gas_used;
9294
contract_info.gas_used = current_gas.checked_add(gas_used).unwrap_or_else(|| {
@@ -119,11 +121,26 @@ impl Display for ReportData {
119121
}
120122
}
121123

122-
fn get_contract_name(contracts_data: &ContractsDataStore, class_hash: ClassHash) -> ContractName {
123-
contracts_data
124-
.get_contract_name(&class_hash)
125-
.map_or("forked contract", |name| name.0.as_str())
126-
.to_string()
124+
impl Display for ContractId {
125+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126+
let name = match self {
127+
ContractId::TestContract(name) => format!("{name} Contract"),
128+
ContractId::ForkedContract(class_hash) => {
129+
format!(
130+
"forked contract\n(class hash: {})",
131+
shorten_felt(class_hash.0)
132+
)
133+
}
134+
};
135+
write!(f, "{name}")
136+
}
137+
}
138+
139+
fn get_contract_id(contracts_data: &ContractsDataStore, class_hash: ClassHash) -> ContractId {
140+
match contracts_data.get_contract_name(&class_hash) {
141+
Some(name) => ContractId::TestContract(name.0.to_string()),
142+
None => ContractId::ForkedContract(class_hash),
143+
}
127144
}
128145

129146
fn get_selector(contracts_data: &ContractsDataStore, selector: EntryPointSelector) -> Selector {
@@ -134,13 +151,11 @@ fn get_selector(contracts_data: &ContractsDataStore, selector: EntryPointSelecto
134151
.clone()
135152
}
136153

137-
pub fn format_table_output(contract_info: &ContractInfo, name: &ContractName) -> Table {
154+
pub fn format_table_output(contract_info: &ContractInfo, contract_id: &ContractId) -> Table {
138155
let mut table = Table::new();
139156
table.apply_modifier(UTF8_ROUND_CORNERS);
140157

141-
table.set_header(vec![
142-
Cell::new(format!("{name} Contract")).fg(Color::Magenta),
143-
]);
158+
table.set_header(vec![Cell::new(contract_id.to_string()).fg(Color::Magenta)]);
144159
table.add_row(vec![
145160
Cell::new("Function Name").add_attribute(Attribute::Bold),
146161
Cell::new("Min").add_attribute(Attribute::Bold),
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use starknet_types_core::felt::Felt;
2+
3+
pub(super) fn shorten_felt(felt: Felt) -> String {
4+
let padded = format!("{felt:#066x}");
5+
let first = &padded[..4];
6+
let last = &padded[padded.len() - 4..];
7+
format!("{first}…{last}")
8+
}
9+
10+
#[cfg(test)]
11+
mod tests {
12+
use super::*;
13+
14+
#[test]
15+
fn test_long() {
16+
let felt = Felt::from_hex_unchecked(
17+
"0x01c902da594beda43db10142ecf1fc3a098b56e8d95f3cd28587a0c6ba05a451",
18+
);
19+
assert_eq!("0x01…a451", &shorten_felt(felt));
20+
}
21+
22+
#[test]
23+
fn test_short() {
24+
let felt = Felt::from_hex_unchecked("0x123");
25+
assert_eq!("0x00…0123", &shorten_felt(felt));
26+
}
27+
}

crates/forge/tests/e2e/gas_report.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::e2e::common::runner::{setup_package, test_runner};
1+
use crate::e2e::common::runner::{
2+
BASE_FILE_PATTERNS, Package, setup_package, setup_package_with_file_patterns, test_runner,
3+
};
24
use indoc::indoc;
35
use shared::test_utils::output_assert::assert_stdout_contains;
46

@@ -115,3 +117,50 @@ fn multiple_contracts_and_constructor() {
115117
"},
116118
);
117119
}
120+
121+
#[test]
122+
fn fork() {
123+
let temp =
124+
setup_package_with_file_patterns(Package::Name("forking".to_string()), BASE_FILE_PATTERNS);
125+
126+
let output = test_runner(&temp)
127+
.arg("test_track_resources")
128+
.arg("--gas-report")
129+
.assert()
130+
.code(0);
131+
132+
assert_stdout_contains(
133+
output,
134+
indoc! {r"
135+
[..]Compiling[..]
136+
[..]Finished[..]
137+
138+
Collected 1 test(s) from forking package
139+
Running 1 test(s) from src/
140+
[PASS] forking::tests::test_track_resources (l1_gas: ~0, l1_data_gas: ~[..], l2_gas: ~[..])
141+
╭-------------------------+-------+-------+-------+---------+---------╮
142+
| forked contract | | | | | |
143+
| (class hash: 0x06…1550) | | | | | |
144+
+=====================================================================+
145+
| Function Name | Min | Max | Avg | Std Dev | # Calls |
146+
|-------------------------+-------+-------+-------+---------+---------|
147+
| get_balance | 40000 | 40000 | 40000 | 0 | 1 |
148+
|-------------------------+-------+-------+-------+---------+---------|
149+
| increase_balance | 40000 | 40000 | 40000 | 0 | 1 |
150+
╰-------------------------+-------+-------+-------+---------+---------╯
151+
152+
╭-------------------------+-------+-------+-------+---------+---------╮
153+
| forked contract | | | | | |
154+
| (class hash: 0x07…af4b) | | | | | |
155+
+=====================================================================+
156+
| Function Name | Min | Max | Avg | Std Dev | # Calls |
157+
|-------------------------+-------+-------+-------+---------+---------|
158+
| get_balance | 13840 | 13840 | 13840 | 0 | 1 |
159+
|-------------------------+-------+-------+-------+---------+---------|
160+
| increase_balance | 25840 | 25840 | 25840 | 0 | 1 |
161+
╰-------------------------+-------+-------+-------+---------+---------╯
162+
163+
Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out
164+
"},
165+
);
166+
}

0 commit comments

Comments
 (0)