Events
A pallet can emit events when it wants to notify external entities about changes or conditions in the runtime to external entities like users, chain explorers, or dApps.
You can define what events your pallet emits, what information is contained within those events, and when those events are emitted.
Declaring an event
Runtime events are created with the #[pallet::event]
macro.
#[pallet::event]
#[pallet::metadata(u32 = "Metadata")]
pub enum Event<T: Config> {
/// Set a value.
ValueSet(u32, T::AccountId),
}
The Event
enum needs to be declared in your runtime's configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}
Exposing events to your runtime
Your pallet's events need to be exposed to your node's runtime (/runtime/src/lib.rs
).
First, you need to implement the Event
type in your pallet's configuration trait:
// runtime/src/lib.rs
impl template::Config for Runtime {
type Event = Event;
}
Then you need to add the Event
type to your construct_runtime!
macro:
// runtime/src/lib.rs
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// --snip--
TemplateModule: template::{Pallet, Call, Storage, Event<T>},
//--add-this------------------------------------->^^^^^^^^
}
);
You may or may not need the <T>
parameter depending on whether your events use generic
types. In our example it does, and is included above.
Depositing an event
Substrate provides a default implementation of how to deposit an event using macros. Depositing an event has the following structure:
// 1. Use the `generate_deposit` attribute when declaring the Events enum.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)] // <------ here ----
#[pallet::metadata(...)]
pub enum Event<T: Config> {
// --snip--
}
// 2. Use `deposit_event` inside the dispatchable function
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(1_000)]
pub(super) fn set_value(
origin: OriginFor<T>,
value: u64,
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
// --snip--
Self::deposit_event(RawEvent::ValueSet(value, sender));
}
}
The default behavior of this function is to call
deposit_event
from the FRAME system, which writes the event to storage.
This function places the event in the System pallet's runtime storage for that block. At the beginning of a new block, the System pallet automatically removes all events that were stored from the previous block.
Events deposited using the default implementation will be directly supported by downstream libraries
like the Polkadot-JS API, however you can implement your own
deposit_event
function if you want to handle events differently.
Supported types
Events can emit any type which supports the Parity SCALE codec.
In the case where you want to use Runtime generic types like AccountId
or Balances
, you need to
include a where
clause to define
those types as shown in the example above.
Listening to events
The Substrate RPC does not directly expose an endpoint for querying events. If you used the default implementation, you can see the list of events for the current block by querying the storage of the System pallet. Otherwise, the Polkadot-JS API supports a WebSocket subscription on runtime events.
Errors
Runtime code should explicitly and gracefully handle all error cases, which is to say that runtime
code must be "non-throwing", or must never
"panic" to use Rust
terminology. A common idiom for writing non-throwing Rust code is to write functions that return
Result
types.
The Result
enum type possesses an Err
variant that allows a function to indicate that it failed
to execute successfully without needing to panic. Dispatchable calls in the FRAME system for runtime
development must return a
DispatchResult
type
that could be a
DispatchError
variant
if the dispatchable function encountered an error.
Each FRAME pallet may define a custom DispatchError
by using the #[pallet::error]
macro.
For example:
#[pallet::error]
pub enum Error<T> {
/// Error names should be descriptive.
InvalidParameter,
/// Errors should have helpful documentation associated with them.
OutOfSpace,
}
The
Substrate node template
demonstrates some ways to correctly handle errors in dispatchable functions. The FRAME Support
module also includes a helpful
ensure!
macro that can be
used to check pre-conditions and emit an errors if they are not met.
frame_support::ensure!(param < T::MaxVal::get(), Error::<T>::InvalidParameter);
Next steps
Learn more
- Learn more about the macros used in Substrate runtime development.
- Learn more about using the Polkadot-JS API.
Examples
- Learn about Events and Errors by completing the Substrate Kitties tutorial