Pallet Events
When a callable function completes successfully, there is often some metadata you would like to expose to the outside world about what exactly happened during the execution.
Events allow Pallets to express that something has happened, and allows off-chain systems like indexers or block explorers to track certain state transitions.
Event Macro
The #[pallet::event]
macro acts on an enum Event
.
#![allow(unused)] fn main() { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { Created { owner: T::AccountId }, } }
In this enum, you can introduce new variants as objects with arbitrary fields. Obviously you don't want to stick a ton of data in there, but you should feel comfortable to put the data which is relevant for tools like indexers and block explorers.
In this case, we set you up with a simple event stating a new kitty was created, and by whom. Of course there is no logic which is actually doing that yet, but that is what we will start to work on next.
Emitting an event is usually the last thing you will do in your extrinsic, noting when everything is done and with any final values you might have generated.
We will probably want to update our Created
event with details about the Kitty we created. We can do that in the future.
Macro Magic
You might ask, "What is this generate_deposit
stuff?
When we deposit an event, we actually have to pass our event to frame_system
, which manages events across all pallets.
The code for that function is:
#![allow(unused)] fn main() { impl<T: Config> Pallet<T> { pub(super) fn deposit_event(event: Event<T>) { let event = <<T as Config>::RuntimeEvent as From<Event<T>>>::from(event); let event = <<T as Config>::RuntimeEvent as Into< <T as frame_system::Config>::RuntimeEvent, >>::into(event); <frame_system::Pallet<T>>::deposit_event(event) } } }
Rather than asking the user to remember and write this every time, we are able to automatically generate it for the user.
Do you not like macro magic?
Delete the generate_deposit
line, and copy and paste this code block into your code!
It is literally the same. In this case, I think the macro magic is justified.
You are able to access this function like you could any other function implemented on Pallet
:
#![allow(unused)] fn main() { Self::deposit_event(Event::<T>::Created { owner }); }
As you see in our starting code.
#![allow(unused)] #![cfg_attr(not(feature = "std"), no_std)] fn main() { mod impls; mod tests; use frame::prelude::*; pub use pallet::*; #[frame::pallet(dev_mode)] pub mod pallet { use super::*; #[pallet::pallet] pub struct Pallet<T>(core::marker::PhantomData<T>); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; } /* 🚧 TODO 🚧: Learn about Pallet Events. */ #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { Created { owner: T::AccountId }, } #[pallet::error] pub enum Error<T> {} #[pallet::call] impl<T: Config> Pallet<T> { pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult { let who = ensure_signed(origin)?; Self::mint(who)?; Ok(()) } } } }
diff --git a/src/lib.rs b/src/lib.rs
index 405a6e52..a52896ed 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,12 +13,12 @@ pub mod pallet {
#[pallet::pallet]
pub struct Pallet<T>(core::marker::PhantomData<T>);
- /* 🚧 TODO 🚧: Learn about Pallet `Config` and `frame_system`. */
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
+ /* 🚧 TODO 🚧: Learn about Pallet Events. */
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {