Events
Events are custom data structures that are emitted by smart contracts during execution. They provide a way for smart contracts to communicate with the external world by logging information about specific occurrences in a contract.
Los acontecimientos desempeñan un papel crucial en la creación de contratos inteligentes. Tomemos, por ejemplo, las fichas no fungibles (NFT) acuñadas en Starknet. Todos ellos se indexan y almacenan en una base de datos, y luego se muestran a los usuarios mediante el uso de estos eventos. Descuidar la inclusión de un evento dentro de su contrato NFT podría conducir a una mala experiencia de usuario. Esto se debe a que los usuarios pueden no ver sus NFTs aparecer en sus carteras (las carteras utilizan estos indexadores para mostrar los NFTs de un usuario).
Defining events
All the different events in the contract are defined under the Event
enum, which implements the starknet::Event
trait, as enum variants. This trait is defined in the core library as follows:
trait Event<T> {
fn append_keys_and_data(self: T, ref keys: Array<felt252>, ref data: Array<felt252>);
fn deserialize(ref keys: Span<felt252>, ref data: Span<felt252>) -> Option<T>;
}
The #[derive(starknet::Event)]
attribute causes the compiler to generate an implementation for the above trait,
instantiated with the Event type, which in our example is the following enum:
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
StoredName: StoredName,
}
#[derive(Drop, starknet::Event)]
struct StoredName {
#[key]
user: ContractAddress,
name: felt252
}
Each event variant has to be a struct of the same name as the variant, and each variant needs to implement the starknet::Event
trait itself.
Moreover, the members of these variants must implement the Serde
trait (c.f. Appendix C: Serializing with Serde), as keys/data are added to the event using a serialization process.
The auto implementation of the starknet::Event
trait will implement the append_keys_and_data
function for each variant of our Event
enum. The generated implementation will append a single key based on the variant name (StoredName
), and then recursively call append_keys_and_data
in the impl of the Event trait for the variant’s type .
In our contract, we define an event named StoredName
that emits the contract address of the caller and the name stored within the contract, where the user
field is serialized as a key and the name
field is serialized as data.
To index the key of an event, simply annotate it with the #[key]
as demonstrated in the example for the user
key.
When emitting the event with self.emit(StoredName { user: user, name: name })
, a key corresponding to the name StoredName
, specifically sn_keccak(StoredName)
, is appended to the keys list. user
is serialized as key, thanks to the #[key]
attribute, while address is serialized as data. After everything is processed, we end up with the following keys and data: keys = [sn_keccak("StoredName"),user]
and data = [address]
.
Emitting events
After defining events, we can emit them using self.emit
, with the following syntax:
self.emit(StoredName { user: user, name: name });