diff --git a/Cargo.lock b/Cargo.lock
index df96a2140abe..200ad4c6177c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11270,6 +11270,7 @@ dependencies = [
"sp-core",
"sp-io",
"sp-runtime",
+ "sp-std 14.0.0",
]
[[package]]
diff --git a/docs/sdk/src/polkadot_sdk/frame_runtime.rs b/docs/sdk/src/polkadot_sdk/frame_runtime.rs
index f9b8a381365c..39255c8f51ad 100644
--- a/docs/sdk/src/polkadot_sdk/frame_runtime.rs
+++ b/docs/sdk/src/polkadot_sdk/frame_runtime.rs
@@ -87,8 +87,6 @@
//! * writing a runtime in pure Rust, as done in [this template](https://github.com/JoshOrndorff/frameless-node-template).
//! * writing a runtime in AssemblyScript,as explored in [this project](https://github.com/LimeChain/subsembly).
-use frame::prelude::*;
-
/// A FRAME based pallet. This `mod` is the entry point for everything else. All
/// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an
/// experimental feature to break these parts into different `mod`s. See [`pallet_examples`] for
@@ -96,7 +94,7 @@ use frame::prelude::*;
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod pallet {
- use super::*;
+ use frame::prelude::*;
/// The configuration trait of a pallet. Mandatory. Allows a pallet to receive types at a
/// later point from the runtime that wishes to contain it. It allows the pallet to be
diff --git a/docs/sdk/src/reference_docs/frame_storage_derives.rs b/docs/sdk/src/reference_docs/frame_storage_derives.rs
new file mode 100644
index 000000000000..3d9edef398a0
--- /dev/null
+++ b/docs/sdk/src/reference_docs/frame_storage_derives.rs
@@ -0,0 +1,199 @@
+//!
+//! In all examples, a few lines of boilerplate have been hidden from each snippet for conciseness.
+//!
+//!
+//! Let's begin by starting to store a `NewType` in a storage item:
+//!
+//! ```compile_fail
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use frame::prelude::*;
+//! # #[pallet::config]
+//! # pub trait Config: frame_system::Config {}
+//! # #[pallet::pallet]
+//! # pub struct Pallet(_);
+//! pub struct NewType(u32);
+//
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, NewType>;
+//! }
+//! ```
+//!
+//! This raises a number of compiler errors, like:
+//! ```text
+//! the trait `MaxEncodedLen` is not implemented for `NewType`, which is required by
+//! `frame::prelude::StorageValue<_GeneratedPrefixForStorageSomething, NewType>:
+//! StorageInfoTrait`
+//! ```
+//!
+//! This implies the following set of traits that need to be derived for a type to be stored in
+//! `frame` storage:
+//! ```rust
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use frame::prelude::*;
+//! # #[pallet::config]
+//! # pub trait Config: frame_system::Config {}
+//! # #[pallet::pallet]
+//! # pub struct Pallet(_);
+//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
+//! pub struct NewType(u32);
+//!
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, NewType>;
+//! }
+//! ```
+//!
+//! Next, let's look at how this will differ if we are to store a type that is derived from `T` in
+//! storage, such as [`frame::prelude::BlockNumberFor`]:
+//! ```compile_fail
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use frame::prelude::*;
+//! # #[pallet::config]
+//! # pub trait Config: frame_system::Config {}
+//! # #[pallet::pallet]
+//! # pub struct Pallet(_);
+//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
+//! pub struct NewType(BlockNumberFor);
+//!
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, NewType>;
+//! }
+//! ```
+//!
+//! Surprisingly, this will also raise a number of errors, like:
+//! ```text
+//! the trait `TypeInfo` is not implemented for `T`, which is required
+//! by`frame_support::pallet_prelude::StorageValue,
+//! pallet_2::NewType>:StorageEntryMetadataBuilder
+//! ```
+//!
+//! Why is that? The underlying reason is that the `TypeInfo` `derive` macro will only work for
+//! `NewType` if all of `NewType`'s generics also implement `TypeInfo`. This is not the case for `T`
+//! in the example above.
+//!
+//! If you expand an instance of the derive, you will find something along the lines of:
+//! `impl TypeInfo for NewType where T: TypeInfo { ... }`. This is the reason why the
+//! `TypeInfo` trait is required for `T`.
+//!
+//! To fix this, we need to add a `#[scale_info(skip_type_params(T))]`
+//! attribute to `NewType`. This additional macro will instruct the `derive` to skip the bound on
+//! `T`.
+//! ```rust
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use frame::prelude::*;
+//! # #[pallet::config]
+//! # pub trait Config: frame_system::Config {}
+//! # #[pallet::pallet]
+//! # pub struct Pallet(_);
+//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
+//! #[scale_info(skip_type_params(T))]
+//! pub struct NewType(BlockNumberFor);
+//!
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, NewType>;
+//! }
+//! ```
+//!
+//! Next, let's say we wish to store `NewType` as [`frame::prelude::ValueQuery`], which means it
+//! must also implement `Default`. This should be as simple as adding `derive(Default)` to it,
+//! right?
+//! ```compile_fail
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use frame::prelude::*;
+//! # #[pallet::config]
+//! # pub trait Config: frame_system::Config {}
+//! # #[pallet::pallet]
+//! # pub struct Pallet(_);
+//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo, Default)]
+//! #[scale_info(skip_type_params(T))]
+//! pub struct NewType(BlockNumberFor);
+//!
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, NewType, ValueQuery>;
+//! }
+//! ```
+//!
+//! Under the hood, the expansion of the `derive(Default)` will suffer from the same restriction as
+//! before: it will only work if `T: Default`, and `T` is not `Default`. Note that this is an
+//! expected issue: `T` is merely a wrapper of many other types, such as `BlockNumberFor`.
+//! `BlockNumberFor` should indeed implement `Default`, but `T` implementing `Default` is rather
+//! meaningless.
+//!
+//! To fix this, frame provides a set of macros that are analogous to normal rust derive macros, but
+//! work nicely on top of structs that are generic over `T: Config`. These macros are:
+//!
+//! - [`frame::prelude::DefaultNoBound`]
+//! - [`frame::prelude::DebugNoBound`]
+//! - [`frame::prelude::PartialEqNoBound`]
+//! - [`frame::prelude::EqNoBound`]
+//! - [`frame::prelude::CloneNoBound`]
+//! - [`frame::prelude::PartialOrdNoBound`]
+//! - [`frame::prelude::OrdNoBound`]
+//!
+//! The above traits are almost certainly needed for your tests: To print your type, assert equality
+//! or clone it.
+//!
+//! We can fix the following example by using [`frame::prelude::DefaultNoBound`].
+//! ```rust
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use frame::prelude::*;
+//! # #[pallet::config]
+//! # pub trait Config: frame_system::Config {}
+//! # #[pallet::pallet]
+//! # pub struct Pallet(_);
+//! #[derive(
+//! codec::Encode,
+//! codec::Decode,
+//! codec::MaxEncodedLen,
+//! scale_info::TypeInfo,
+//! DefaultNoBound
+//! )]
+//! #[scale_info(skip_type_params(T))]
+//! pub struct NewType(BlockNumberFor);
+//!
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, NewType, ValueQuery>;
+//! }
+//! ```
+//!
+//! Finally, if a custom type that is provided through `Config` is to be stored in the storage, it
+//! is subject to the same trait requirements. The following does not work:
+//! ```compile_fail
+//! #[frame::pallet]
+//! pub mod pallet {
+//! use frame::prelude::*;
+//! #[pallet::config]
+//! pub trait Config: frame_system::Config {
+//! type CustomType;
+//! }
+//! #[pallet::pallet]
+//! pub struct Pallet(_);
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, T::CustomType>;
+//! }
+//! ```
+//!
+//! But adding the right trait bounds will fix it.
+//! ```rust
+//! #[frame::pallet]
+//! pub mod pallet {
+//! use frame::prelude::*;
+//! #[pallet::config]
+//! pub trait Config: frame_system::Config {
+//! type CustomType: codec::FullCodec
+//! + codec::MaxEncodedLen
+//! + scale_info::TypeInfo
+//! + Debug
+//! + Default;
+//! }
+//! #[pallet::pallet]
+//! pub struct Pallet(_);
+//! #[pallet::storage]
+//! pub type Something = StorageValue<_, T::CustomType>;
+//! }
+//! ```
diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs
index 688339b7e380..c69c79365427 100644
--- a/docs/sdk/src/reference_docs/mod.rs
+++ b/docs/sdk/src/reference_docs/mod.rs
@@ -45,6 +45,10 @@ pub mod signed_extensions;
/// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built.
pub mod frame_origin;
+/// Learn about the details of what derives are needed for a type to be store-able in `frame`
+/// storage.
+pub mod frame_storage_derives;
+
/// Learn about how to write safe and defensive code in your FRAME runtime.
pub mod defensive_programming;
diff --git a/substrate/frame/bags-list/src/list/tests.rs b/substrate/frame/bags-list/src/list/tests.rs
index cd39b0831726..e5fff76d75c7 100644
--- a/substrate/frame/bags-list/src/list/tests.rs
+++ b/substrate/frame/bags-list/src/list/tests.rs
@@ -777,7 +777,8 @@ mod bags {
assert_eq!(bag_1000.tail, Some(4));
assert_eq!(bag_1000.iter().count(), 3);
bag_1000.insert_node_unchecked(node(4, None, None, bag_1000.bag_upper)); // panics in debug
- assert_eq!(bag_1000.iter().count(), 3); // in release we expect it to silently ignore the request.
+ assert_eq!(bag_1000.iter().count(), 3); // in release we expect it to silently ignore the
+ // request.
});
}
diff --git a/substrate/frame/nis/src/lib.rs b/substrate/frame/nis/src/lib.rs
index f38755836fb9..d815ea6ac115 100644
--- a/substrate/frame/nis/src/lib.rs
+++ b/substrate/frame/nis/src/lib.rs
@@ -755,7 +755,13 @@ pub mod pallet {
// We ignore this error as it just means the amount we're trying to deposit is
// dust and the beneficiary account doesn't exist.
.or_else(
- |e| if e == TokenError::CannotCreate.into() { Ok(()) } else { Err(e) },
+ |e| {
+ if e == TokenError::CannotCreate.into() {
+ Ok(())
+ } else {
+ Err(e)
+ }
+ },
)?;
summary.receipts_on_hold.saturating_reduce(on_hold);
}
diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs
index 2aaea0446366..2b5fe8b60412 100644
--- a/substrate/frame/nomination-pools/src/lib.rs
+++ b/substrate/frame/nomination-pools/src/lib.rs
@@ -494,7 +494,6 @@ impl ClaimPermission {
frame_support::PartialEqNoBound,
)]
#[cfg_attr(feature = "std", derive(DefaultNoBound))]
-#[codec(mel_bound(T: Config))]
#[scale_info(skip_type_params(T))]
pub struct PoolMember {
/// The identifier of the pool to which `who` belongs.
diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs
index e41f7f1c0ef3..e09d8fc4fa1d 100644
--- a/substrate/frame/src/lib.rs
+++ b/substrate/frame/src/lib.rs
@@ -30,13 +30,43 @@
//! > **F**ramework for **R**untime **A**ggregation of **M**odularized **E**ntities: Substrate's
//! > State Transition Function (Runtime) Framework.
//!
-//! ## Documentation
+//! //! ## Usage
//!
-//! See [`polkadot_sdk::frame`](../polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
+//! The main intended use of this crate is for it to be imported with its preludes:
//!
-//! ## WARNING: Experimental
+//! ```
+//! # use polkadot_sdk_frame as frame;
+//! #[frame::pallet]
+//! pub mod pallet {
+//! # use polkadot_sdk_frame as frame;
+//! use frame::prelude::*;
+//! // ^^ using the prelude!
//!
-//! **This crate and all of its content is experimental, and should not yet be used in production.**
+//! #[pallet::config]
+//! pub trait Config: frame_system::Config {}
+//!
+//! #[pallet::pallet]
+//! pub struct Pallet(_);
+//! }
+//!
+//! pub mod tests {
+//! # use polkadot_sdk_frame as frame;
+//! use frame::testing_prelude::*;
+//! }
+//!
+//! pub mod runtime {
+//! # use polkadot_sdk_frame as frame;
+//! use frame::runtime::prelude::*;
+//! }
+//! ```
+//!
+//! See: [`prelude`], [`testing_prelude`] and [`runtime::prelude`].
+//!
+//! Please note that this crate can only be imported as `polkadot-sdk-frame` or `frame`.
+//!
+//! ## Documentation
+//!
+//! See [`polkadot_sdk::frame`](../polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
//!
//! ## Underlying dependencies
//!
@@ -46,9 +76,9 @@
//! In short, this crate only re-exports types and traits from multiple sources. All of these
//! sources are listed (and re-exported again) in [`deps`].
//!
-//! ## Usage
+//! ## WARNING: Experimental
//!
-//! Please note that this crate can only be imported as `polkadot-sdk-frame` or `frame`.
+//! **This crate and all of its content is experimental, and should not yet be used in production.**
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg(feature = "experimental")]
diff --git a/substrate/frame/state-trie-migration/src/lib.rs b/substrate/frame/state-trie-migration/src/lib.rs
index 4ec649f9080d..22ad640d3bd2 100644
--- a/substrate/frame/state-trie-migration/src/lib.rs
+++ b/substrate/frame/state-trie-migration/src/lib.rs
@@ -109,7 +109,6 @@ pub mod pallet {
MaxEncodedLen,
)]
#[scale_info(skip_type_params(MaxKeyLen))]
- #[codec(mel_bound())]
pub enum Progress> {
/// Yet to begin.
ToStart,
@@ -126,7 +125,6 @@ pub mod pallet {
///
/// It tracks the last top and child keys read.
#[derive(Clone, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq, MaxEncodedLen)]
- #[codec(mel_bound(T: Config))]
#[scale_info(skip_type_params(T))]
pub struct MigrationTask {
/// The current top trie migration progress.
diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs
index 138091689a59..94f4b9dd4bdc 100644
--- a/substrate/frame/support/src/lib.rs
+++ b/substrate/frame/support/src/lib.rs
@@ -2297,6 +2297,18 @@ pub mod pallet_macros {
/// }
/// ```
///
+ /// ### Value Trait Bounds
+ ///
+ /// To use a type as the value of a storage type, be it `StorageValue`, `StorageMap` or
+ /// anything else, you need to meet a number of trait bound constraints.
+ ///
+ /// See: .
+ ///
+ /// Notably, all value types need to implement `Encode`, `Decode`, `MaxEncodedLen` and
+ /// `TypeInfo`, and possibly `Default`, if
+ /// [`ValueQuery`](frame_support::storage::types::ValueQuery) is used, explained in the
+ /// next section.
+ ///
/// ### QueryKind
///
/// Every storage type mentioned above has a generic type called
diff --git a/templates/minimal/README.md b/templates/minimal/README.md
index eaa21a05ee89..b556a4536089 100644
--- a/templates/minimal/README.md
+++ b/templates/minimal/README.md
@@ -37,21 +37,6 @@ A Polkadot SDK based project such as this one consists of:
* ๐ ๏ธ Depending on your operating system and Rust version, there might be additional
packages required to compile this template - please take note of the Rust compiler output.
-## Customization
-
-In case you would like to change the pallet's name from `pallet-minimal-template` to `pallet-test`,
-you would need to implement the following changes:
-* Change the pallet folder name from `template` to `test` for consistency
-* Also for consistency, in `/runtime/src/lib.rs`, change from `pub type Template` to `pub type Test`,
-and don't forget to also customize the corresponding comments.
-* Change `pallet-minimal-template` to `pallet-test` in the following files:
- * `/runtime/Cargo.toml`
- * `/runtime/src/lib.rs`
- * `/pallets/test/Cargo.toml`
- * `/src/lib.rs`
-* Change `pallet_minimal_template` to `pallet_test` in the following files:
- * `/runtime/src/lib.rs`
-
### Build
๐จ Use the following command to build the node without launching it:
@@ -80,8 +65,8 @@ docker run --rm polkadot-sdk-minimal-template --dev
Development chains:
* ๐งน Do not persist the state.
-* ๐ฐ Are preconfigured with a genesis state that includes several pre-funded development accounts.
-* ๐งโโ๏ธ Development accounts are used as `sudo` accounts.
+* ๐ฐ Are pre-configured with a genesis state that includes several pre-funded development accounts.
+* ๐งโโ๏ธ One development account (`ALICE`) is used as `sudo` accounts.
### Connect with the Polkadot-JS Apps Front-End
diff --git a/templates/minimal/pallets/template/src/lib.rs b/templates/minimal/pallets/template/src/lib.rs
index 713f014bbe61..92b90ad4412b 100644
--- a/templates/minimal/pallets/template/src/lib.rs
+++ b/templates/minimal/pallets/template/src/lib.rs
@@ -1,4 +1,7 @@
//! A shell pallet built with [`frame`].
+//!
+//! To get started with this pallet, try implementing the guide in
+//!
#![cfg_attr(not(feature = "std"), no_std)]
diff --git a/templates/parachain/pallets/template/Cargo.toml b/templates/parachain/pallets/template/Cargo.toml
index 250895843e4f..3b651ce427f9 100644
--- a/templates/parachain/pallets/template/Cargo.toml
+++ b/templates/parachain/pallets/template/Cargo.toml
@@ -25,10 +25,13 @@ frame-benchmarking = { optional = true, workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
+# primitive deps
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+
[dev-dependencies]
sp-core = { workspace = true, default-features = true }
sp-io = { workspace = true, default-features = true }
-sp-runtime = { workspace = true, default-features = true }
[features]
default = ["std"]
@@ -40,13 +43,14 @@ runtime-benchmarks = [
]
std = [
"codec/std",
+ "scale-info/std",
+
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
- "scale-info/std",
- "sp-core/std",
- "sp-io/std",
+
"sp-runtime/std",
+ "sp-std/std",
]
try-runtime = [
"frame-support/try-runtime",
diff --git a/templates/parachain/pallets/template/src/benchmarking.rs b/templates/parachain/pallets/template/src/benchmarking.rs
index d1a9554aed6d..5acad6e60dec 100644
--- a/templates/parachain/pallets/template/src/benchmarking.rs
+++ b/templates/parachain/pallets/template/src/benchmarking.rs
@@ -1,34 +1,33 @@
//! Benchmarking setup for pallet-template
#![cfg(feature = "runtime-benchmarks")]
-use super::*;
-#[allow(unused)]
-use crate::Pallet as Template;
+use super::*;
use frame_benchmarking::v2::*;
-use frame_system::RawOrigin;
#[benchmarks]
mod benchmarks {
use super::*;
+ #[cfg(test)]
+ use crate::pallet::Pallet as Template;
+ use frame_system::RawOrigin;
#[benchmark]
fn do_something() {
- let value = 100u32;
let caller: T::AccountId = whitelisted_caller();
#[extrinsic_call]
- do_something(RawOrigin::Signed(caller), value);
+ do_something(RawOrigin::Signed(caller), 100);
- assert_eq!(Something::::get(), Some(value));
+ assert_eq!(Something::::get().map(|v| v.block_number), Some(100u32.into()));
}
#[benchmark]
fn cause_error() {
- Something::::put(100u32);
+ Something::::put(CompositeStruct { block_number: 100u32.into() });
let caller: T::AccountId = whitelisted_caller();
#[extrinsic_call]
cause_error(RawOrigin::Signed(caller));
- assert_eq!(Something::::get(), Some(101u32));
+ assert_eq!(Something::::get().map(|v| v.block_number), Some(101u32.into()));
}
impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test);
diff --git a/templates/parachain/pallets/template/src/lib.rs b/templates/parachain/pallets/template/src/lib.rs
index 0420e2b90821..6bfb98972aed 100644
--- a/templates/parachain/pallets/template/src/lib.rs
+++ b/templates/parachain/pallets/template/src/lib.rs
@@ -1,3 +1,50 @@
+//! # Template Pallet
+//!
+//! A pallet with minimal functionality to help developers understand the essential components of
+//! writing a FRAME pallet. It is typically used in beginner tutorials or in Polkadot SDK template
+//! as a starting point for creating a new pallet and **not meant to be used in production**.
+//!
+//! ## Overview
+//!
+//! This template pallet contains basic examples of:
+//! - declaring a storage item that stores a single block-number
+//! - declaring and using events
+//! - declaring and using errors
+//! - a dispatchable function that allows a user to set a new value to storage and emits an event
+//! upon success
+//! - another dispatchable function that causes a custom error to be thrown
+//!
+//! Each pallet section is annotated with an attribute using the `#[pallet::...]` procedural macro.
+//! This macro generates the necessary code for a pallet to be aggregated into a FRAME runtime.
+//!
+//! To get started with pallet development, consider using this tutorial:
+//!
+//!
+//!
+//! And reading the main documentation of the `frame` crate:
+//!
+//!
+//!
+//! And looking at the frame [`kitchen-sink`](https://paritytech.github.io/polkadot-sdk/master/pallet_example_kitchensink/index.html)
+//! pallet, a showcase of all pallet macros.
+//!
+//! ### Pallet Sections
+//!
+//! The pallet sections in this template are:
+//!
+//! - A **configuration trait** that defines the types and parameters which the pallet depends on
+//! (denoted by the `#[pallet::config]` attribute). See: [`Config`].
+//! - A **means to store pallet-specific data** (denoted by the `#[pallet::storage]` attribute).
+//! See: [`storage_types`].
+//! - A **declaration of the events** this pallet emits (denoted by the `#[pallet::event]`
+//! attribute). See: [`Event`].
+//! - A **declaration of the errors** that this pallet can throw (denoted by the `#[pallet::error]`
+//! attribute). See: [`Error`].
+//! - A **set of dispatchable functions** that define the pallet's functionality (denoted by the
+//! `#[pallet::call]` attribute). See: [`dispatchables`].
+//!
+//! Run `cargo doc --package pallet-template --open` to view this pallet's documentation.
+
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
@@ -21,8 +68,9 @@ mod benchmarking;
//
#[frame_support::pallet]
pub mod pallet {
- use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*};
+ use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*, DefaultNoBound};
use frame_system::pallet_prelude::*;
+ use sp_runtime::traits::{CheckedAdd, One};
/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
@@ -38,11 +86,22 @@ pub mod pallet {
#[pallet::pallet]
pub struct Pallet(_);
+ /// A struct to store a single block-number. Has all the right derives to store it in storage.
+ ///
+ #[derive(
+ Encode, Decode, MaxEncodedLen, TypeInfo, CloneNoBound, PartialEqNoBound, DefaultNoBound,
+ )]
+ #[scale_info(skip_type_params(T))]
+ pub struct CompositeStruct {
+ /// A block number.
+ pub(crate) block_number: BlockNumberFor,
+ }
+
/// The pallet's storage items.
///
///
#[pallet::storage]
- pub type Something = StorageValue<_, u32>;
+ pub type Something = StorageValue<_, CompositeStruct>;
/// Pallets use events to inform users when important changes are made.
///
@@ -50,7 +109,7 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
/// We usually use passive tense for events.
- SomethingStored { something: u32, who: T::AccountId },
+ SomethingStored { block_number: BlockNumberFor, who: T::AccountId },
}
/// Errors inform users that something went wrong.
@@ -76,19 +135,23 @@ pub mod pallet {
/// storage and emits an event. This function must be dispatched by a signed extrinsic.
#[pallet::call_index(0)]
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
- pub fn do_something(origin: OriginFor, something: u32) -> DispatchResultWithPostInfo {
+ pub fn do_something(origin: OriginFor, bn: u32) -> DispatchResultWithPostInfo {
// Check that the extrinsic was signed and get the signer.
// This function will return an error if the extrinsic is not signed.
//
let who = ensure_signed(origin)?;
+ // Convert the u32 into a block number. This is possible because the set of trait bounds
+ // defined in [`frame_system::Config::BlockNumber`].
+ let block_number: BlockNumberFor = bn.into();
+
// Update storage.
- >::put(something);
+ >::put(CompositeStruct { block_number });
// Emit an event.
- Self::deposit_event(Event::SomethingStored { something, who });
+ Self::deposit_event(Event::SomethingStored { block_number, who });
- // Return a successful DispatchResultWithPostInfo
+ // Return a successful [`DispatchResultWithPostInfo`] or [`DispatchResult`].
Ok(().into())
}
@@ -102,11 +165,19 @@ pub mod pallet {
match >::get() {
// Return an error if the value has not been set.
None => Err(Error::::NoneValue)?,
- Some(old) => {
+ Some(mut old) => {
// Increment the value read from storage; will error in the event of overflow.
- let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?;
+ old.block_number = old
+ .block_number
+ .checked_add(&One::one())
+ // ^^ equivalent is to:
+ // .checked_add(&1u32.into())
+ // both of which build a `One` instance for the type `BlockNumber`.
+ .ok_or(Error::::StorageOverflow)?;
// Update the value in storage with the incremented result.
- >::put(new);
+ >::put(old);
+ // Explore how you can rewrite this using
+ // [`frame_support::storage::StorageValue::mutate`].
Ok(().into())
},
}
diff --git a/templates/parachain/pallets/template/src/tests.rs b/templates/parachain/pallets/template/src/tests.rs
index 9ad3076be2cc..a4a41af63c2e 100644
--- a/templates/parachain/pallets/template/src/tests.rs
+++ b/templates/parachain/pallets/template/src/tests.rs
@@ -7,7 +7,7 @@ fn it_works_for_default_value() {
// Dispatch a signed extrinsic.
assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42));
// Read pallet storage and assert an expected result.
- assert_eq!(Something::::get(), Some(42));
+ assert_eq!(Something::::get().map(|v| v.block_number), Some(42));
});
}