Add the Nicks Pallet to your Runtime
As you saw in the Create your first Substrate blockchain tutorial, the Substrate node template provides a working runtime that you can use to start building your own blockchain. The node template includes a runtime that is constructed using FRAME development framework and a few modules—called pallets—to get you started. In this tutorial, you'll learn how to add the Nicks pallet to the node template. You can follow similar patterns to add other FRAME pallets to your runtime. However, you need to configure each pallet with the specific settings it requires to use it. This tutorial illustrates how to configure the settings each pallet requires but not the specific settings that apply to each pallet you decide to add to your runtime.
Before you begin
Before we even get started, let's layout our objectives:
Add the Nicks pallet to your node template
Implement the Nicks pallet configuration trait.
Use the front-end template to interact with the Nicks pallet.
Learning outcomes
- Learn how to integrate a FRAME pallet to your node and runtime
- Familiarize yourself with customizing a pallet's configuration trait
- Learn how to publish a custom pallet to crates.io
Add the Nicks pallet to the node template
The Nicks pallet allows blockchain users to pay a deposit to reserve a nickname and associate it with an account they control.
To add the Nicks pallet to the Substrate node template:
Open a terminal shell and change to the root directory for the node template.
Open the
runtime/Cargo.toml
configuration file in a text editor.Import the
pallet-nicks
crate to make it available to the node template runtime by adding it to the list of dependencies.
[dependencies.pallet-nicks]
default-features = false
git = 'https://github.com/paritytech/substrate.git'
tag = 'monthly-2021-10'
version = '4.0.0-dev'
In this dependencies.pallet-nicks
section, default-features = false
specifies that the default
features to be understand are the features defined for the std
native Rust binary listed in the features
section.
It is important to note that the Substrate runtime compiles to both a native Rust
std
binary and a WebAssembly (Wasm)
binary. For more information about compiling std
and
no_std
features, see XXX.
The remaining lines specify that Cargo should use the pallet-nicks
crate from the
paritytech/substrate
repository with the commit tag identified using the monthly-YYYY-MM
naming
convention.
For an introduction to editing Cargo.toml
files, see the
Cargo reference documentation.
- Add the
pallet-nicks
crate to the list offeatures
to be included in thestd
feature set by adding the following linefeatures
section.
[features]
default = ['std']
std = [
...
'pallet-aura/std',
'pallet-balances/std',
'pallet-nicks/std',
...
]
If you forget to update the features
section, you might see errors similar to the following when you
build the native binaries:
error[E0425]: cannot find function `memory_teardown` in module `sandbox`
--> ~/.cargo/git/checkouts/substrate-7e08433d4c370a21/83a6f1a/primitives/sandbox/src/../without_std.rs:53:12
|
53 | sandbox::memory_teardown(self.memory_idx);
| ^^^^^^^^^^^^^^^ not found in `sandbox`
error[E0425]: cannot find function `memory_new` in module `sandbox`
--> ~/.cargo/git/checkouts/substrate-7e08433d4c370a21/83a6f1a/primitives/sandbox/src/../without_std.rs:72:18
|
72 | match sandbox::memory_new(initial, maximum) {
|
...
- Check that the new dependencies resolve correctly by running the following command:
cargo check -p node-template-runtime
Review the configuration trait for the Nicks pallet
Every pallet has a Rust trait called Config
that is used to configure the parameters and types that the pallet needs from the runtime.
If you review the comments for the runtime in the
node template, you will see examples
of using the Config
configuration trait to define and tune parameters for each pallet.
Most of the pallet-specific code required to add a pallet is implemented using the Config
trait.
To see what you need to implement for the Nicks pallet specifically, see the pallet_nicks::Config
documentation or the
definition of the trait itself in the source code
of the Nicks pallet.
In the source code for the nicks
pallet, the Config
trait declares the following types:
pub trait Config: frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// The currency trait.
type Currency: ReservableCurrency<Self::AccountId>;
/// Reservation fee.
#[pallet::constant]
type ReservationFee: Get<BalanceOf<Self>>;
/// What to do with slashed funds.
type Slashed: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// The origin which may forcibly set or remove a name. Root can always do this.
type ForceOrigin: EnsureOrigin<Self::Origin>;
/// The minimum length a name may be.
#[pallet::constant]
type MinLength: Get<u32>;
/// The maximum length a name may be.
#[pallet::constant]
type MaxLength: Get<u32>;
}
Review the implementation of the Balances pallet
Just like you used the Balances pallet as a template for importing the Nicks pallet, you can use the
Balances pallet as an example to see how to implement the Config
interface for the Nicks pallet.
Open the
runtime/src/lib.rs
file in a text editor.Locate the Balances pallet section and note that the implementation consists of two parts:
The
parameter_types!
block where constant values are defined.parameter_types! { // The u128 constant value 500 is aliased to a type named ExistentialDeposit. pub const ExistentialDeposit: u128 = 500; // A heuristic that is used for weight estimation. pub const MaxLocks: u32 = 50; }
The
impl
block where the types and values defined by theConfig
interface are configured.impl pallet_balances::Config for Runtime { // The previously defined parameter_type is used as a configuration parameter. type MaxLocks = MaxLocks; // The "Balance" that appears after the equal sign is an alias for the u128 type. type Balance = Balance; // The empty value, (), is used to specify a no-op callback function. type DustRemoval = (); // The previously defined parameter_type is used as a configuration parameter. type ExistentialDeposit = ExistentialDeposit; // The FRAME runtime system is used to track the accounts that hold balances. type AccountStore = System; // Weight information is supplied to the Balances pallet by the node template runtime. // type WeightInfo = (); // old way type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>; // The ubiquitous event type. type Event = Event; }
The impl pallet_balances::Config
block allows your to configure the types and parameters that are
specified by the Balances pallet Config
trait.
For example, this impl
block configures the Balances pallet to use the u128
type to track balances.
If you were developing a chain where it was important to optimize storage, you could use any unsigned
integer type that was at least 32-bits in size because the
Balance
type for
the Balances pallet Config
trait is bound by the
AtLeast32BitUnsigned
trait.
Implement the Nicks pallet Config
trait
Now that you have seen an example of how the Config
trait is implemented for the Balances pallet,
you're ready to implement the Config
trait for the Nicks pallet.
To implement the nicks pallet in your runtime:
Open the
runtime/src/lib.rs
file in a text editor.After the last line of the Balances code block, add the following code block for the Nicks pallet:
/// Add this code block to your template for Nicks:
parameter_types! {
// Choose a fee that incentivizes desireable behavior.
pub const NickReservationFee: u128 = 100;
pub const MinNickLength: u32 = 8;
// Maximum bounds on storage are important to secure your chain.
pub const MaxNickLength: u32 = 32;
}
impl pallet_nicks::Config for Runtime {
// The Balances pallet implements the ReservableCurrency trait.
// `Balances` is defined in `construct_runtime!` macro. See below.
// https://docs.substrate.io/rustdocs/latest/pallet_balances/index.html#implementations-2
type Currency = Balances;
// Use the NickReservationFee from the parameter_types block.
type ReservationFee = NickReservationFee;
// No action is taken when deposits are forfeited.
type Slashed = ();
// Configure the FRAME System Root origin as the Nick pallet admin.
// https://docs.substrate.io/rustdocs/latest/frame_system/enum.RawOrigin.html#variant.Root
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
// Use the MinNickLength from the parameter_types block.
type MinLength = MinNickLength;
// Use the MaxNickLength from the parameter_types block.
type MaxLength = MaxNickLength;
// The ubiquitous event type.
type Event = Event;
}
Identify the types that the Nicks pallet exposes.
You can find a complete list of types in the
construct_runtime!
macro documentation.The Nicks pallet uses the following types:
- Pallet Storage: Because it uses the
#[pallet::storage]
macro. - Pallet Events: Because it uses the
#[pallet::events]
macro. You will notice that in the case of the Nicks pallet, theEvent
keyword is parameterized with respect to a type,T
; this is because at least one of the events defined by the Nicks pallet depends on a type that is configured with theConfig
configuration trait. - Callable Functions: Because it has dispatchable functions in the
#[pallet::call]
macro. - The Pallet type from the
#[pallet::pallet]
macro.
Add Nicks to the
construct_runtime!
macro.For example:
construct_runtime!( pub enum Runtime where Block = Block, NodeBlock = opaque::Block, UncheckedExtrinsic = UncheckedExtrinsic { /* --snip-- */ Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>}, /*** Add This Line ***/ Nicks: pallet_nicks::{Pallet, Call, Storage, Event<T>}, } );
Check that the new dependencies resolve correctly by running the following command:
cargo check -p node-template-runtime
If there are no errors, you are ready to compile.
Compile the node in release mode by running the following command:
cargo build --release
Start the blockchain node and use the Nicks pallet
After your node compiles, you are ready to start the node that has been enhanced with nickname capabilities from the Nicks pallet and interact with it using the front-end template.
Start the local Substrate node
To start the local Substrate node:
Open a terminal shell, if necessary.
Change to the root directory of the Substrate node template.
Start the node in development mode by running the following command:
./target/release/node-template --dev --tmp
The node-template command-line options specify how you want the running node to operate. In this case, the options specify the following:
The
--dev
option specifies that the node run as a developer node chain specification.The
--tmp
option specifies that the node save all active data—such as keys, blockchain database, and networking information—while it is running, then delete the data when you stop the node by pressing Control-c. You should use--tmp
option to ensure you have a clean working state any time you stop and restart the node.
Verify your node is up and running successfully by reviewing the output displayed in the terminal.
If the number after
finalized
is increasing in the console output, your blockchain is producing new blocks and reaching consensus about the state they describe.Keep the terminal that displays the node output open to continue.
Start the front-end template
Now that you have added a new pallet to your runtime, you can use the Substrate front-end template to interact with the node template and access the Nicks pallet.
To start the front-end template:
Open a new terminal shell on your computer.
In the new terminal, change to the root directory where you installed the front-end template.
Start the web server for the front-end template by running the following command:
yarn start
Open http://localhost:8000/ in a browser to view the front-end template.
Set a nickname using the Nicks pallet
After you start the front-end template, you can use it to interact with the Nicks pallet you just added to the runtime.
To set a nickname for an account:
Check the account selection list to verify that the Alice account is currently selected.
In the Pallet Interactor component, verify that Extrinsic is selected.
Select
nicks
from the list of pallets available to call.Select the
setName
dispatchable as the function to call from thenicks
pallet.Type a name that is longer than the
MinNickLength
(8 characters)and no longer than theMaxNickLength
(32 characters).Click
Signed
to execute the function.Observe the status of the call and the events emitted by the Nicks pallet.
Query information for an account using the Nicks pallet
Next, you can use Query capability to read the value of Alice's nickname from the runtime storage for the Nicks pallet.
To return the information stored for Alice:
In the Pallet Interactor component, select Query.
Select
nicks
from the list of pallets available to query.Select the
nameOf
.Copy and paste the address for the alice account in the Account field, then click Query.
The return type is a tuple that contains two values:
The hex-encoded nickname for the Alice account.
The amount that was reserved from Alice's account to secure the nickname.
If you were to query the Nicks pallet for the nameOf for Bob's account, you would see the
None
value returned because Bob has not invoked thesetName
dispatchable and deposited the funds to reserve a nickname.
Explore more advanced topics
This tutorial
Use the Signed
button to invoke
the killName
dispatchable
function and use Bob's account ID as the function's argument. The killName
function must be called
by the ForceOrigin
that was configured with the Nicks pallet's Config
interface in the previous
section. You may recall that we configured this to be the FRAME system's Root
origin. The Node
Template's
chain specification
file is used to configure the
Sudo pallet to give Alice access to
this origin.
The front-end template makes it easy to use the Sudo pallet to dispatch a call from the
Root
origin - just use the SUDO
button to invoke the dispatchable. Since we just used the
Signed
button as opposed to the SUDO
button, the function was dispatched by
the Signed
origin
associated with Alice's account as opposed to the Root
origin.
You will notice that even though the function call was successfully dispatched, a BadOrigin
error
was emitted and is visible in the Events pane. This means that Alice's account was still charged
fees for the dispatch, but there weren't any state changes
executed because the Nicks pallet follows the important
verify-first-write-last pattern. Now
use the SUDO
button to dispatch the same call with the same parameter.
The Sudo pallet emits a
Sudid
event
to inform network participants that the Root
origin dispatched a call, however, you will notice
that the inner dispatch failed with a
DispatchError
(the
Sudo pallet's
sudo
function is
the "outer" dispatch). In particular, this was an instance of
the DispatchError::Module
variant,
which reports two pieces of metadata: an index
number and an error
number. The index
number
relates to the pallet from which the error originated; it corresponds with the index (position) of
the pallet within the construct_runtime!
macro. The error
number corresponds with the index of
the relevant variant from that pallet's Error
enum. When using these numbers to find pallet
errors, remember that the first position corresponds with index zero. In the screenshot above,
the index
is 9
(the tenth pallet) and the error
is 2
(the third error). Depending on the
position of the Nicks pallet in your construct_runtime!
macro, you may see a different number for
index
. Regardless of the value of index
, you should see that the error
value is 2
, which
corresponds to the third variant of the Nick's pallet's Error
enum,
the Unnamed
variant.
This shouldn't be a surprise since Bob has not yet reserved a nickname, thus it cannot be cleared!
You should confirm that Alice can use the SUDO
button to invoke the killName
dispatchable and
forcibly clear the nickname associated with any account (including her own) that actually has a
nickname associated with it. Here are some other things you may want to try:
- Add a nickname that is shorter than the
MinNickLength
or longer than theMaxNickLength
that you configured with the Nick's pallet'sConfig
configuration trait. - Add a nickname for Bob then use Alice's account and the
SUDO
button to forcibly kill Bob's nickname. Switch back to Bob's account and dispatch theclearName
function.
Adding Other FRAME Pallets
In this guide, we walked through specifically how to import the Nicks pallet, but as mentioned in the beginning of this guide, each pallet will be a little different. Have no fear, you can always refer to the Substrate runtime which includes nearly every pallets in the library of core FRAME pallets.
In the Substrate node runtime bin/node/runtime/Cargo.toml
file, you will see how to import
different pallets, and in the bin/node/runtime/src/lib.rs
file you will see how to add each pallet to your
runtime. You can generally copy what was done there as a starting point to include a pallet in your
own runtime.
Learn how to add a more complex pallet to the Node Template by going through the Add the Contracts Pallet guide or complete the Forkless Upgrade a Chain tutorial to learn how Substrate enables forkless runtime upgrades and follow steps to perform two distinct types of upgrades.
Publish Your Own Pallet
By now you should have successfully imported the Nicks pallet. In future, you will write your own pallets to execute your application-specific logics. In those cases, you may want to share these pallets with others.
In this section, we will cover how to publish your own pallet for open source usage.
Option 1: Publishing on GitHub
To publish your pallet on GitHub, you need to create a GitHub repository and push your pallet's code to it.
Once published, other developers could refer to your pallet in their Cargo.toml
using the
following snippet:
runtime/Cargo.toml
[dependencies.your-pallet-name]
default_features = false
git = 'https://github.com/your-username/your-pallet'
version = '1.0.0'
branch = 'master'
# You may choose to refer to a specific commit or tag instead of branch
# rev = '<git-commit>'
# tag = '<some tag>
Option 2: Publishing on crates.io
crates.io allows permissionless publishing of any Rust module. You could learn the procedure by following their guide on how to publish on crates.io.
Once published, other developers could refer to your pallet in their Cargo.toml
using the
following snippet:
runtime/Cargo.toml
[dependencies.your-pallet-name]
default_features = false
version = 'some-compatible-version'
We do not specify any target destination on the above, and by default it will search for the package in crates.io repository.
Next steps
- We have plenty of tutorials to showcase Substrate development concepts and techniques.
- For more information about runtime development tips and patterns, refer to our How-to Guides.
- For a bare FRAME pallet with detailed comments about what you can access within FRAME, see
this example in
substrate
.