Events
Events in Cairo smart contracts allow you to emit and record data on the Starknet blockchain. They are essential for tracking important state changes and providing transparency to users and other contracts. Events are also useful when building interfaces, to be notified about important state changes.
To use events in your contract:
- Create event structs that derive the
starknet::Event
trait - Define an
Event
enum in the contract, annotated with#[event]
, where each variant is linked to an event struct - Emit events with the
emit
function
You can make events searchable by adding the #[key]
attribute to specific fields, which indexes them for efficient querying later.
Events variant names and structs are recommended to be named consistently, even if it create some redundancy when emitting events.
Here's a practical example of a contract that emits events when incrementing a counter:
#[starknet::interface]
trait IEventCounter<TContractState> {
fn increment(ref self: TContractState, amount: u128);
}
mod Events {
// Events must derive the `starknet::Event` trait
#[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
pub struct CounterIncreased {
pub amount: u128,
}
#[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
pub struct UserIncreaseCounter {
// The `#[key]` attribute indicates that this event will be indexed.
// You can also use `#[flat]` for nested structs.
#[key]
pub user: starknet::ContractAddress,
pub new_value: u128,
}
}
#[starknet::contract]
mod EventCounter {
use super::IEventCounter;
use super::Events::*;
use starknet::get_caller_address;
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
#[storage]
struct Storage {
// Counter value
counter: u128,
}
#[event]
#[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
// The event enum must be annotated with the `#[event]` attribute.
// It must also derive at least the `Drop` and `starknet::Event` traits.
pub enum Event {
CounterIncreased: CounterIncreased,
UserIncreaseCounter: UserIncreaseCounter,
}
#[abi(embed_v0)]
impl EventCounter of IEventCounter<ContractState> {
fn increment(ref self: ContractState, amount: u128) {
self.counter.write(self.counter.read() + amount);
// Emit event
self.emit(Event::CounterIncreased(CounterIncreased { amount }));
self
.emit(
Event::UserIncreaseCounter(
UserIncreaseCounter {
user: get_caller_address(), new_value: self.counter.read(),
},
),
);
}
}
}