Developers Home»tutorials»Add the Nicks Pallet to your Runtime

Add the Nicks Pallet to your Runtime

Introduction

The Substrate Node Template provides a minimal working runtime which you can use to quickly get started building your own custom blockchain. The Node Template includes a number of components, including a runtime that is constructed using FRAME runtime development framework. However, in order to remain minimal, it does not include most of the modules (called "pallets") from Substrate's set of core FRAME pallets.

This guide will show you how you can add the Nicks pallet. You can follow a similar pattern to add additional FRAME pallets to your runtime. However you should note that each pallet is a little different in terms of the specific configuration settings needed to use it correctly. This tutorial will help you understand what you'll need to consider when adding a new pallet to your FRAME runtime.

We're here to help

If you run into any issues during this tutorial, we are here to help! You can ask a question on Stack Overflow and use the substrate tag or contact us on Element.

You should already have the latest version of the Node Template compiled on your computer if you completed the Create Your First Substrate Chain tutorial. If you do not, please complete it first.

Information

If you skipped the first blockchain tutorial, you should install the node template now. according to the instructions in its readme.

What You Will Be Doing

Before we get started, let's layout our objectives:

1. Add the Nicks pallet to your Node Template

2. Implement the Nicks pallet configuration trait.

3. Use the Front-end Template to interact with the Nicks pallet.

4. Learn how to publish a custom pallet to Github or crates.io.

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.

Import the Nicks Pallet

We'll start by modifying the Substrate Node Template to include the Nicks pallet. This pallet allows blockchain users to pay a deposit to reserve a nickname and associate it with an account they control.

Open the Node Template in your favorite code editor. We will be editing two files: runtime/src/lib.rs, and runtime/Cargo.toml.

substrate-node-template/
|
+-- runtime/
|   |
|   +-- Cargo.toml    <-- One change in this file
|   |
|   +-- build.rs
|   |
|   +-- src/
|       |
|       +-- lib.rs     <-- Most changes in this file
|       |
|       +-- ...
|
+-- pallets/
|
+-- scripts/
|
+-- node/
|
+-- ...

Importing a Pallet Crate

The first thing you need to do is to import the pallet-nicks crate in your runtime's Cargo.toml file. If you want a proper primer into Cargo syntax, please check out its official documentation.

Open substrate-node-template/runtime/Cargo.toml and you will see all runtime dependencies. For example, it depends on the Balances pallet:

runtime/Cargo.toml

#--snip--
[dependencies.pallet-balances]
default-features = false
git = 'https://github.com/paritytech/substrate.git'
tag = 'monthly-2021-08'
version = '4.0.0-dev'

This is telling Cargo to find the crate from git repository paritytech/substrate with commit that is tagged with monthly-YYYY-MM. YYYY and MM are the corresponding year and month.

Crate Features

Note that when importing pallet crates we need to make sure crate features is setup correctly. In the code snippet above, you will notice that we set default-features = false. If you look further down in the Cargo.toml, you will find something like:

runtime/Cargo.toml

[features]
default = ['std']
std = [
    'codec/std',
    'serde',
    'frame-executive/std',
    'frame-support/std',
    'frame-system/std',
    'frame-system-rpc-runtime-api/std',
    'pallet-aura/std',
    'pallet-balances/std',
    # --snip--
]

This second line defines the default features of your runtime crate as std. You can imagine, each pallet crate has a similar configuration defining the default feature for the crate. Your feature will determine the features that should be used on downstream dependencies. For example, the snippet above should be read as:

The default feature for this Substrate runtime is std. When std feature is enabled for the runtime, codec, frame-executive, frame-support, and all the other listed dependencies should use their std feature.

This is important to enable the Substrate runtime to compile to both native binary, which supports Rust std, and Wasm binary, which do not (read about no_std).

The use of Wasm runtime binaries is one of Substrate's defining features. It allows the runtime code to become a part of a blockchain's evolving state; it also means that the definition of the runtime itself is subject to the cryptographic consensus mechanisms that ensure the security of the blockchain network. The usage of Wasm runtimes enables one of Substrate's most innovative features: forkless runtime upgrades, which means that Substrate blockchain nodes can stay up-to-date and even acquire new features without needing to be replaced with an updated application binary.

To see how the std and no_std features are actually used in the runtime code, we can open the project file:

runtime/src/lib.rs

#![cfg_attr(not(feature = "std"), no_std)]
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
#![recursion_limit="256"]

// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

// --snip--

You can see that at the top of the file, we define that we will use no_std when we are not using the std feature. A few lines lower you can see #[cfg(feature = "std")] above the wasm_binary.rs import, which is a flag saying to only import the Wasm binary when we have enabled the std feature.

Importing the Nicks Pallet Crate

Okay, now that we have covered the basics of crate features, we can actually import the Nicks pallet. The Nicks pallet is one of the simpler pallets in FRAME, so it makes for a good example of the common points you need to consider when adding a pallet to your runtime.

First we will add the new dependency by simply copying an existing pallet and changing the values. So based on the balances import shown above, the nicks import will look like:

runtime/Cargo.toml

[dependencies.pallet-nicks]
default-features = false
git = 'https://github.com/paritytech/substrate.git'
tag = 'monthly-YYYY-MM'
version = '4.0.0-dev'

As with other pallets, the Nicks pallet has an std feature. We should build its std feature when the runtime is built with its own std feature. Add the following line to the runtime's std feature.

runtime/Cargo.toml

[features]
default = ["std"]
std = [
    #--snip--
    'pallet-nicks/std',
    #--snip--
]

If you forget to set the feature, when building to your native binaries you will get errors like:

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) {
   |

...

Before moving on, check that the new dependencies resolve correctly by running:

cargo check -p node-template-runtime

Next we will implement the Nicks pallet configuration trait and add the Nicks pallet to your runtime.

Configure the Nicks Pallet

Every pallet has a component called Config that is used for configuration. This component is a Rust "trait"; traits in Rust are similar to interfaces in languages such as C++, Java and Go. FRAME developers must have their runtime implement this trait for each pallet they would like to include and configure that pallet with the parameters and types that it needs from the outer runtime. For instance, in the template pallet that is included in the Node Template, you will see the following Config configuration trait:

pallets/template/src/lib.rs

/// Configure the pallet by specifying the parameters and types on which it depends.
pub trait Config: frame_system::Config {
    /// Because this pallet emits events, it depends on the runtime's definition of an event.
    type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}

As the comment states, we have an Event associated type of the Config configuration trait in order to allow the template pallet to emit events that is compatible with the outer runtime. The Config configuration trait may also be used to tune parameters that control the resources required to interact with a pallet or even to limit the resources of the runtime that the pallet may consume. You will see examples of both such cases below when you implement the Config configuration trait for the Nicks pallet.

To figure out what you need to implement for the Nicks pallet specifically, you can take a look at the pallet_nicks::Config documentation or the definition of the trait itself in the source code of the Nicks pallet. We have annotated the source code for nicks pallet from the substrate source below with enhanced comments that expand on those already included in the documentation, so be sure to read this:

substrate/frame/nicks/src/lib.rs

/// Already in the Nicks pallet included substrate (with enhanced comments):
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>;
}

Implement the Nicks pallet Config trait

Just like we used the Balances pallet as a template for importing the Nicks pallet, let's use the Balances pallet as an example to help us understand how we can implement the Config interface for the Nicks pallet. You will notice that this implementation consists of two parts: a parameter_types! block where constant values are defined and an impl block where the types and values defined by the Config interface are configured. These code blocks have also been annotated with additional comments that you should be sure to read:

runtime/src/lib.rs

/// Already in your template for Balances:
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;
}

impl pallet_balances::Config for Runtime {
    // The previously defined parameter_type is used as a configuration parameter.
    type MaxLocks = MaxLocks;

    type MaxReserves = ();

    type ReserveIdentifier = [u8; 8];

    // 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's 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 runtime developers that are including the Balances pallet in their runtime to configure the types and parameters that are defined in the Balances pallet Config configuration trait. For example, the impl block above configures the Balances pallet to use the Balance type, a type alias of u128, to track balances. If you're developing a chain where it's important to optimize storage, you could use any unsigned integer type that was at least 32-bits in size; this is because the Balance type for the Balances pallet Config configuration trait is bound by the AtLeast32BitUnsigned trait.

Now that you have an idea of the purpose behind the Config configuration trait and how you can implement a FRAME pallet's Config interface for your runtime, let's implement the Config configuration trait for the Nicks pallet. Add the following code to your runtime:

runtime/src/lib.rs

/// 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_runtimes!` macro. See below.
    // /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.
    // /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;
}

Adding Nicks to the construct_runtime! Macro

Next, we need to add the Nicks pallet to the construct_runtime! macro. For this, we need to determine the types that the pallet exposes so that we can tell the runtime that they exist. The complete list of possible types can be found in the construct_runtime! macro documentation.

If we look at the Nicks pallet in detail, we know it has:

  • 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, the Event 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 the Config configuration trait.
  • Callable Functions: Because it has dispatchable functions in the #[pallet::call] macro.
  • The Pallet type from the #[pallet::pallet] macro.

Thus, when we add the pallet, it will look like this:

runtime/src/lib.rs

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>},
    }
);
Note

Not all pallets will expose all of these runtime types, and some may expose more! You should always look at the documentation or source code of a pallet to determine which of these types you need to expose.

Interact with the Nicks Pallet

Now you are ready to compile and run your node that has been enhanced with nickname capabilities from the Nicks pallet.

Compile and run the node

Compile the node in release mode with:

cargo build --release

If the build fails, go back to the previous section and make sure you followed all the steps correctly. You can also view the full solution of including the Nick pallet.

After the build succeeds, you can start the node:

# Run a temporary node in development mode
./target/release/node-template --dev --tmp

Start the Front-End

As in the previous tutorials, this tutorial will use the Substrate Front-End Template to allow you to interact with the Node Template. As long as you have completed the Create Your First Chain and Proof of Existence tutorials, you should be prepared to continue with the rest of this tutorial.

Information

Refer directly to the front-end setup instructions in the Create Your First Chain Tutorial if necessary.

To start the Front-End Template, navigate to its directory and run:

yarn start

Use the Nicks Pallet

You should already be familiar with using the Front-End Template to interact with a pallet. In this section we will use the Nicks pallet to further illustrate how the Front-End Template can be used to interact with FRAME pallets. We will also learn more about how to use the Front-End Template to invoke privileged functions with the Sudo pallet, which is included by default as part of the Node Template. Finally, you will learn how to interpret the different types of events and errors that FRAME pallets may emit.

To get started, use the account selector from the Front-End Template to select Alice's account and then use the Pallet Interactor component to call the setName dispatchable function from the nicks pallet. You can select any name you'd like as long as it is no shorter than the MinNickLength and no longer than the MaxNickLength you configured in the previous step. Use the Signed button to execute the function.

Set a Name

As you can see in the image above, the Front-End Template will report the status of the dispatchable, as well as allow you to observe the events emitted by the Nicks pallet and the other pallets that compose your chain's runtime. Now use the Pallet Interactor's Query capabilities to read the value of Alice's nickname from the runtime storage of the Nicks pallet.

Read a Name

The return type is a tuple that contains two values: Alice's hex-encoded nickname and the amount that was reserved from Alice's account in order to secure the nickname. If you query the Nicks pallet for Bob's nickname, you'll see that the None value is returned. This is because Bob has not invoked the setName dispatchable and deposited the funds needed to reserve a nickname.

Read an Empty Name

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.

`BadOrigin` Error

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.

Nicks Pallet Error

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 the MaxNickLength that you configured with the Nick's pallet's Config 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 the clearName 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 More

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 Rust modules. 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 higly detailed doc comments on specifics of what more you can access within FRAME, see this example in substrate.

References

Last edit: on

Was This Tutorial Helpful?
Help us improve