Attribute Macro frame_support::pallet
source · #[pallet]
Expand description
The pallet
attribute macro defines a pallet that can be used with
construct_runtime!
. It must be attached to a module named pallet
as follows:
#[pallet]
pub mod pallet {
...
}
Note that various types can be automatically imported using
frame_support::pallet_prelude
and frame_system::pallet_prelude
:
#[pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
...
}
pallet::* Attributes
The pallet
macro will parse any items within your pallet
module that are annotated with
#[pallet::*]
attributes. Some of these attributes are mandatory and some are optional,
and they can attach to different types of items within your pallet depending on the
attribute in question. The full list of #[pallet::*]
attributes is shown below in the
order in which they are mentioned in this document:
pallet::pallet
pallet::config
pallet::constant
pallet::disable_frame_system_supertrait_check
pallet::generate_store($vis trait Store)
pallet::storage_version
pallet::hooks
pallet::call
pallet::weight($expr)
pallet::compact
pallet::call_index($idx)
pallet::extra_constants
pallet::error
pallet::event
pallet::generate_deposit($visibility fn deposit_event)
pallet::storage
pallet::getter(fn $my_getter_fn_name)
pallet::storage_prefix = "SomeName"
pallet::unbounded
pallet::whitelist_storage
cfg(..)
(on storage items)pallet::type_value
pallet::genesis_config
pallet::genesis_build
pallet::inherent
pallet::validate_unsigned
pallet::origin
pallet::composite_enum
Note that at compile-time, the #[pallet]
macro will analyze and expand all of these
attributes, ultimately removing their AST nodes before they can be parsed as real
attribute macro calls. This means that technically we do not need attribute macro
definitions for any of these attributes, however, for consistency and discoverability
reasons, we still maintain stub attribute macro definitions for all of these attributes in
the pallet_macros
module which is automatically included in all pallets as part of the
pallet prelude. The actual “work” for all of these attribute macros can be found in the
macro expansion for #[pallet]
.
Also note that in this document, pallet attributes are explained using the syntax of non-instantiable pallets. For an example of an instantiable pallet, see this example.
Dev Mode (#[pallet(dev_mode)]
)
Specifying the argument dev_mode
on the #[pallet]
or #[frame_support::pallet]
attribute attached to your pallet module will allow you to enable dev mode for a pallet.
The aim of dev mode is to loosen some of the restrictions and requirements placed on
production pallets for easy tinkering and development. Dev mode pallets should not be used
in production. Enabling dev mode has the following effects:
- Weights no longer need to be specified on every
#[pallet::call]
declaration. By default, dev mode pallets will assume a weight of zero (0
) if a weight is not specified. This is equivalent to specifying#[weight(0)]
on all calls that do not specify a weight. - Call indices no longer need to be specified on every
#[pallet::call]
declaration. By default, dev mode pallets will assume a call index based on the order of the call. - All storages are marked as unbounded, meaning you do not need to implement
MaxEncodedLen
on storage types. This is equivalent to specifying#[pallet::unbounded]
on all storage type definitions. - Storage hashers no longer need to be specified and can be replaced by
_
. In dev mode, these will be replaced byBlake2_128Concat
. In case of explicit key-binding,Hasher
can simply be ignored when indev_mode
.
Note that the dev_mode
argument can only be supplied to the #[pallet]
or
#[frame_support::pallet]
attribute macro that encloses your pallet module. This argument
cannot be specified anywhere else, including but not limited to the #[pallet::pallet]
attribute macro.
WARNING: You should not deploy or use dev mode pallets in production. Doing so can break your chain and therefore should never be done. Once you are done tinkering, you should remove the 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before attempting to use your pallet in a production scenario.
Pallet struct placeholder: #[pallet::pallet]
(mandatory)
The pallet struct placeholder #[pallet::pallet]
is mandatory and allows you to specify
pallet information.
The struct must be defined as follows:
#[pallet::pallet]
pub struct Pallet<T>(_);
I.e. a regular struct definition named Pallet
, with generic T and no where clause.
Macro expansion:
The macro adds this attribute to the struct definition:
#[derive(
frame_support::CloneNoBound,
frame_support::EqNoBound,
frame_support::PartialEqNoBound,
frame_support::RuntimeDebugNoBound,
)]
and replaces the type _
with PhantomData<T>
. It also implements on the pallet:
GetStorageVersion
OnGenesis
: contains some logic to write the pallet version into storage.PalletErrorTypeInfo
: provides the type information for the pallet error, if defined.
It declares type Module
type alias for Pallet
, used by construct_runtime
.
It implements PalletInfoAccess
on Pallet
to ease access
to pallet information given by frame_support::traits::PalletInfo
. (The implementation
uses the associated type frame_system::Config::PalletInfo
).
It implements StorageInfoTrait
on Pallet
which give
information about all storages.
If the attribute generate_store
is set then the macro creates the trait Store
and
implements it on Pallet
.
If the attribute set_storage_max_encoded_len
is set then the macro calls
StorageInfoTrait
for each storage in the implementation of
StorageInfoTrait
for the pallet. Otherwise it implements
StorageInfoTrait
for the pallet using the
PartialStorageInfoTrait
implementation of storages.
Config trait: #[pallet::config]
(mandatory)
The mandatory attribute #[pallet::config]
defines the configurable options for the
pallet.
Item must be defined as:
#[pallet::config]
pub trait Config: frame_system::Config + $optionally_some_other_supertraits
$optional_where_clause
{
...
}
I.e. a regular trait definition named Config
, with the supertrait
frame_system::pallet::Config
, and optionally other supertraits and a where clause.
(Specifying other supertraits here is known as tight
coupling)
The associated type RuntimeEvent
is reserved. If defined, it must have the bounds
From<Event>
and IsType<<Self as frame_system::Config>::RuntimeEvent>
.
pallet::event
must be present if RuntimeEvent
exists as a config item in your #[pallet::config]
.
Also see pallet::config
pallet::constant
The #[pallet::constant]
attribute can be used to add an associated type trait bounded by
Get
from pallet::config
into metadata, e.g.:
#[pallet::config]
pub trait Config: frame_system::Config {
#[pallet::constant]
type Foo: Get<u32>;
}
Also see pallet::constant
pallet::disable_frame_system_supertrait_check
To bypass the frame_system::Config
supertrait check, use the attribute
pallet::disable_frame_system_supertrait_check
, e.g.:
#[pallet::config]
#[pallet::disable_frame_system_supertrait_check]
pub trait Config: pallet_timestamp::Config {}
NOTE: Bypassing the frame_system::Config
supertrait check is typically desirable when you
want to write an alternative to the frame_system
pallet.
Also see
pallet::disable_frame_system_supertrait_check
Macro expansion:
The macro expands pallet constant metadata with the information given by
#[pallet::constant]
.
pallet::generate_store($vis trait Store)
To generate a Store
trait associating all storages, annotate your Pallet
struct with
the attribute #[pallet::generate_store($vis trait Store)]
, e.g.:
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
More precisely, the Store
trait contains an associated type for each storage. It is
implemented for Pallet
allowing access to the storage from pallet struct.
Thus when defining a storage named Foo
, it can later be accessed from Pallet
using
<Pallet as Store>::Foo
.
NOTE: this attribute is only valid when applied directly to your Pallet
struct
definition.
Also see pallet::generate_store
.
pallet::storage_version
Because the pallet::pallet
macro
implements traits::GetStorageVersion
, the current storage version needs to be
communicated to the macro. This can be done by using the pallet::storage_version
attribute:
const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);
If not present, the current storage version is set to the default value.
Also see pallet::storage_version
Hooks: #[pallet::hooks]
(optional)
The pallet::hooks
attribute allows you to specify a Hooks
implementation for Pallet
that specifies pallet-specific logic.
The item the attribute attaches to must be defined as follows:
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> $optional_where_clause {
...
}
I.e. a regular trait implementation with generic bound: T: Config
, for the trait
Hooks<BlockNumberFor<T>>
(they are defined in preludes), for the type Pallet<T>
and
with an optional where clause.
If no #[pallet::hooks]
exists, then the following default implementation is
automatically generated:
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
Also see pallet::hooks
Call: #[pallet::call]
(optional)
Implementation of pallet dispatchables.
Item must be defined as:
#[pallet::call]
impl<T: Config> Pallet<T> {
/// $some_doc
#[pallet::weight($ExpressionResultingInWeight)]
pub fn $fn_name(
origin: OriginFor<T>,
$some_arg: $some_type,
// or with compact attribute: #[pallet::compact] $some_arg: $some_type,
...
) -> DispatchResultWithPostInfo { // or `-> DispatchResult`
...
}
...
}
I.e. a regular type implementation, with generic T: Config
, on type Pallet<T>
, with
an optional where clause.
#[pallet::weight($expr)]
Each dispatchable needs to define a weight with #[pallet::weight($expr)]
attribute, the
first argument must be origin: OriginFor<T>
.
Also see pallet::weight
#[pallet::compact] $some_arg: $some_type
Compact encoding for arguments can be achieved via #[pallet::compact]
. The function must
return a DispatchResultWithPostInfo
or DispatchResult
.
Also see pallet::compact
#[pallet::call_index($idx)]
Each dispatchable may also be annotated with the #[pallet::call_index($idx)]
attribute,
which explicitly defines the codec index for the dispatchable function in the Call
enum.
All call indexes start from 0, until it encounters a dispatchable function with a defined
call index. The dispatchable function that lexically follows the function with a defined
call index will have that call index, but incremented by 1, e.g. if there are 3
dispatchable functions fn foo
, fn bar
and fn qux
in that order, and only fn bar
has a call index of 10, then fn qux
will have an index of 11, instead of 1.
WARNING: modifying dispatchables, changing their order, removing some, etc., must be
done with care. Indeed this will change the outer runtime call type (which is an enum with
one variant per pallet), this outer runtime call can be stored on-chain (e.g. in
pallet-scheduler
). Thus migration might be needed. To mitigate against some of this, the
#[pallet::call_index($idx)]
attribute can be used to fix the order of the dispatchable so
that the Call
enum encoding does not change after modification. As a general rule of
thumb, it is therefore adventageous to always add new calls to the end so you can maintain
the existing order of calls.
Also see pallet::call_index
Extra constants: #[pallet::extra_constants]
(optional)
Allows you to define some extra constants to be added into constant metadata.
Item must be defined as:
#[pallet::extra_constants]
impl<T: Config> Pallet<T> where $optional_where_clause {
/// $some_doc
$vis fn $fn_name() -> $some_return_type {
...
}
...
}
I.e. a regular rust impl
block with some optional where clause and functions with 0 args,
0 generics, and some return type.
Macro expansion
The macro add some extra constants to pallet constant metadata.
Also see: pallet::extra_constants
Error: #[pallet::error]
(optional)
The #[pallet::error]
attribute allows you to define an error enum that will be returned
from the dispatchable when an error occurs. The information for this error type is then
stored in metadata.
Item must be defined as:
#[pallet::error]
pub enum Error<T> {
/// $some_optional_doc
$SomeFieldLessVariant,
/// $some_more_optional_doc
$SomeVariantWithOneField(FieldType),
...
}
I.e. a regular enum named Error
, with generic T
and fieldless or multiple-field
variants.
Any field type in the enum variants must implement scale_info::TypeInfo
in order to be
properly used in the metadata, and its encoded size should be as small as possible,
preferably 1 byte in size in order to reduce storage size. The error enum itself has an
absolute maximum encoded size specified by MAX_MODULE_ERROR_ENCODED_SIZE
.
(1 byte can still be 256 different errors. The more specific the error, the easier it is to diagnose problems and give a better experience to the user. Don’t skimp on having lots of individual error conditions.)
Field types in enum variants must also implement PalletError
,
otherwise the pallet will fail to compile. Rust primitive types have already implemented
the PalletError
trait along with some commonly used stdlib types
such as Option
and PhantomData
, and
hence in most use cases, a manual implementation is not necessary and is discouraged.
The generic T
must not bound anything and a where
clause is not allowed. That said,
bounds and/or a where clause should not needed for any use-case.
Also see: pallet::error
Event: #[pallet::event]
(optional)
Allows you to define pallet events. Pallet events are stored under the system
/ events
key when the block is applied (and then replaced when the next block writes it’s events).
The Event enum must be defined as follows:
#[pallet::event]
#[pallet::generate_deposit($visibility fn deposit_event)] // Optional
pub enum Event<$some_generic> $optional_where_clause {
/// Some doc
$SomeName($SomeType, $YetanotherType, ...),
...
}
I.e. an enum (with named or unnamed fields variant), named Event
, with generic: none or
T
or T: Config
, and optional w here clause.
Each field must implement Clone
, Eq
, PartialEq
, Encode
, Decode
, and
Debug
(on std only). For ease of use, bound by the trait
Member
, available in
frame_support::pallet_prelude.
Also see pallet::event
#[pallet::generate_deposit($visibility fn deposit_event)]
The attribute #[pallet::generate_deposit($visibility fn deposit_event)]
generates a
helper function on Pallet
that handles deposit events.
NOTE: For instantiable pallets, the event must be generic over T
and I
.
Also see pallet::generate_deposit
Storage: #[pallet::storage]
(optional)
The #[pallet::storage]
attribute lets you define some abstract storage inside of runtime
storage and also set its metadata. This attribute can be used multiple times.
Item should be defined as:
#[pallet::storage]
#[pallet::getter(fn $getter_name)] // optional
$vis type $StorageName<$some_generic> $optional_where_clause
= $StorageType<$generic_name = $some_generics, $other_name = $some_other, ...>;
or with unnamed generic:
#[pallet::storage]
#[pallet::getter(fn $getter_name)] // optional
$vis type $StorageName<$some_generic> $optional_where_clause
= $StorageType<_, $some_generics, ...>;
I.e. it must be a type alias, with generics: T
or T: Config
. The aliased type must be
one of StorageValue
,
StorageMap
or
StorageDoubleMap
. The generic arguments of the
storage type can be given in two manners: named and unnamed. For named generic arguments,
the name for each argument should match the name defined for it on the storage struct:
StorageValue
expectsValue
and optionallyQueryKind
andOnEmpty
,StorageMap
expectsHasher
,Key
,Value
and optionallyQueryKind
andOnEmpty
,CountedStorageMap
expectsHasher
,Key
,Value
and optionallyQueryKind
andOnEmpty
,StorageDoubleMap
expectsHasher1
,Key1
,Hasher2
,Key2
,Value
and optionallyQueryKind
andOnEmpty
.
For unnamed generic arguments: Their first generic must be _
as it is replaced by the
macro and other generic must declared as a normal generic type declaration.
The Prefix
generic written by the macro is generated using
PalletInfo::name::<Pallet<..>>()
and the name of the storage type. E.g. if runtime names
the pallet “MyExample” then the storage type Foo<T> = ...
should use the prefix:
Twox128(b"MyExample") ++ Twox128(b"Foo")
.
For the CountedStorageMap
variant, the Prefix
also implements
CountedStorageMapInstance
.
It also associates a CounterPrefix
, which is
implemented the same as above, but the storage prefix is prepend with "CounterFor"
. E.g.
if runtime names the pallet “MyExample” then the storage type Foo<T> = CountedStorageaMap<...>
will store its counter at the prefix: Twox128(b"MyExample") ++ Twox128(b"CounterForFoo")
.
E.g:
#[pallet::storage]
pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
In this case the final prefix used by the map is Twox128(b"MyExample") ++ Twox128(b"OtherName")
.
Also see pallet::storage
#[pallet::getter(fn $my_getter_fn_name)]
(optional)
The optional attribute #[pallet::getter(fn $my_getter_fn_name)]
allows you to define a
getter function on Pallet
.
Also see pallet::getter
#[pallet::storage_prefix = "SomeName"]
(optional)
The optional attribute #[pallet::storage_prefix = "SomeName"]
allows you to define the
storage prefix to use, see how Prefix
generic is implemented above. This is helpful if
you wish to rename the storage field but don’t want to perform a migration.
E.g:
#[pallet::storage]
#[pallet::storage_prefix = "foo"]
#[pallet::getter(fn my_storage)]
pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
or
#[pallet::storage]
#[pallet::getter(fn my_storage)]
pub(super) type MyStorage<T> = StorageMap<_, Blake2_128Concat, u32, u32>;
Also see pallet::storage_prefix
#[pallet::unbounded]
(optional)
The optional attribute #[pallet::unbounded]
declares the storage as unbounded. When
implementating the storage info (when #[pallet::generate_storage_info]
is specified on
the pallet struct placeholder), the size of the storage will be declared as unbounded. This
can be useful for storage which can never go into PoV (Proof of Validity).
Also see pallet::unbounded
#[pallet::whitelist_storage]
(optional)
The optional attribute #[pallet::whitelist_storage]
will declare the storage as
whitelisted from benchmarking.
See
pallet::whitelist_storage
for more info.
#[cfg(..)]
(for storage)
The optional attributes #[cfg(..)]
allow conditional compilation for the storage.
E.g:
#[cfg(feature = "my-feature")]
#[pallet::storage]
pub(super) type MyStorage<T> = StorageValue<Value = u32>;
All the cfg
attributes are automatically copied to the items generated for the storage,
i.e. the getter, storage prefix, and the metadata element etc.
Any type placed as the QueryKind
parameter must implement
frame_support::storage::types::QueryKindTrait
. There are 3 implementations of this
trait by default:
OptionQuery
, the defaultQueryKind
used when this type parameter is omitted. Specifying this as theQueryKind
would cause storage map APIs that return aQueryKind
to instead return anOption
, returningSome
when a value does exist under a specified storage key, andNone
otherwise.ValueQuery
causes storage map APIs that return aQueryKind
to instead return the value type. In cases where a value does not exist under a specified storage key, theOnEmpty
type parameter onQueryKindTrait
is used to return an appropriate value.ResultQuery
causes storage map APIs that return aQueryKind
to instead return aResult<T, E>
, withT
being the value type andE
being the pallet error type specified by the#[pallet::error]
attribute. In cases where a value does not exist under a specified storage key, anErr
with the specified pallet error variant is returned.
NOTE: If the QueryKind
generic parameter is still generic at this stage or is using some
type alias then the generation of the getter might fail. In this case the getter can be
implemented manually.
NOTE: The generic Hasher
must implement the StorageHasher
trait (or the type is not
usable at all). We use StorageHasher::METADATA
for the metadata of the hasher of the
storage item. Thus generic hasher is supported.
Macro expansion
For each storage item the macro generates a struct named
_GeneratedPrefixForStorage$NameOfStorage
, and implements
StorageInstance
on it using the pallet and storage name. It
then uses it as the first generic of the aliased type. For
CountedStorageMap
,
CountedStorageMapInstance
is implemented, and another similar struct is generated.
For a named generic, the macro will reorder the generics, and remove the names.
The macro implements the function storage_metadata
on the Pallet
implementing the
metadata for all storage items based on their kind:
- for a storage value, the type of the value is copied into the metadata
- for a storage map, the type of the values and the key’s type is copied into the metadata
- for a storage double map, the type of the values, and the types of
key1
andkey2
are copied into the metadata.
Type value: #[pallet::type_value]
(optional)
The #[pallet::type_value]
attribute lets you define a struct implementing the
Get
trait to ease use of storage types. This attribute is meant to
be used alongside #[pallet::storage]
to define a
storage’s default value. This attribute can be used multiple times.
Item must be defined as:
#[pallet::type_value]
fn $MyDefaultName<$some_generic>() -> $default_type $optional_where_clause { $expr }
I.e.: a function definition with generics none or T: Config
and a returned type.
E.g.:
#[pallet::type_value]
fn MyDefault<T: Config>() -> T::Balance { 3.into() }
Also see pallet::type_value
Genesis config: #[pallet::genesis_config]
(optional)
The #[pallet::genesis_config]
attribute allows you to define the genesis configuration
for the pallet.
Item is defined as either an enum or a struct. It needs to be public and implement the
trait BuildGenesisConfig
with
#[pallet::genesis_build]
. The type
generics are constrained to be either none, or T
or T: Config
.
E.g:
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
_myfield: BalanceOf<T>,
}
Also see pallet::genesis_config
Genesis build: #[pallet::genesis_build]
(optional)
The #[pallet::genesis_build]
attribute allows you to define how genesis_configuration
is built. This takes as input the GenesisConfig
type (as self
) and constructs the
pallet’s initial state.
The impl must be defined as:
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<$maybe_generics> {
fn build(&self) { $expr }
}
I.e. a trait implementation with generic T: Config
, of trait GenesisBuild<T>
on
type GenesisConfig
with generics none or T
.
E.g.:
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {}
}
Also see pallet::genesis_build
Inherent: #[pallet::inherent]
(optional)
The #[pallet::inherent]
attribute allows the pallet to provide some
inherent.
An inherent is some piece of data that is inserted by a block authoring node at block
creation time and can either be accepted or rejected by validators based on whether the
data falls within an acceptable range.
The most common inherent is the timestamp
that is inserted into every block. Since there
is no way to validate timestamps, validators simply check that the timestamp reported by
the block authoring node falls within an acceptable range.
Item must be defined as:
#[pallet::inherent]
impl<T: Config> ProvideInherent for Pallet<T> {
// ... regular trait implementation
}
I.e. a trait implementation with bound T: Config
, of trait
ProvideInherent
for type Pallet<T>
, and some
optional where clause.
Also see pallet::inherent
Validate unsigned: #[pallet::validate_unsigned]
(optional)
The #[pallet::validate_unsigned]
attribute allows the pallet to validate some unsigned
transaction:
Item must be defined as:
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
// ... regular trait implementation
}
I.e. a trait implementation with bound T: Config
, of trait
ValidateUnsigned
for type Pallet<T>
, and some
optional where clause.
NOTE: There is also the sp_runtime::traits::SignedExtension
trait that can be used to
add some specific logic for transaction validation.
Also see pallet::validate_unsigned
Origin: #[pallet::origin]
(optional)
The #[pallet::origin]
attribute allows you to define some origin for the pallet.
Item must be either a type alias, an enum, or a struct. It needs to be public.
E.g.:
#[pallet::origin]
pub struct Origin<T>(PhantomData<(T)>);
WARNING: modifying origin changes the outer runtime origin. This outer runtime origin
can be stored on-chain (e.g. in pallet-scheduler
), thus any change must be done with care
as it might require some migration.
NOTE: for instantiable pallets, the origin must be generic over T
and I
.
Also see pallet::origin
Composite enum #[pallet::composite_enum]
(optional)
The #[pallet::composite_enum]
attribute allows you to define an enum on the pallet which
will then instruct construct_runtime
to amalgamate all similarly-named enums from other
pallets into an aggregate enum. This is similar in principle with how the aggregate enum is
generated for #[pallet::event]
or #[pallet::error]
.
The item tagged with #[pallet::composite_enum]
MUST be an enum declaration, and can ONLY
be the following identifiers: FreezeReason
, HoldReason
, LockId
or SlashReason
.
Custom identifiers are not supported.
NOTE: For ease of usage, when no #[derive]
attributes are detected, the
#[pallet::composite_enum]
attribute will automatically derive the following traits for
the enum:
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo,
RuntimeDebug
The inverse is also true: if there are any #derive attributes present for the enum, then the attribute will not automatically derive any of the traits described above.
General notes on instantiable pallets
An instantiable pallet is one where Config is generic, i.e. Config<I>
. This allows
runtime to implement multiple instances of the pallet, by using different types for the
generic. This is the sole purpose of the generic I
, but because
PalletInfo
requires the Pallet
placeholder to be static, it is
important to bound by 'static
whenever PalletInfo
can be used.
Additionally, in order to make an instantiable pallet usable as a regular pallet without an
instance, it is important to bound by = ()
on every type.
Thus impl bound looks like impl<T: Config<I>, I: 'static>
, and types look like
SomeType<T, I=()>
or SomeType<T: Config<I>, I: 'static = ()>
.
Example of a non-instantiable pallet
pub use pallet::*; // reexport in crate namespace for `construct_runtime!`
#[frame_support::pallet]
// NOTE: The name of the pallet is provided by `construct_runtime` and is used as
// the unique identifier for the pallet's storage. It is not defined in the pallet itself.
pub mod pallet {
use frame_support::pallet_prelude::*; // Import various types used in the pallet definition
use frame_system::pallet_prelude::*; // Import some system helper types.
type BalanceOf<T> = <T as Config>::Balance;
// Define the generic parameter of the pallet
// The macro parses `#[pallet::constant]` attributes and uses them to generate metadata
// for the pallet's constants.
#[pallet::config]
pub trait Config: frame_system::Config {
#[pallet::constant] // put the constant in metadata
type MyGetParam: Get<u32>;
type Balance: Parameter + MaxEncodedLen + From<u8>;
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
// Define some additional constant to put into the constant metadata.
#[pallet::extra_constants]
impl<T: Config> Pallet<T> {
/// Some description
fn exra_constant_name() -> u128 { 4u128 }
}
// Define the pallet struct placeholder, various pallet function are implemented on it.
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
// Implement the pallet hooks.
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
unimplemented!();
}
// can implement also: on_finalize, on_runtime_upgrade, offchain_worker, ...
// see `Hooks` trait
}
// Declare Call struct and implement dispatchables.
//
// WARNING: Each parameter used in functions must implement: Clone, Debug, Eq, PartialEq,
// Codec.
//
// The macro parses `#[pallet::compact]` attributes on function arguments and implements
// the `Call` encoding/decoding accordingly.
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Doc comment put in metadata
#[pallet::weight(0)] // Defines weight for call (function parameters are in scope)
pub fn toto(
origin: OriginFor<T>,
#[pallet::compact] _foo: u32,
) -> DispatchResultWithPostInfo {
let _ = origin;
unimplemented!();
}
}
// Declare the pallet `Error` enum (this is optional).
// The macro generates error metadata using the doc comment on each variant.
#[pallet::error]
pub enum Error<T> {
/// doc comment put into metadata
InsufficientProposersBalance,
}
// Declare pallet Event enum (this is optional).
//
// WARNING: Each type used in variants must implement: Clone, Debug, Eq, PartialEq, Codec.
//
// The macro generates event metadata, and derive Clone, Debug, Eq, PartialEq and Codec
#[pallet::event]
// Generate a funciton on Pallet to deposit an event.
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// doc comment put in metadata
// `<T as frame_system::Config>::AccountId` is not defined in metadata list, the last
// Thus the metadata is `<T as frame_system::Config>::AccountId`.
Proposed(<T as frame_system::Config>::AccountId),
/// doc
// here metadata will be `Balance` as define in metadata list
Spending(BalanceOf<T>),
// here metadata will be `Other` as define in metadata list
Something(u32),
}
// Define a struct which implements `frame_support::traits::Get<T::Balance>` (optional).
#[pallet::type_value]
pub(super) fn MyDefault<T: Config>() -> T::Balance { 3.into() }
// Declare a storage item. Any amount of storage items can be declared (optional).
//
// Is expected either `StorageValue`, `StorageMap` or `StorageDoubleMap`.
// The macro generates the prefix type and replaces the first generic `_`.
//
// The macro expands the metadata for the storage item with the type used:
// * for a storage value the type of the value is copied into the metadata
// * for a storage map the type of the values and the type of the key is copied into the metadata
// * for a storage double map the types of the values and keys are copied into the
// metadata.
//
// NOTE: The generic `Hasher` must implement the `StorageHasher` trait (or the type is not
// usable at all). We use [`StorageHasher::METADATA`] for the metadata of the hasher of the
// storage item. Thus generic hasher is supported.
#[pallet::storage]
pub(super) type MyStorageValue<T: Config> =
StorageValue<Value = T::Balance, QueryKind = ValueQuery, OnEmpty = MyDefault<T>>;
// Another storage declaration
#[pallet::storage]
#[pallet::getter(fn my_storage)]
#[pallet::storage_prefix = "SomeOtherName"]
pub(super) type MyStorage<T> =
StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
// Declare the genesis config (optional).
//
// The macro accepts either a struct or an enum; it checks that generics are consistent.
//
// Type must implement the `Default` trait.
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
_config: sp_std::marker::PhantomData<T>,
_myfield: u32,
}
// Declare genesis builder. (This is need only if GenesisConfig is declared)
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
// Declare a pallet origin (this is optional).
//
// The macro accept type alias or struct or enum, it checks generics are consistent.
#[pallet::origin]
pub struct Origin<T>(PhantomData<T>);
// Declare validate_unsigned implementation (this is optional).
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(
source: TransactionSource,
call: &Self::Call
) -> TransactionValidity {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
}
}
// Declare inherent provider for pallet (this is optional).
#[pallet::inherent]
impl<T: Config> ProvideInherent for Pallet<T> {
type Call = Call<T>;
type Error = InherentError;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
unimplemented!();
}
fn is_inherent(_call: &Self::Call) -> bool {
unimplemented!();
}
}
// Regular rust code needed for implementing ProvideInherent trait
#[derive(codec::Encode, sp_runtime::RuntimeDebug)]
#[cfg_attr(feature = "std", derive(codec::Decode))]
pub enum InherentError {
}
impl sp_inherents::IsFatalError for InherentError {
fn is_fatal_error(&self) -> bool {
unimplemented!();
}
}
pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall";
}
Example of an instantiable pallet
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
type BalanceOf<T, I = ()> = <T as Config<I>>::Balance;
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
#[pallet::constant]
type MyGetParam: Get<u32>;
type Balance: Parameter + MaxEncodedLen + From<u8>;
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
#[pallet::extra_constants]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Some description
fn extra_constant_name() -> u128 { 4u128 }
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
}
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Doc comment put in metadata
#[pallet::weight(0)]
pub fn toto(origin: OriginFor<T>, #[pallet::compact] _foo: u32) -> DispatchResultWithPostInfo {
let _ = origin;
unimplemented!();
}
}
#[pallet::error]
pub enum Error<T, I = ()> {
/// doc comment put into metadata
InsufficientProposersBalance,
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// doc comment put in metadata
Proposed(<T as frame_system::Config>::AccountId),
/// doc
Spending(BalanceOf<T, I>),
Something(u32),
}
#[pallet::type_value]
pub(super) fn MyDefault<T: Config<I>, I: 'static>() -> T::Balance { 3.into() }
#[pallet::storage]
pub(super) type MyStorageValue<T: Config<I>, I: 'static = ()> =
StorageValue<Value = T::Balance, QueryKind = ValueQuery, OnEmpty = MyDefault<T, I>>;
#[pallet::storage]
#[pallet::getter(fn my_storage)]
#[pallet::storage_prefix = "SomeOtherName"]
pub(super) type MyStorage<T, I = ()> =
StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
_config: sp_std::marker::PhantomData<(T,I)>,
_myfield: u32,
}
#[pallet::genesis_build]
impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
fn build(&self) {}
}
#[pallet::origin]
pub struct Origin<T, I = ()>(PhantomData<(T, I)>);
#[pallet::validate_unsigned]
impl<T: Config<I>, I: 'static> ValidateUnsigned for Pallet<T, I> {
type Call = Call<T, I>;
fn validate_unsigned(
source: TransactionSource,
call: &Self::Call
) -> TransactionValidity {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
}
}
#[pallet::inherent]
impl<T: Config<I>, I: 'static> ProvideInherent for Pallet<T, I> {
type Call = Call<T, I>;
type Error = InherentError;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
unimplemented!();
}
fn is_inherent(_call: &Self::Call) -> bool {
unimplemented!();
}
}
// Regular rust code needed for implementing ProvideInherent trait
#[derive(codec::Encode, sp_runtime::RuntimeDebug)]
#[cfg_attr(feature = "std", derive(codec::Decode))]
pub enum InherentError {
}
impl sp_inherents::IsFatalError for InherentError {
fn is_fatal_error(&self) -> bool {
unimplemented!();
}
}
pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall";
}
Upgrade guidelines
-
Export the metadata of the pallet for later checks
- run your node with the pallet active
- query the metadata using the
state_getMetadata
RPC and curl, or usesubsee -p <PALLET_NAME> > meta.json
-
Generate the template upgrade for the pallet provided by
decl_storage
with the environment variablePRINT_PALLET_UPGRADE
:PRINT_PALLET_UPGRADE=1 cargo check -p my_pallet
. This template can be used as it contains all information for storages, genesis config and genesis build. -
Reorganize the pallet to have the trait
Config
,decl_*
macros,ValidateUnsigned
,ProvideInherent
, and Origin` all together in one file. Suggested order:Config
,decl_module
,decl_event
,decl_error
,decl_storage
,origin
,validate_unsigned
,provide_inherent
, so far it should compile and all be correct.
-
start writing the new pallet module
ⓘpub use pallet::*; #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; #[pallet::pallet] #[pallet::generate_store($visibility_of_trait_store trait Store)] // NOTE: if the visibility of trait store is private but you want to make it available // in super, then use `pub(super)` or `pub(crate)` to make it available in crate. pub struct Pallet<T>(_); // pub struct Pallet<T, I = ()>(PhantomData<T>); // for instantiable pallet }
-
migrate Config: move trait into the module with
- all const in
decl_module
to#[pallet::constant]
- add the bound
IsType<<Self as frame_system::Config>::RuntimeEvent>
totype RuntimeEvent
- all const in
-
migrate decl_module: write:
ⓘ#[pallet::hooks] impl<T: Config> Hooks for Pallet<T> { }
and write inside
on_initialize
,on_finalize
,on_runtime_upgrade
,offchain_worker
, andintegrity_test
.then write:
ⓘ#[pallet::call] impl<T: Config> Pallet<T> { }
and write inside all the calls in
decl_module
with a few changes in the signature:- origin must now be written completely, e.g.
origin: OriginFor<T>
- result type must be
DispatchResultWithPostInfo
, you need to write it and also you might need to putOk(().into())
at the end or the function. #[compact]
must now be written#[pallet::compact]
#[weight = ..]
must now be written#[pallet::weight(..)]
- origin must now be written completely, e.g.
-
migrate event: rewrite as a simple enum with the attribute
#[pallet::event]
, use#[pallet::generate_deposit($vis fn deposit_event)]
to generatedeposit_event
, -
migrate error: rewrite it with attribute
#[pallet::error]
. -
migrate storage:
decl_storage
provide an upgrade template (see 3.). All storages, genesis config, genesis build and default implementation of genesis config can be taken from it directly.Otherwise here is the manual process:
first migrate the genesis logic. write:
ⓘ#[pallet::genesis_config] struct GenesisConfig { // fields of add_extra_genesis } impl Default for GenesisConfig { // type default or default provided for fields } #[pallet::genesis_build] impl<T: Config> GenesisBuild<T> for GenesisConfig { // for instantiable pallet: // `impl<T: Config, I: 'static> GenesisBuild<T, I> for GenesisConfig { fn build() { // The add_extra_genesis build logic } }
for each storage, if it contains
config(..)
then add fields, and make it default to the value in= ..;
or the type default if none, if it contains no build then also add the logic to build the value. for each storage if it containsbuild(..)
then add the logic togenesis_build
.NOTE: within
decl_storage
: the individual config is executed first, followed by the build and finally theadd_extra_genesis
build.Once this is done you can migrate storages individually, a few notes:
-
for private storage use
pub(crate) type
orpub(super) type
or nothing, -
for storages with
get(fn ..)
use#[pallet::getter(fn ...)]
-
for storages with value being
Option<$something>
make genericValue
being$something
and genericQueryKind
beingOptionQuery
(note: this is default). Otherwise makeValue
the complete value type andQueryKind
beingValueQuery
. -
for storages with default value:
= $expr;
provide some specificOnEmpty
generic. To do so use of#[pallet::type_value]
to generate the wanted struct to put. example:MyStorage: u32 = 3u32
would be written:ⓘ#[pallet::type_value] fn MyStorageOnEmpty() -> u32 { 3u32 } #[pallet::storage] pub(super) type MyStorage<T> = StorageValue<_, u32, ValueQuery, MyStorageOnEmpty>;
NOTE:
decl_storage
also generates the functionsassimilate_storage
andbuild_storage
directly onGenesisConfig
, and these are sometimes used in tests. In order not to break they can be implemented manually, one can implement those functions by calling theGenesisBuild
implementation.
-
-
migrate origin: move the origin to the pallet module to be under a
#[pallet::origin]
attribute -
migrate validate_unsigned: move the
ValidateUnsigned
implementation to the pallet module under a#[pallet::validate_unsigned]
attribute -
migrate provide_inherent: move the
ProvideInherent
implementation to the pallet module under a#[pallet::inherent]
attribute -
rename the usage of
Module
toPallet
inside the crate. -
migration is done, now double check the migration with the checking migration guidelines shown below.
Checking upgrade guidelines:
- compare metadata. Use subsee to fetch the metadata
and do a diff of the resulting json before and after migration. This checks for:
* call, names, signature, docs
- event names, docs
- error names, docs
- storage names, hasher, prefixes, default value
- error, error, constant
- manually check that:
Origin
was moved inside the macro under#[pallet::origin]
if it existsValidateUnsigned
was moved inside the macro under#[pallet::validate_unsigned)]
if it existsProvideInherent
was moved inside the macro under#[pallet::inherent)]
if it existson_initialize
/on_finalize
/on_runtime_upgrade
/offchain_worker
were moved to theHooks
implementation- storages with
config(..)
were converted toGenesisConfig
field, and their default is= $expr;
if the storage has a default value - storages with
build($expr)
orconfig(..)
were built inGenesisBuild::build
add_extra_genesis
fields were converted toGenesisConfig
field with their correct default if specifiedadd_extra_genesis
build was written intoGenesisBuild::build
- storage items defined with
pallet
use the name of the pallet provided bytraits::PalletInfo::name
aspallet_prefix
(indecl_storage
, storage items used thepallet_prefix
given as input ofdecl_storage
with the syntaxas Example
). Thus a runtime using the pallet must be careful with this change. To handle this change:- either ensure that the name of the pallet given to
construct_runtime!
is the same as the name the pallet was giving todecl_storage
, - or do a storage migration from the old prefix used to the new prefix used.
- either ensure that the name of the pallet given to
NOTE: The prefixes used by storage items are in metadata. Thus, ensuring the metadata
hasn’t changed ensures that the pallet_prefix
s used by the storage items haven’t changed.
Notes when macro fails to show proper error message spans:
Rustc loses span for some macro input. Some tips to fix it:
- do not use inner attribute:
ⓘ
#[pallet] pub mod pallet { //! This inner attribute will make span fail .. }
- use the newest nightly possible.
The pallet struct placeholder
#[pallet::pallet]
is mandatory and allows you to specify pallet information.
The struct must be defined as follows:
#[pallet::pallet]
pub struct Pallet<T>(_);
I.e. a regular struct definition named Pallet
, with generic T and no where clause.
Macro expansion:
The macro adds this attribute to the struct definition:
#[derive(
frame_support::CloneNoBound,
frame_support::EqNoBound,
frame_support::PartialEqNoBound,
frame_support::RuntimeDebugNoBound,
)]
and replaces the type _
with PhantomData<T>
. It also implements on the pallet:
GetStorageVersion
OnGenesis
: contains some logic to write the pallet version into storage.PalletErrorTypeInfo
: provides the type information for the pallet error, if defined.
It declares type Module
type alias for Pallet
, used by construct_runtime
.
It implements PalletInfoAccess
on Pallet
to ease access to pallet information given by
frame_support::traits::PalletInfo
. (The implementation uses the associated type
frame_system::Config::PalletInfo
).
It implements StorageInfoTrait
on Pallet
which give information about all storages.
If the attribute generate_store
is set then the macro creates the trait Store
and
implements it on Pallet
.
If the attribute set_storage_max_encoded_len
is set then the macro calls
StorageInfoTrait
for each storage in the implementation of StorageInfoTrait
for the
pallet. Otherwise it implements StorageInfoTrait
for the pallet using the
PartialStorageInfoTrait
implementation of storages.
Dev Mode (#[pallet(dev_mode)]
)
Specifying the argument dev_mode
will allow you to enable dev mode for a pallet. The aim
of dev mode is to loosen some of the restrictions and requirements placed on production
pallets for easy tinkering and development. Dev mode pallets should not be used in
production. Enabling dev mode has the following effects:
- Weights no longer need to be specified on every
#[pallet::call]
declaration. By default, dev mode pallets will assume a weight of zero (0
) if a weight is not specified. This is equivalent to specifying#[weight(0)]
on all calls that do not specify a weight. - Call indices no longer need to be specified on every
#[pallet::call]
declaration. By default, dev mode pallets will assume a call index based on the order of the call. - All storages are marked as unbounded, meaning you do not need to implement
MaxEncodedLen
on storage types. This is equivalent to specifying#[pallet::unbounded]
on all storage type definitions. - Storage hashers no longer need to be specified and can be replaced by
_
. In dev mode, these will be replaced byBlake2_128Concat
. In case of explicit key-binding,Hasher
can simply be ignored when indev_mode
.
Note that the dev_mode
argument can only be supplied to the #[pallet]
or
#[frame_support::pallet]
attribute macro that encloses your pallet module. This argument
cannot be specified anywhere else, including but not limited to the #[pallet::pallet]
attribute macro.
WARNING: You should not deploy or use dev mode pallets in production. Doing so can break your chain and therefore should never be done. Once you are done tinkering, you should remove the 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before attempting to use your pallet in a production scenario.
See frame_support::pallet
docs for more info.
Runtime Metadata Documentation
The documentation added to this pallet is included in the runtime metadata.
The documentation can be defined in the following ways:
#[pallet::pallet]
/// Documentation for pallet 1
#[doc = "Documentation for pallet 2"]
#[doc = include_str!("../README.md")]
#[pallet_doc("../doc1.md")]
#[pallet_doc("../doc2.md")]
pub mod pallet {}
The runtime metadata for this pallet contains the following
- “ Documentation for pallet 1“ (captured from
///
) - “Documentation for pallet 2” (captured from
#[doc]
) - content of ../README.md (captured from
#[doc]
withinclude_str!
) - content of “../doc1.md” (captured from
pallet_doc
) - content of “../doc2.md” (captured from
pallet_doc
)
doc
attribute
The value of the doc
attribute is included in the runtime metadata, as well as
expanded on the pallet module. The previous example is expanded to:
/// Documentation for pallet 1
/// Documentation for pallet 2
/// Content of README.md
pub mod pallet {}
If you want to specify the file from which the documentation is loaded, you can use the
include_str
macro. However, if you only want the documentation to be included in the
runtime metadata, use the pallet_doc
attribute.
pallet_doc
attribute
Unlike the doc
attribute, the documentation provided to the pallet_doc
attribute is
not inserted on the module.
The pallet_doc
attribute can only be provided with one argument,
which is the file path that holds the documentation to be added to the metadata.
This approach is beneficial when you use the include_str
macro at the beginning of the file
and want that documentation to extend to the runtime metadata, without reiterating the
documentation on the pallet module itself.