Skip to content

Commit daa4640

Browse files
Display events in the debugging trace
1 parent 6e162e9 commit daa4640

18 files changed

Lines changed: 259 additions & 16 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424
- `#[should_panic(expected: (...))]` now supports regular strings inside mixed tuples, alongside short strings and numbers.
2525
- `#[derive(Fuzzable)]` macro that automatically generates `Fuzzable` trait implementations for structs and enums
2626
- `SNFOUNDRY_CACHE` environment variable to allow to specify a custom cache directory
27+
- Contract debug traces now include emitted Starknet events via the `events` trace component and `detailed` trace verbosity.
2728

2829
#### Changed
2930

crates/data-transformer/src/shared/path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,5 @@ fn extract_generic_args(
8686
return Err(PathSplitError::MoreThanOneGenericArg);
8787
};
8888

89-
Ok(generic_arg.to_string())
89+
Ok((*generic_arg).to_string())
9090
}

crates/debugging/src/trace/collect.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::contracts_data_store::ContractsDataStore;
22
use crate::trace::types::{
3-
CallerAddress, ContractAddress, ContractName, ContractTrace, Gas, Selector, TestName,
4-
TraceInfo, TransformedCallResult, TransformedCalldata,
3+
CallerAddress, ContractAddress, ContractName, ContractTrace, Event, Events, Gas, Selector,
4+
TestName, TraceInfo, TransformedCallResult, TransformedCalldata,
55
};
66
use crate::{Context, Trace};
7+
use blockifier::execution::call_info::OrderedEvent;
78
use cheatnet::runtime_extensions::outer_call_runtime_extension::rpc::{CallFailure, CallSuccess};
89
use cheatnet::trace_data::{CallTrace, CallTraceNode};
910
use data_transformer::{ReverseTransformError, reverse_transform_input, reverse_transform_output};
@@ -51,6 +52,7 @@ impl<'a> Collector<'a> {
5152
call_type: components.call_type(entry_point.call_type),
5253
nested_calls,
5354
call_result: components.call_result_lazy(|| self.collect_transformed_call_result(abi)),
55+
events: components.events_lazy(|| self.collect_events()),
5456
gas: components.gas_lazy(|| self.collect_gas()),
5557
};
5658

@@ -148,6 +150,23 @@ impl<'a> Collector<'a> {
148150
.l2_gas)
149151
}
150152

153+
fn collect_events(&self) -> Events {
154+
Events(
155+
self.call_trace
156+
.events
157+
.iter()
158+
.map(Self::collect_event)
159+
.collect(),
160+
)
161+
}
162+
163+
fn collect_event(event: &OrderedEvent) -> Event {
164+
Event {
165+
keys: event.event.keys.iter().map(|key| key.0).collect(),
166+
data: event.event.data.0.clone(),
167+
}
168+
}
169+
151170
fn class_hash(&self) -> &ClassHash {
152171
self.call_trace
153172
.entry_point

crates/debugging/src/trace/components.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::trace::types::{
2-
CallerAddress, ContractAddress, ContractName, Gas, TransformedCallResult, TransformedCalldata,
2+
CallerAddress, ContractAddress, ContractName, Events, Gas, TransformedCallResult,
3+
TransformedCalldata,
34
};
45
use blockifier::execution::entry_point::CallType;
56
use paste::paste;
@@ -42,6 +43,8 @@ pub enum Component {
4243
CallType,
4344
/// The result of the call, transformed for display.
4445
CallResult,
46+
/// The raw events emitted by the call.
47+
Events,
4548
/// The L2 gas used by the call.
4649
Gas,
4750
}
@@ -125,3 +128,4 @@ impl_component_container!(CallerAddress);
125128
impl_component_container!(CallType);
126129
impl_component_container!(CallResult, TransformedCallResult);
127130
impl_component_container!(Gas);
131+
impl_component_container!(Events);

crates/debugging/src/trace/types.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ use crate::Context;
22
use crate::trace::collect::Collector;
33
use crate::trace::components::{
44
CallResultContainer, CallTypeContainer, CalldataContainer, CallerAddressContainer,
5-
ContractAddressContainer, ContractNameContainer, EntryPointTypeContainer, GasContainer,
5+
ContractAddressContainer, ContractNameContainer, EntryPointTypeContainer, EventsContainer,
6+
GasContainer,
67
};
78
use crate::tree::TreeSerialize;
89
use cheatnet::trace_data::CallTrace;
910
use starknet_api::core::ContractAddress as ApiContractAddress;
1011
use starknet_api::execution_resources::GasAmount as ApiGasAmount;
12+
use starknet_types_core::felt::Felt;
1113
use std::fmt;
1214
use std::fmt::Display;
1315

@@ -33,6 +35,7 @@ pub struct TraceInfo {
3335
pub call_type: CallTypeContainer,
3436
pub nested_calls: Vec<ContractTrace>,
3537
pub call_result: CallResultContainer,
38+
pub events: EventsContainer,
3639
pub gas: GasContainer,
3740
}
3841

@@ -60,6 +63,15 @@ pub struct CallerAddress(pub ApiContractAddress);
6063
#[derive(Debug, Clone)]
6164
pub struct Gas(pub ApiGasAmount);
6265

66+
#[derive(Debug, Clone)]
67+
pub struct Events(pub Vec<Event>);
68+
69+
#[derive(Debug, Clone)]
70+
pub struct Event {
71+
pub keys: Vec<Felt>,
72+
pub data: Vec<Felt>,
73+
}
74+
6375
impl Trace {
6476
/// Creates a new [`Trace`] from a given [`Context`] and a test name.
6577
#[must_use]

crates/debugging/src/tree/ui/as_tree_node.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ impl AsTreeNode for TraceInfo {
3434
parent.leaf_optional(self.caller_address.as_option());
3535
parent.leaf_optional(self.call_type.as_option());
3636
parent.leaf_optional(self.call_result.as_option());
37+
parent.leaf_optional(self.events.as_option());
3738
parent.leaf_optional(self.gas.as_option());
3839
for nested_call in &self.nested_calls {
3940
parent.as_tree_node(nested_call);

crates/debugging/src/tree/ui/display.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::trace::types::{
2-
CallerAddress, ContractAddress, ContractName, Gas, Selector, TestName, TransformedCallResult,
3-
TransformedCalldata,
2+
CallerAddress, ContractAddress, ContractName, Event, Events, Gas, Selector, TestName,
3+
TransformedCallResult, TransformedCalldata,
44
};
55
use blockifier::execution::entry_point::CallType;
66
use starknet_api::contract_class::EntryPointType;
@@ -84,6 +84,20 @@ impl NodeDisplay for TransformedCallResult {
8484
}
8585
}
8686

87+
impl NodeDisplay for Events {
88+
const TAG: &'static str = "events";
89+
fn string_pretty(&self) -> String {
90+
format!(
91+
"[{}]",
92+
self.0
93+
.iter()
94+
.map(Event::string_pretty)
95+
.collect::<Vec<_>>()
96+
.join(", ")
97+
)
98+
}
99+
}
100+
87101
impl NodeDisplay for Gas {
88102
const TAG: &'static str = "L2 gas";
89103
fn string_pretty(&self) -> String {
@@ -102,3 +116,21 @@ fn string_hex(data: impl Into<Felt>) -> String {
102116
fn string_debug(data: impl Debug) -> String {
103117
format!("{data:?}")
104118
}
119+
120+
impl Event {
121+
fn string_pretty(&self) -> String {
122+
format!(
123+
"Event {{ keys: [{}], data: [{}] }}",
124+
self.keys
125+
.iter()
126+
.map(Felt::to_hex_string)
127+
.collect::<Vec<_>>()
128+
.join(", "),
129+
self.data
130+
.iter()
131+
.map(Felt::to_hex_string)
132+
.collect::<Vec<_>>()
133+
.join(", "),
134+
)
135+
}
136+
}

crates/forge-runner/src/debugging/component.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub enum Component {
1919
CallType,
2020
/// The result of the call, transformed for display.
2121
CallResult,
22+
/// The raw events emitted by the call.
23+
Events,
2224
/// The L2 gas used by the call.
2325
Gas,
2426
}
@@ -33,6 +35,7 @@ impl Component {
3335
| Component::CallerAddress
3436
| Component::EntryPointType
3537
| Component::CallType
38+
| Component::Events
3639
| Component::Gas => TraceVerbosity::Detailed,
3740
}
3841
}
@@ -48,6 +51,7 @@ impl From<&Component> for debugging::Component {
4851
Component::CallerAddress => debugging::Component::CallerAddress,
4952
Component::CallType => debugging::Component::CallType,
5053
Component::CallResult => debugging::Component::CallResult,
54+
Component::Events => debugging::Component::Events,
5155
Component::Gas => debugging::Component::Gas,
5256
}
5357
}

crates/forge/tests/data/debugging/src/lib.cairo

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct RecursiveCall {
88

99
#[starknet::interface]
1010
trait RecursiveCaller<T> {
11-
fn execute_calls(self: @T, calls: Array<RecursiveCall>) -> Array<RecursiveCall>;
11+
fn execute_calls(ref self: T, calls: Array<RecursiveCall>) -> Array<RecursiveCall>;
1212
}
1313

1414
#[starknet::interface]
@@ -20,7 +20,7 @@ trait Failing<TContractState> {
2020
mod SimpleContract {
2121
use core::array::ArrayTrait;
2222
use core::traits::Into;
23-
use starknet::{ContractAddress, get_contract_address};
23+
use starknet::ContractAddress;
2424
use super::{
2525
Failing, RecursiveCall, RecursiveCaller, RecursiveCallerDispatcher,
2626
RecursiveCallerDispatcherTrait,
@@ -30,12 +30,24 @@ mod SimpleContract {
3030
#[storage]
3131
struct Storage {}
3232

33+
#[event]
34+
#[derive(Drop, starknet::Event)]
35+
enum Event {
36+
CallsExecuted: CallsExecuted,
37+
}
38+
39+
#[derive(Drop, starknet::Event)]
40+
struct CallsExecuted {
41+
calls_len: felt252,
42+
}
3343

3444
#[abi(embed_v0)]
3545
impl RecursiveCallerImpl of RecursiveCaller<ContractState> {
3646
fn execute_calls(
37-
self: @ContractState, calls: Array<RecursiveCall>,
47+
ref self: ContractState, calls: Array<RecursiveCall>,
3848
) -> Array<RecursiveCall> {
49+
self.emit(Event::CallsExecuted(CallsExecuted { calls_len: calls.len().into() }));
50+
3951
let mut i = 0;
4052
#[cairofmt::skip]
4153
while i < calls.len() {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "debugging_events"
3+
version = "0.1.0"
4+
edition = "2023_01"
5+
6+
[dependencies]
7+
starknet = ">=2.8.0"
8+
9+
[dev-dependencies]
10+
snforge_std = { path = "../../../../../snforge_std" }

0 commit comments

Comments
 (0)