Macros
As of January 2021, FRAME based pallets have upgraded their use of macros. Refer to this guide to learn about migrating a v1 pallet to v2 and this resource to learn more about FRAME's version changes.
Substrate uses Rust macros to aggregate the logic derived from pallets that are implemented for a runtime. These runtime macros allow developers to focus on runtime logic rather than encoding and decoding on-chain variables or writing extensive blocks of code to achieve basic blockchain fundamentals. This offloads a lot of the heavy lifting from blockchain development efforts and removes the need to duplicate code.
The purpose of this article is to give a basic overview of Rust macros and explain the Substrate macros that runtime engineers most frequently use.
Macro basics
Put simply, macros are lines of code that write code. They provide the ability to abstract things out without needing to declare complex data structures explicitly. There are four kinds of macro in Rust:
- declarative macros (most widely used)
- custom derive (a type of procedural macro)
- attribute-like macros (a type of procedural macro)
- function-like macros (a type of procedural macro)
Most Substrate runtime macros are defined using either declarative macros or function-like macros, with a recent adoption of attribute-like macros in FRAME v2 pallets.
- Read the documentation on commonly used macros in FRAME.
- Use
cargo expand
to review a macro's expanded code and understand what's happening under the hood. - Read more on macro rules of expression pattern matching.
Substrate runtime macros
The following section is a comprehensive explanation of the macros that runtime engineers most frequently encounter. Developers who want to know about the implementation details are encouraged to follow the links in "Docs and Notes" sub-sections to better understand how each macro expands.
Substrate Primitives and FRAME both rely on a collection of various types of macros. The following sections will go over each in more detail. Here's a general overview of Substrate macros:
Macros in the FRAME Support Library:
- in
frame_support
:- function-like macros
- derive macros
- attribute macros
Macros in the Substrate System Library:
- in
sp_core
:- function-like macros macros
- derive
RuntimeDebug
macro
- in
sp_runtime
:- function-like macros
- derive macros
- in
serde
: function-like macros - in
sp_api
: function-like macros - in
sp_std
: function-like macros - in
sp_version
: function-like macros
Refer to #Substrate dependencies
in the Cargo.toml
file of the
node template runtime
to see where these are put to use.
FRAME macros and attributes
This sections covers all of the different macros that ship with FRAME. Refer to the structure of a pallet to understand the composition of a Substrate pallet.
#[frame_support::pallet]
When to use
Required to declare a pallet consisting of a set of types, functions, and trait implementations that will later be aggregated by construct_runtime
to build to runtime.
What it does
This is the attribute macro that allows the pallet to be used in construct_runtime!
. This macro
is made up of the various attributes that will be used to identify the specific items the pallet requires.
Similar to a derive macro, this macro only expands types and trait implementations by reading the input and its input is almost never touched. The only exception where this macro modifies its input (contrary to a derive macro) is:
- when a generic is replaced with a type. For example, in
pallet::pallet
the inner types of the item inpub struct Pallet<..>(_)
is replaced byPhantomData
and inpallet::storage
, the first generic_
is replaced with a type that implements theStorageInstance
trait. - some item is changed. For example, when
pallet::type_value
changes the function item into a struct and trait implementation. - some docs are added when none are provided by the user. For example, if no documentation is provided the macro
pallet::pallet
will modify the input to add documentation above the itemstruct Pallet<T>(_);
.
#[pallet::config]
When to use
Required to define the pallet's generics.
What it does
Provides constants that are part of the Config
trait and gives information about external tools to use for the runtime.
Docs
#[pallet::constant]
When to use
To pass values of associated types to metadata, which allows the value of the type to be used by external tools and third parties. Note that this attribute is only usable from inside a #[pallet::config]
item.
What it does
Provides the Config
trait with the types and attributes it needs for the runtime and generates associated metadata.
Adding the #[pallet::constant]
attribute will result in the macro adding to the constant metadata, including: the constant's name; the name of the associated type; the constant's value; and the value returned by Get::get()
.
For example, we can use #[pallet::constant]
to put type MyGetParam
in metadata:
#[pallet::config]
pub trait Config: frame_system::Config {
#[pallet::constant] // puts attributes in metadata
type MyGetParam: Get<u32>;
}
Docs
- See use in the context of #[pallet::config]
#[pallet::extra_constants]
When to use
Optionally, for additional constants one may want to pass to metadata.
What it does
It allows to define some additional constants to put into metadata. For example, you could declare a function that returns a value that will be generated to metadata:
#[pallet::extra_constants]
impl<T: Config> Pallet<T> {
//Example function using extra_constants
fn example_extra_constants() -> u128 { 4u128 }
}
Docs
- See documentation
#[pallet::pallet]
When to use
Required to declare the pallet struct place holder, to be later used by construct_runtime
.
What it does
Generates the Store
trait if the attribute is provided, which contains an associated type for each storage item. It takes the form of a more explicit Rust struct module and can be written as pub struct Pallet<T>(_);
, with the appropriate PhantomData
replacing _
to make it generic.
This is an example of its definition:
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::generate_storage_info]
pub struct Pallet<T>(_);
Docs
- See the macro expansion added to the
struct Pallet<T>
definition
#[pallet::generate_storage_info]
When to use
Highly encouraged, but optional which will help ensure all of your pallet storage items are bounded.
What it does
This requires all storage to implement the trait traits::StorageInfoTrait
, thus all keys and value types must bound pallet_prelude::MaxEncodedLen
. Some individual storage can opt-out from this constraint by using #[pallet::unbounded]
, see #[pallet::storage]
documentation.
This is an example of its definition:
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::generate_storage_info]
pub struct Pallet<T>(_);
Docs
- See the macro expansion added to the
struct Pallet<T>
definition
#[pallet::hooks]
When to use
Required for declaring pallet hooks.
What it does
Hooks
are made available by using the Hooks
trait.
For example:
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
// Hooks functions and logic goes here.
}
Docs
- See the documentation
- See the macro expansion
#[pallet::call]
When to use
Required, to implement a pallet's dispatchables. Each dispatchable must:
- define a weight with the
#[pallet::weight($expr)]
attribute - have its first argument as
origin: OriginFor<T>
, - use compact encoding for argument using #[pallet::compact]
- return
DispatchResultWithPostInfo
orDispatchResult
What it does
Extrinsics can use calls to
trigger specific logic. Calls can also be used in on-chain governance, demonstrated by the democracy
pallet where calls can be voted on.
#[pallet::call]
allows to create dispatchable functions which will generate associated items
from the impl
code blocks. In other words, it aggregates all dispatchable logic using the
Call
enum which will aggregate all
dispatchable calls into a single runtime call.
Docs
- See the documentation
#[pallet::error]
When to use
Optionally, to define error types to be used in dispatchables.
What it does
Puts error variants documentation into metadata.
Docs
- See documentation
#[pallet::event]
When to use
Optionally, to define a pallet's event types.
What it does
It is similar to errors but it can holds more information. They generate the metadata of the event and add_derive. It uses the #[pallet::metadata(..)]
attribute to define what metadata to put from the events.
For example:
#[pallet::event]
#[pallet::metadata(u32 = "SpecialU32")]
pub enum Event<T: Config> {
Proposed(u32, T::AccountId),
}
Docs
- See documentation
#[pallet::storage]
When to use
Optionally, as it can be used multiple times.
What it does
To define some abstract storage inside runtime storage.
[pallet::storage]
is using a Rust-like HashMap to do storage, except it takes a Key, Value,
Hasher, Prefix, QueryKind and an OnEmpty
parameter. The prefix generic defines the place where the
storage will store its (key, value)
pairs (i.e. the trie) and defines the pallet and storage prefix like such:
concat(twox_128(pallet_prefix), towx_128(storage_prefix))
, replacing _
with the generated prefix implementations
of the pallet and storage names.
Docs
Other attributes
Other FRAME macro attributes include:
- #[pallet::genesis_config]
- #[pallet::type_value]
- #[pallet::genesis_build]
- #[pallet::inherent]
- #[pallet::validate_unsigned]
- #[pallet::origin]
Additional FRAME macros
construct_runtime!
When to use
To construct Substrate runtime and integrating various pallets into the runtime.
What it does
The macro declares and implements various struct and enum, e.g.Runtime
, Event
, Origin
, Call
,
GenesisConfig
etc, and implements various helper traits for these struct types. The macro also
adds logics of mapping different events and dispatchable calls from the overarching runtime back to
a particular pallet.
Docs and notes
- API Documentation
Runtime
struct type is defined to represent the Substrate runtime.Event
enum type is defined with variants of all pallets that emit events, with helper traits and encoding/decoding traits implemented for the enum. Various conversion traitsEvent
also implementsTryInto<pallets::Event<Runtime>>
trait to extract the event out from the enum type.Origin
enum type is defined with helper traits, e.g.PartialEq
,Clone
,Debug
implemented. This enum type defines who calls an extrinsic:NONE
,ROOT
, or signed by a particular account. These three primitive origins are defined by the FRAME System module, but optional FRAME pallets may also define origins.Call
enum type is defined with all integrated pallets as variants. It contains the data and metadata of each of the integrated pallets, and redirects calls to the specific pallet via implementingframe_support::traits::UnfilteredDispatchable
trait.GenesisConfig
struct type is defined and implementssp_runtime::BuildStorage
trait for building up the storage genesis config.- The macro adopts the
frame_support::unsigned::ValidateUnsigned
trait implementation from each pallet. If noValidateUnsigned
trait is implemented in any included pallets, all unsigned transactions will be rejected.
parameter_types!
When to use
To declare parameter types to be assigned to pallet configurable trait associated types during runtime construction.
What it does
The macro replaces each parameter specified into a struct type with a get()
function returning its
specified value. Each parameter struct type also implements a frame_support::traits::Get<I>
trait
to convert the type to its specified value.
Docs
Substrate system library macros
impl_runtime_apis!
When to use
This macro generates the API implementations for the client side through the RuntimeApi
and
RuntimeApiImpl
struct type.
What it does
The macro defines the RuntimeApi
and RuntimeApiImpl
exposed to the Substrate node client. It
provides implementation details of the RuntimeApiImpl based on the setup and appended user specified
implementation in the RuntimeApiImpl
.
Docs and notes
- API Documentation
RuntimeApi
andRuntimeApiImpl
structs are declared. The macro also implements various helper traits forRuntimeApiImpl
.- What developers define within
impl_runtime_apis!
macro are appended to the end ofRuntimeApiImpl
implementation. - To expose version information about the runtime, a constant
RUNTIME_API_VERSIONS
is defined. containing the runtime coreID
/VERSION
, metadataID
/VERSION
, SessionKeysID
/VERSION
, etc. - A public module
api
is defined with adispatch()
function implemented deciding how various strings are mapped to metadata or chain lifecycle calls.
app_crypto!
When to use
To specify cryptographic key pairs and its signature algorithm that are to be managed by a pallet.
What it does
The macro declares three struct types, Public
, Signature
, and Pair
. Aside from having various
helper traits implemented for these three types, the Public
type is implemented for generating
keypairs, signing and verifying signatures; the Signature
type is to hold the signature property given the chosen signature (e.g. SR25519, ED25519 etc); and the Pair
type is to generate a
public-private key pair from a seed.
Docs and notes
- API Documentation
Public
struct type is declared, and implementssp_application_crypto::AppKey
trait defining the public key type, andsp_application_crypto::RuntimeAppPublic
trait for generating keypairs, signing, and verifying signatures.Signature
struct type is declared, and implementscore::hash::Hash
trait on how the data with this signature type is hashed.Pair
struct type is declared to wrap over the crypto pair. This type implementssp_application_crypto::Pair
andsp_application_crypto::AppKey
traits determining how it generates public-private key pairs from a phrase or seed.