Skip to content

Commit c5d1e90

Browse files
Display events in the debugging trace
1 parent 7086c7e commit c5d1e90

18 files changed

Lines changed: 262 additions & 16 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Forge
1111

12+
#### Added
13+
14+
- Contract debug traces now include emitted Starknet events via the `events` trace component and `detailed` trace verbosity.
15+
1216
#### Changed
1317

1418
- `snforge_scarb_plugin` diagnostics for named-argument kind violations now include both possible values and invalid arguments found.

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::call_to_blockifier_runtime_extension::rpc::{
89
CallFailure, CallSuccess,
910
};
@@ -53,6 +54,7 @@ impl<'a> Collector<'a> {
5354
call_type: components.call_type(entry_point.call_type),
5455
nested_calls,
5556
call_result: components.call_result_lazy(|| self.collect_transformed_call_result(abi)),
57+
events: components.events_lazy(|| self.collect_events()),
5658
gas: components.gas_lazy(|| self.collect_gas()),
5759
};
5860

@@ -150,6 +152,23 @@ impl<'a> Collector<'a> {
150152
.l2_gas)
151153
}
152154

155+
fn collect_events(&self) -> Events {
156+
Events(
157+
self.call_trace
158+
.events
159+
.iter()
160+
.map(Self::collect_event)
161+
.collect(),
162+
)
163+
}
164+
165+
fn collect_event(event: &OrderedEvent) -> Event {
166+
Event {
167+
keys: event.event.keys.iter().map(|key| key.0).collect(),
168+
data: event.event.data.0.clone(),
169+
}
170+
}
171+
153172
fn class_hash(&self) -> &ClassHash {
154173
self.call_trace
155174
.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)