|
|
|
@@ -47,7 +47,7 @@
|
|
|
|
|
//!
|
|
|
|
|
//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any
|
|
|
|
|
//! pallet. Refer to the documentation of each to get an overview of what they do.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", shell_pallet)]
|
|
|
|
|
//!
|
|
|
|
|
//! All of the code that follows in this guide should live inside of the `mod pallet`.
|
|
|
|
|
//!
|
|
|
|
@@ -61,17 +61,17 @@
|
|
|
|
|
//! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
|
|
|
|
|
//! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a
|
|
|
|
|
//! > generic bounded type in the `Config` trait, and then specify it in the implementation.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Balance)]
|
|
|
|
|
//!
|
|
|
|
|
//! The definition of these two storage items, based on [`pallet::storage`] details, is as follows:
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", TotalIssuance)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Balances)]
|
|
|
|
|
//!
|
|
|
|
|
//! ### Dispatchables
|
|
|
|
|
//!
|
|
|
|
|
//! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined
|
|
|
|
|
//! as normal `fn`s attached to `struct Pallet`.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", impl_pallet)]
|
|
|
|
|
//!
|
|
|
|
|
//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related
|
|
|
|
|
//! details:
|
|
|
|
@@ -108,14 +108,14 @@
|
|
|
|
|
//! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be
|
|
|
|
|
//! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better.
|
|
|
|
|
//! This macro will call `.into()` under the hood.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_better)]
|
|
|
|
|
//!
|
|
|
|
|
//! Moreover, you will learn in the [Defensive Programming
|
|
|
|
|
//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
|
|
|
|
|
//! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not
|
|
|
|
|
//! only take a step in that direction, but also improve the error handing and make it slightly more
|
|
|
|
|
//! ergonomic.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_better_checked)]
|
|
|
|
|
//!
|
|
|
|
|
//! This is more or less all the logic that there is in this basic currency pallet!
|
|
|
|
|
//!
|
|
|
|
@@ -145,7 +145,7 @@
|
|
|
|
|
//! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include
|
|
|
|
|
//! [`frame::prelude::pezframe_system`]. So we expect to see a runtime with two pallet, `pezframe_system`
|
|
|
|
|
//! and the one we just wrote.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", runtime)]
|
|
|
|
|
//!
|
|
|
|
|
//! > [`frame::pezpallet_macros::derive_impl`] is a FRAME feature that enables developers to have
|
|
|
|
|
//! > defaults for associated types.
|
|
|
|
@@ -182,7 +182,7 @@
|
|
|
|
|
//! to learn is that all of your pallet testing code should be wrapped in
|
|
|
|
|
//! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state
|
|
|
|
|
//! to be used in our tests.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", first_test)]
|
|
|
|
|
//!
|
|
|
|
|
//! In the first test, we simply assert that there is no total issuance, and no balance associated
|
|
|
|
|
//! with Alice's account. Then, we mint some balance into Alice's, and re-check.
|
|
|
|
@@ -206,16 +206,16 @@
|
|
|
|
|
//!
|
|
|
|
|
//! Let's see how we can implement a better test setup using this pattern. First, we define a
|
|
|
|
|
//! `struct StateBuilder`.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", StateBuilder)]
|
|
|
|
|
//!
|
|
|
|
|
//! This struct is meant to contain the same list of accounts and balances that we want to have at
|
|
|
|
|
//! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];`
|
|
|
|
|
//! so far. Then, if desired, we attach a default value for this struct.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", default_state_builder)]
|
|
|
|
|
//!
|
|
|
|
|
//! Like any other builder pattern, we attach functions to the type to mutate its internal
|
|
|
|
|
//! properties.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", impl_state_builder_add)]
|
|
|
|
|
//!
|
|
|
|
|
//! Finally --the useful part-- we write our own custom `build_and_execute` function on
|
|
|
|
|
//! this type. This function will do multiple things:
|
|
|
|
@@ -227,23 +227,23 @@
|
|
|
|
|
//! after each test. For example, in this test, we do some additional checking about the
|
|
|
|
|
//! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the
|
|
|
|
|
//! assertion should always hold, and how it is checked.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", impl_state_builder_build)]
|
|
|
|
|
//!
|
|
|
|
|
//! We can write tests that specifically check the initial state, and making sure our `StateBuilder`
|
|
|
|
|
//! is working exactly as intended.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", state_builder_works)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", state_builder_add_balance)]
|
|
|
|
|
//!
|
|
|
|
|
//! ### More Tests
|
|
|
|
|
//!
|
|
|
|
|
//! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and
|
|
|
|
|
//! mint would look like.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_works)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", mint_works)]
|
|
|
|
|
//!
|
|
|
|
|
//! It is always a good idea to build a mental model where you write *at least* one test for each
|
|
|
|
|
//! "success path" of a dispatchable, and one test for each "failure path", such as:
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_from_non_existent_fails)]
|
|
|
|
|
//!
|
|
|
|
|
//! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
|
|
|
|
|
//!
|
|
|
|
@@ -272,8 +272,8 @@
|
|
|
|
|
//! With the explanation out of the way, let's see how these components can be added. Both follow a
|
|
|
|
|
//! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`]
|
|
|
|
|
//! attributes attached.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Event)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Error)]
|
|
|
|
|
//!
|
|
|
|
|
//! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into
|
|
|
|
|
//! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do
|
|
|
|
@@ -288,17 +288,17 @@
|
|
|
|
|
//! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME
|
|
|
|
|
//! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is
|
|
|
|
|
//! doing.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", config_v2)]
|
|
|
|
|
//!
|
|
|
|
|
//! > These `Runtime*` types are better explained in
|
|
|
|
|
//! > [`crate::reference_docs::frame_runtime_types`].
|
|
|
|
|
//!
|
|
|
|
|
//! Then, we can rewrite the `transfer` dispatchable as such:
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_v2)]
|
|
|
|
|
//!
|
|
|
|
|
//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
|
|
|
|
|
//! setup.
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)]
|
|
|
|
|
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", runtime_v2)]
|
|
|
|
|
//!
|
|
|
|
|
//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
|
|
|
|
|
//! RuntimeEvent`) is generated by
|
|
|
|
|