#
pezkuwi_sdk_docs/guides/your_first_pallet/
mod.rs

    1//! # Currency Pallet
    2//!
    3//! By the end of this guide, you will have written a small FRAME pallet (see
    4//! [`crate::pezkuwi_sdk::frame_runtime`]) that is capable of handling a simple crypto-currency.
    5//! This pallet will:
    6//!
    7//! 1. Allow anyone to mint new tokens into accounts (which is obviously not a great idea for a real
    8//!    system).
    9//! 2. Allow any user that owns tokens to transfer them to others.
    10//! 3. Track the total issuance of all tokens at all times.
    11//!
    12//! > This guide will build a currency pallet from scratch using only the lowest primitives of
    13//! > FRAME, and is mainly intended for education, not *applicability*. For example, almost all
    14//! > FRAME-based runtimes use various techniques to re-use a currency pallet instead of writing
    15//! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
    16//!
    17//! ## Writing Your First Pallet
    18//!
    19//! To get started, clone one of the templates mentioned in [`crate::pezkuwi_sdk::templates`]. We
    20//! recommend using the `pezkuwi-sdk-minimal-template`. You might need to change small parts of
    21//! this guide, namely the crate/package names, based on which template you use.
    22//!
    23//! > Be aware that you can read the entire source code backing this tutorial by clicking on the
    24//! > `source` button at the top right of the page.
    25//!
    26//! You should have studied the following modules as a prelude to this guide:
    27//!
    28//! - [`crate::reference_docs::blockchain_state_machines`]
    29//! - [`crate::reference_docs::trait_based_programming`]
    30//! - [`crate::pezkuwi_sdk::frame_runtime`]
    31//!
    32//! ## Topics Covered
    33//!
    34//! The following FRAME topics are covered in this guide:
    35//!
    36//! - [`pallet::storage`]
    37//! - [`pallet::call`]
    38//! - [`pallet::event`]
    39//! - [`pallet::error`]
    40//! - Basics of testing a pallet
    41//! - [Constructing a runtime](frame::runtime::prelude::construct_runtime)
    42//!
    43//! ### Shell Pallet
    44//!
    45//! Consider the following as a "shell pallet". We continue building the rest of this pallet based
    46//! on this template.
    47//!
    48//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any
    49//! pallet. Refer to the documentation of each to get an overview of what they do.
    50#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)]
    51//!
    52//! All of the code that follows in this guide should live inside of the `mod pallet`.
    53//!
    54//! ### Storage
    55//!
    56//! First, we will need to create two onchain storage declarations.
    57//!
    58//! One should be a mapping from account-ids to a balance type, and one value that is the total
    59//! issuance.
    60//!
    61//! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
    62//! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a
    63//! > generic bounded type in the `Config` trait, and then specify it in the implementation.
    64#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)]
    65//!
    66//! The definition of these two storage items, based on [`pallet::storage`] details, is as follows:
    67#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)]
    68#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)]
    69//!
    70//! ### Dispatchables
    71//!
    72//! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined
    73//! as normal `fn`s attached to `struct Pallet`.
    74#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)]
    75//!
    76//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related
    77//! details:
    78//!
    79//! - Where do `T::AccountId` and `T::RuntimeOrigin` come from? These are both defined in
    80//!  [`frame::prelude::frame_system::Config`], therefore we can access them in `T`.
    81//! - What is `ensure_signed`, and what does it do with the aforementioned `T::RuntimeOrigin`? This
    82//!   is outside the scope of this guide, and you can learn more about it in the origin reference
    83//!   document ([`crate::reference_docs::frame_origin`]). For now, you should only know the
    84//!   signature of the function: it takes a generic `T::RuntimeOrigin` and returns a
    85//!   `Result<T::AccountId, _>`. So by the end of this function call, we know that this dispatchable
    86//!   was signed by `sender`.
    87#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", ensure_signed)]
    88//!
    89//! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are
    90//! explained in the corresponding `type`, for example, for `Balances::<T>::insert`, you can look
    91//! into [`frame::prelude::StorageMap::insert`].
    92//!
    93//! - The return type of all dispatchable functions is [`frame::prelude::DispatchResult`]:
    94#![doc = docify::embed!("../../substrate/frame/support/src/dispatch.rs", DispatchResult)]
    95//!
    96//! Which is more or less a normal Rust `Result`, with a custom [`frame::prelude::DispatchError`] as
    97//! the `Err` variant. We won't cover this error in detail here, but importantly you should know
    98//! that there is an `impl From<&'static string> for DispatchError` provided (see
    99//! [here](`frame::prelude::DispatchError#impl-From<%26str>-for-DispatchError`)). Therefore,
    100//! we can use basic string literals as our error type and `.into()` them into `DispatchError`.
    101//!
    102//! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior
    103//!   of FRAME storage APIs. You can learn more about how to override this by looking into
    104//!   [`pallet::storage`], and [`frame::prelude::ValueQuery`]/[`frame::prelude::OptionQuery`]
    105//!
    106//! ### Improving Errors
    107//!
    108//! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be
    109//! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better.
    110//! This macro will call `.into()` under the hood.
    111#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)]
    112//!
    113//! Moreover, you will learn in the [Defensive Programming
    114//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
    115//! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not
    116//! only take a step in that direction, but also improve the error handing and make it slightly more
    117//! ergonomic.
    118#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)]
    119//!
    120//! This is more or less all the logic that there is in this basic currency pallet!
    121//!
    122//! ### Your First (Test) Runtime
    123//!
    124//! The typical testing code of a pallet lives in a module that imports some preludes useful for
    125//! testing, similar to:
    126//!
    127//! ```
    128//! pub mod pallet {
    129//! 	// snip -- actually pallet code.
    130//! }
    131//!
    132//! #[cfg(test)]
    133//! mod tests {
    134//! 	// bring in the testing prelude of frame
    135//! 	use frame::testing_prelude::*;
    136//! 	// bring in all pallet items
    137//! 	use super::pallet::*;
    138//!
    139//! 	// snip -- rest of the testing code.
    140//! }
    141//! ```
    142//!
    143//! Next, we create a "test runtime" in order to test our pallet. Recall from
    144//! [`crate::pezkuwi_sdk::frame_runtime`] that a runtime is a collection of pallets, expressed
    145//! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include
    146//! [`frame::prelude::frame_system`]. So we expect to see a runtime with two pallet, `frame_system`
    147//! and the one we just wrote.
    148#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)]
    149//!
    150//! > [`frame::pallet_macros::derive_impl`] is a FRAME feature that enables developers to have
    151//! > defaults for associated types.
    152//!
    153//! Recall that within our pallet, (almost) all blocks of code are generic over `<T: Config>`. And,
    154//! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or
    155//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
    156//! Rust traits and generics work. If unfamiliar with this pattern, read
    157//! [`crate::reference_docs::trait_based_programming`] before going further.
    158//!
    159//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
    160//! is to implement the `trait Config` of all pallets. That is, anywhere within your pallet code
    161//! where you see `<T: Config>` (read: *"some type `T` that implements `Config`"*), in the runtime,
    162//! it can be replaced with `<Runtime>`, because `Runtime` implements `Config` of all pallets, as we
    163//! see above.
    164//!
    165//! Another way to think about this is that within a pallet, a lot of types are "unknown" and, we
    166//! only know that they will be provided at some later point. For example, when you write
    167//! `T::AccountId` (which is short for `<T as frame_system::Config>::AccountId`) in your pallet,
    168//! you are in fact saying "*Some type `AccountId` that will be known later*". That "later" is in
    169//! fact when you specify these types when you implement all `Config` traits for `Runtime`.
    170//!
    171//! As you see above, `frame_system::Config` is setting the `AccountId` to `u64`. Of course, a real
    172//! runtime will not use this type, and instead reside to a proper type like a 32-byte standard
    173//! public key. This is a HUGE benefit that FRAME developers can tap into: through the framework
    174//! being so generic, different types can always be customized to simple things when needed.
    175//!
    176//! > Imagine how hard it would have been if all tests had to use a real 32-byte account id, as
    177//! > opposed to just a u64 number 🙈.
    178//!
    179//! ### Your First Test
    180//!
    181//! The above is all you need to execute the dispatchables of your pallet. The last thing you need
    182//! to learn is that all of your pallet testing code should be wrapped in
    183//! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state
    184//! to be used in our tests.
    185#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)]
    186//!
    187//! In the first test, we simply assert that there is no total issuance, and no balance associated
    188//! with Alice's account. Then, we mint some balance into Alice's, and re-check.
    189//!
    190//! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
    191//! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
    192//! dispatchables are simply functions that can be called on top of the `Pallet` struct.
    193//!
    194//! Congratulations! You have written your first pallet and tested it! Next, we learn a few optional
    195//! steps to improve our pallet.
    196//!
    197//! ## Improving the Currency Pallet
    198//!
    199//! ### Better Test Setup
    200//!
    201//! Idiomatic FRAME pallets often use Builder pattern to define their initial state.
    202//!
    203//! > The Pezkuwi Blockchain Academy's Rust entrance exam has a
    204//! > [section](https://github.com/pezkuwichain/kurdistan_blockchain-akademy/blob/main/src/m_builder.rs)
    205//! > on this that you can use to learn the Builder Pattern.
    206//!
    207//! Let's see how we can implement a better test setup using this pattern. First, we define a
    208//! `struct StateBuilder`.
    209#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)]
    210//!
    211//! This struct is meant to contain the same list of accounts and balances that we want to have at
    212//! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];`
    213//! so far. Then, if desired, we attach a default value for this struct.
    214#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)]
    215//!
    216//! Like any other builder pattern, we attach functions to the type to mutate its internal
    217//! properties.
    218#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)]
    219//!
    220//!  Finally --the useful part-- we write our own custom `build_and_execute` function on
    221//! this type. This function will do multiple things:
    222//!
    223//! 1. It would consume `self` to produce our `TestState` based on the properties that we attached
    224//!    to `self`.
    225//! 2. It would execute any test function that we pass in as closure.
    226//! 3. A nifty trick, this allows our test setup to have some code that is executed both before and
    227//!    after each test. For example, in this test, we do some additional checking about the
    228//!    correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the
    229//!    assertion should always hold, and how it is checked.
    230#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)]
    231//!
    232//! We can write tests that specifically check the initial state, and making sure our `StateBuilder`
    233//! is working exactly as intended.
    234#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)]
    235#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)]
    236//!
    237//! ### More Tests
    238//!
    239//! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and
    240//! mint would look like.
    241#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)]
    242#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)]
    243//!
    244//! It is always a good idea to build a mental model where you write *at least* one test for each
    245//! "success path" of a dispatchable, and one test for each "failure path", such as:
    246#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)]
    247//!
    248//! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
    249//!
    250//! ### Event and Error
    251//!
    252//! Our pallet is mainly missing two parts that are common in most FRAME pallets: Events, and
    253//! Errors. First, let's understand what each is.
    254//!
    255//! - **Error**: The static string-based error scheme we used so far is good for readability, but it
    256//!   has a few drawbacks. The biggest problem with strings are that they are not type safe, e.g. a
    257//!   match statement cannot be exhaustive. These string literals will bloat the final wasm blob,
    258//!   and are relatively heavy to transmit and encode/decode. Moreover, it is easy to mistype them
    259//!   by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
    260//!   the drawbacks mentioned. In short, we use an enum to represent different variants of our
    261//!   error. These variants are then mapped in an efficient way (using only `u8` indices) to
    262//!   [`sp_runtime::DispatchError::Module`]. Read more about this in [`pallet::error`].
    263//!
    264//! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
    265//!   emitted by the runtime to let outside world know what is happening inside the pallet. Since
    266//!   otherwise, the outside world does not have an easy access to the state changes. They should
    267//!   represent what happened at the end of a dispatch operation. Therefore, the convention is to
    268//!   use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
    269//!   external parties (eg. a light-node, a DApp) to listen to particular events happening, without
    270//!   needing to re-execute the whole state transition function.
    271//!
    272//! With the explanation out of the way, let's see how these components can be added. Both follow a
    273//! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`]
    274//! attributes attached.
    275#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)]
    276#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)]
    277//!
    278//! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into
    279//! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do
    280//! two things:
    281//!
    282//! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In
    283//! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent:
    284//! From<Event<Self>>`. Read: a `RuntimeEvent` exists, and it can be created from the local `enum
    285//! Event` of this pallet. This enables the pallet to convert its `Event` into `RuntimeEvent`, and
    286//! store it where needed.
    287//!
    288//! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME
    289//! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is
    290//! doing.
    291#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)]
    292//!
    293//! > These `Runtime*` types are better explained in
    294//! > [`crate::reference_docs::frame_runtime_types`].
    295//!
    296//! Then, we can rewrite the `transfer` dispatchable as such:
    297#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)]
    298//!
    299//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
    300//! setup.
    301#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)]
    302//!
    303//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
    304//! RuntimeEvent`) is generated by
    305//! [`construct_runtime`](frame::runtime::prelude::construct_runtime). An interesting way to inspect
    306//! this type is to see its definition in rust-docs:
    307//! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`].
    308//!
    309//!
    310//! ## What Next?
    311//!
    312//! The following topics where used in this guide, but not covered in depth. It is suggested to
    313//! study them subsequently:
    314//!
    315//! - [`crate::reference_docs::defensive_programming`].
    316//! - [`crate::reference_docs::frame_origin`].
    317//! - [`crate::reference_docs::frame_runtime_types`].
    318//! - The pallet we wrote in this guide was using `dev_mode`, learn more in [`pallet::config`].
    319//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
    320//!   [`frame::pallet_macros`].
    321//!
    322//! [`pallet::storage`]: frame_support::pallet_macros::storage
    323//! [`pallet::call`]: frame_support::pallet_macros::call
    324//! [`pallet::event`]: frame_support::pallet_macros::event
    325//! [`pallet::error`]: frame_support::pallet_macros::error
    326//! [`pallet::pallet`]: frame_support::pallet
    327//! [`pallet::config`]: frame_support::pallet_macros::config
    328//! [`pallet::generate_deposit`]: frame_support::pallet_macros::generate_deposit
    329
    330#[docify::export]
    331#[frame::pallet(dev_mode)]
    332pub mod shell_pallet {
    333	use frame::prelude::*;
    334
    335	#[pallet::config]
    336	pub trait Config: frame_system::Config {}
    337
    338	#[pallet::pallet]
    339	pub struct Pallet<T>(_);
    340}
    341
    342#[frame::pallet(dev_mode)]
    343pub mod pallet {
    344	use frame::prelude::*;
    345
    346	#[docify::export]
    347	pub type Balance = u128;
    348
    349	#[pallet::config]
    350	pub trait Config: frame_system::Config {}
    351
    352	#[pallet::pallet]
    353	pub struct Pallet<T>(_);
    354
    355	#[docify::export]
    356	/// Single storage item, of type `Balance`.
    357	#[pallet::storage]
    358	pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
    359
    360	#[docify::export]
    361	/// A mapping from `T::AccountId` to `Balance`
    362	#[pallet::storage]
    363	pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
    364
    365	#[docify::export(impl_pallet)]
    366	#[pallet::call]
    367	impl<T: Config> Pallet<T> {
    368		/// An unsafe mint that can be called by anyone. Not a great idea.
    369		pub fn mint_unsafe(
    370			origin: T::RuntimeOrigin,
    371			dest: T::AccountId,
    372			amount: Balance,
    373		) -> DispatchResult {
    374			// ensure that this is a signed account, but we don't really check `_anyone`.
    375			let _anyone = ensure_signed(origin)?;
    376
    377			// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
    378			Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
    379			// update total issuance.
    380			TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
    381
    382			Ok(())
    383		}
    384
    385		/// Transfer `amount` from `origin` to `dest`.
    386		pub fn transfer(
    387			origin: T::RuntimeOrigin,
    388			dest: T::AccountId,
    389			amount: Balance,
    390		) -> DispatchResult {
    391			let sender = ensure_signed(origin)?;
    392
    393			// ensure sender has enough balance, and if so, calculate what is left after `amount`.
    394			let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
    395			if sender_balance < amount {
    396				return Err("InsufficientBalance".into());
    397			}
    398			let remainder = sender_balance - amount;
    399
    400			// update sender and dest balances.
    401			Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
    402			Balances::<T>::insert(&sender, remainder);
    403
    404			Ok(())
    405		}
    406	}
    407
    408	#[allow(unused)]
    409	impl<T: Config> Pallet<T> {
    410		#[docify::export]
    411		pub fn transfer_better(
    412			origin: T::RuntimeOrigin,
    413			dest: T::AccountId,
    414			amount: Balance,
    415		) -> DispatchResult {
    416			let sender = ensure_signed(origin)?;
    417
    418			let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
    419			ensure!(sender_balance >= amount, "InsufficientBalance");
    420			let remainder = sender_balance - amount;
    421
    422			// .. snip
    423			Ok(())
    424		}
    425
    426		#[docify::export]
    427		/// Transfer `amount` from `origin` to `dest`.
    428		pub fn transfer_better_checked(
    429			origin: T::RuntimeOrigin,
    430			dest: T::AccountId,
    431			amount: Balance,
    432		) -> DispatchResult {
    433			let sender = ensure_signed(origin)?;
    434
    435			let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
    436			let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
    437
    438			// .. snip
    439			Ok(())
    440		}
    441	}
    442
    443	#[cfg(any(test, doc))]
    444	pub(crate) mod tests {
    445		use crate::guides::your_first_pallet::pallet::*;
    446
    447		#[docify::export(testing_prelude)]
    448		use frame::testing_prelude::*;
    449
    450		pub(crate) const ALICE: u64 = 1;
    451		pub(crate) const BOB: u64 = 2;
    452		pub(crate) const CHARLIE: u64 = 3;
    453
    454		#[docify::export]
    455		// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
    456		// tests { .. }`
    457		mod runtime {
    458			use super::*;
    459			// we need to reference our `mod pallet` as an identifier to pass to
    460			// `construct_runtime`.
    461			// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
    462			use crate::guides::your_first_pallet::pallet as pallet_currency;
    463
    464			construct_runtime!(
    465				pub enum Runtime {
    466					// ---^^^^^^ This is where `enum Runtime` is defined.
    467					System: frame_system,
    468					Currency: pallet_currency,
    469				}
    470			);
    471
    472			#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
    473			impl frame_system::Config for Runtime {
    474				type Block = MockBlock<Runtime>;
    475				// within pallet we just said `<T as frame_system::Config>::AccountId`, now we
    476				// finally specified it.
    477				type AccountId = u64;
    478			}
    479
    480			// our simple pallet has nothing to be configured.
    481			impl pallet_currency::Config for Runtime {}
    482		}
    483
    484		pub(crate) use runtime::*;
    485
    486		#[allow(unused)]
    487		#[docify::export]
    488		fn new_test_state_basic() -> TestState {
    489			let mut state = TestState::new_empty();
    490			let accounts = vec![(ALICE, 100), (BOB, 100)];
    491			state.execute_with(|| {
    492				for (who, amount) in &accounts {
    493					Balances::<Runtime>::insert(who, amount);
    494					TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
    495				}
    496			});
    497
    498			state
    499		}
    500
    501		#[docify::export]
    502		pub(crate) struct StateBuilder {
    503			balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
    504		}
    505
    506		#[docify::export(default_state_builder)]
    507		impl Default for StateBuilder {
    508			fn default() -> Self {
    509				Self { balances: vec![(ALICE, 100), (BOB, 100)] }
    510			}
    511		}
    512
    513		#[docify::export(impl_state_builder_add)]
    514		impl StateBuilder {
    515			fn add_balance(
    516				mut self,
    517				who: <Runtime as frame_system::Config>::AccountId,
    518				amount: Balance,
    519			) -> Self {
    520				self.balances.push((who, amount));
    521				self
    522			}
    523		}
    524
    525		#[docify::export(impl_state_builder_build)]
    526		impl StateBuilder {
    527			pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
    528				let mut ext = TestState::new_empty();
    529				ext.execute_with(|| {
    530					for (who, amount) in &self.balances {
    531						Balances::<Runtime>::insert(who, amount);
    532						TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
    533					}
    534				});
    535
    536				ext.execute_with(test);
    537
    538				// assertions that must always hold
    539				ext.execute_with(|| {
    540					assert_eq!(
    541						Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
    542						TotalIssuance::<Runtime>::get().unwrap_or_default()
    543					);
    544				})
    545			}
    546		}
    547
    548		#[docify::export]
    549		#[test]
    550		fn first_test() {
    551			TestState::new_empty().execute_with(|| {
    552				// We expect Alice's account to have no funds.
    553				assert_eq!(Balances::<Runtime>::get(&ALICE), None);
    554				assert_eq!(TotalIssuance::<Runtime>::get(), None);
    555
    556				// mint some funds into Alice's account.
    557				assert_ok!(Pallet::<Runtime>::mint_unsafe(
    558					RuntimeOrigin::signed(ALICE),
    559					ALICE,
    560					100
    561				));
    562
    563				// re-check the above
    564				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
    565				assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
    566			})
    567		}
    568
    569		#[docify::export]
    570		#[test]
    571		fn state_builder_works() {
    572			StateBuilder::default().build_and_execute(|| {
    573				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
    574				assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
    575				assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
    576				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
    577			});
    578		}
    579
    580		#[docify::export]
    581		#[test]
    582		fn state_builder_add_balance() {
    583			StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
    584				assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
    585				assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
    586			})
    587		}
    588
    589		#[test]
    590		#[should_panic]
    591		fn state_builder_duplicate_genesis_fails() {
    592			StateBuilder::default()
    593				.add_balance(CHARLIE, 42)
    594				.add_balance(CHARLIE, 43)
    595				.build_and_execute(|| {
    596					assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
    597					assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
    598				})
    599		}
    600
    601		#[docify::export]
    602		#[test]
    603		fn mint_works() {
    604			StateBuilder::default().build_and_execute(|| {
    605				// given the initial state, when:
    606				assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
    607
    608				// then:
    609				assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
    610				assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
    611
    612				// given:
    613				assert_ok!(Pallet::<Runtime>::mint_unsafe(
    614					RuntimeOrigin::signed(ALICE),
    615					CHARLIE,
    616					100
    617				));
    618
    619				// then:
    620				assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
    621				assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
    622			});
    623		}
    624
    625		#[docify::export]
    626		#[test]
    627		fn transfer_works() {
    628			StateBuilder::default().build_and_execute(|| {
    629				// given the initial state, when:
    630				assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
    631
    632				// then:
    633				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
    634				assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
    635				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
    636
    637				// when:
    638				assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
    639
    640				// then:
    641				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
    642				assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
    643				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
    644			});
    645		}
    646
    647		#[docify::export]
    648		#[test]
    649		fn transfer_from_non_existent_fails() {
    650			StateBuilder::default().build_and_execute(|| {
    651				// given the initial state, when:
    652				assert_err!(
    653					Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
    654					"NonExistentAccount"
    655				);
    656
    657				// then nothing has changed.
    658				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
    659				assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
    660				assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
    661				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
    662			});
    663		}
    664	}
    665}
    666
    667#[frame::pallet(dev_mode)]
    668pub mod pallet_v2 {
    669	use super::pallet::Balance;
    670	use frame::prelude::*;
    671
    672	#[docify::export(config_v2)]
    673	#[pallet::config]
    674	pub trait Config: frame_system::Config {
    675		/// The overarching event type of the runtime.
    676		#[allow(deprecated)]
    677		type RuntimeEvent: From<Event<Self>>
    678			+ IsType<<Self as frame_system::Config>::RuntimeEvent>
    679			+ TryInto<Event<Self>>;
    680	}
    681
    682	#[pallet::pallet]
    683	pub struct Pallet<T>(_);
    684
    685	#[pallet::storage]
    686	pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
    687
    688	#[pallet::storage]
    689	pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
    690
    691	#[docify::export]
    692	#[pallet::error]
    693	pub enum Error<T> {
    694		/// Account does not exist.
    695		NonExistentAccount,
    696		/// Account does not have enough balance.
    697		InsufficientBalance,
    698	}
    699
    700	#[docify::export]
    701	#[pallet::event]
    702	#[pallet::generate_deposit(pub(super) fn deposit_event)]
    703	pub enum Event<T: Config> {
    704		/// A transfer succeeded.
    705		Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
    706	}
    707
    708	#[pallet::call]
    709	impl<T: Config> Pallet<T> {
    710		#[docify::export(transfer_v2)]
    711		pub fn transfer(
    712			origin: T::RuntimeOrigin,
    713			dest: T::AccountId,
    714			amount: Balance,
    715		) -> DispatchResult {
    716			let sender = ensure_signed(origin)?;
    717
    718			// ensure sender has enough balance, and if so, calculate what is left after `amount`.
    719			let sender_balance =
    720				Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
    721			let remainder =
    722				sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
    723
    724			Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
    725			Balances::<T>::insert(&sender, remainder);
    726
    727			Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
    728
    729			Ok(())
    730		}
    731	}
    732
    733	#[cfg(any(test, doc))]
    734	pub mod tests {
    735		use super::{super::pallet::tests::StateBuilder, *};
    736		use frame::testing_prelude::*;
    737		const ALICE: u64 = 1;
    738		const BOB: u64 = 2;
    739
    740		#[docify::export]
    741		pub mod runtime_v2 {
    742			use super::*;
    743			use crate::guides::your_first_pallet::pallet_v2 as pallet_currency;
    744
    745			construct_runtime!(
    746				pub enum Runtime {
    747					System: frame_system,
    748					Currency: pallet_currency,
    749				}
    750			);
    751
    752			#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
    753			impl frame_system::Config for Runtime {
    754				type Block = MockBlock<Runtime>;
    755				type AccountId = u64;
    756			}
    757
    758			impl pallet_currency::Config for Runtime {
    759				type RuntimeEvent = RuntimeEvent;
    760			}
    761		}
    762
    763		pub(crate) use runtime_v2::*;
    764
    765		#[docify::export(transfer_works_v2)]
    766		#[test]
    767		fn transfer_works() {
    768			StateBuilder::default().build_and_execute(|| {
    769				// skip the genesis block, as events are not deposited there and we need them for
    770				// the final assertion.
    771				System::set_block_number(ALICE);
    772
    773				// given the initial state, when:
    774				assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
    775
    776				// then:
    777				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
    778				assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
    779				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
    780
    781				// now we can also check that an event has been deposited:
    782				assert_eq!(
    783					System::read_events_for_pallet::<Event<Runtime>>(),
    784					vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
    785				);
    786			});
    787		}
    788	}
    789}