From 02e50adf7ba6cc65a9ef5c332b3e2974c8d23f48 Mon Sep 17 00:00:00 2001
From: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Date: Wed, 10 Jul 2024 17:46:08 +0100
Subject: [PATCH] Explain usage of `<T: Config>` in FRAME storage + Update
 parachain pallet template  (#4941)

Explains one of the annoying parts of FRAME storage that we have seen
multiple times in PBA everyone gets stuck on.

I have not updated the other two templates for now, and only reflected
it in the parachain template. That can happen in a follow-up.

- [x] Update possible answers in SE about the same topic.

---------

Co-authored-by: Serban Iorga <serban@parity.io>
Co-authored-by: command-bot <>
---
 Cargo.lock                                    |   1 +
 docs/sdk/src/polkadot_sdk/frame_runtime.rs    |   4 +-
 .../reference_docs/frame_storage_derives.rs   | 199 ++++++++++++++++++
 docs/sdk/src/reference_docs/mod.rs            |   4 +
 substrate/frame/bags-list/src/list/tests.rs   |   3 +-
 substrate/frame/nis/src/lib.rs                |   8 +-
 substrate/frame/nomination-pools/src/lib.rs   |   1 -
 substrate/frame/src/lib.rs                    |  42 +++-
 .../frame/state-trie-migration/src/lib.rs     |   2 -
 substrate/frame/support/src/lib.rs            |  12 ++
 templates/minimal/README.md                   |  19 +-
 templates/minimal/pallets/template/src/lib.rs |   3 +
 .../parachain/pallets/template/Cargo.toml     |  12 +-
 .../pallets/template/src/benchmarking.rs      |  17 +-
 .../parachain/pallets/template/src/lib.rs     |  91 +++++++-
 .../parachain/pallets/template/src/tests.rs   |   2 +-
 16 files changed, 365 insertions(+), 55 deletions(-)
 create mode 100644 docs/sdk/src/reference_docs/frame_storage_derives.rs

diff --git a/Cargo.lock b/Cargo.lock
index ab38930b1b0..3b46a2680e8 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 f9b8a381365..39255c8f51a 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 00000000000..3d9edef398a
--- /dev/null
+++ b/docs/sdk/src/reference_docs/frame_storage_derives.rs
@@ -0,0 +1,199 @@
+//! <section class="info">
+//! In all examples, a few lines of boilerplate have been hidden from each snippet for conciseness.
+//! </section>
+//!
+//! 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<T>(_);
+//! 	pub struct NewType(u32);
+//
+//! 	#[pallet::storage]
+//! 	pub type Something<T> = 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<T>, 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<T>(_);
+//! 	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
+//! 	pub struct NewType(u32);
+//!
+//! 	#[pallet::storage]
+//! 	pub type Something<T> = 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<T>(_);
+//! 	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
+//! 	pub struct NewType<T: Config>(BlockNumberFor<T>);
+//!
+//! 	#[pallet::storage]
+//! 	pub type Something<T: Config> = StorageValue<_, NewType<T>>;
+//! }
+//! ```
+//! 
+//! 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::_GeneratedPrefixForStorageSomething<T>,
+//! pallet_2::NewType<T>>: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<T> TypeInfo for NewType<T> 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<T>(_);
+//! 	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
+//! 	#[scale_info(skip_type_params(T))]
+//! 	pub struct NewType<T: Config>(BlockNumberFor<T>);
+//!
+//! 	#[pallet::storage]
+//! 	pub type Something<T: Config> = StorageValue<_, NewType<T>>;
+//! }
+//! ```
+//! 
+//! 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<T>(_);
+//! 	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo, Default)]
+//! 	#[scale_info(skip_type_params(T))]
+//! 	pub struct NewType<T: Config>(BlockNumberFor<T>);
+//!
+//! 	#[pallet::storage]
+//! 	pub type Something<T: Config> = StorageValue<_, NewType<T>, 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<T>`.
+//! `BlockNumberFor<T>` 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<T>(_);
+//! 	#[derive(
+//! 		codec::Encode,
+//! 		codec::Decode,
+//! 		codec::MaxEncodedLen,
+//! 		scale_info::TypeInfo,
+//! 		DefaultNoBound
+//!		)]
+//! 	#[scale_info(skip_type_params(T))]
+//! 	pub struct NewType<T:Config>(BlockNumberFor<T>);
+//!
+//! 	#[pallet::storage]
+//! 	pub type Something<T: Config> = StorageValue<_, NewType<T>, 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<T>(_);
+//! 	#[pallet::storage]
+//! 	pub type Something<T: Config> = 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<T>(_);
+//! 	#[pallet::storage]
+//! 	pub type Something<T: Config> = StorageValue<_, T::CustomType>;
+//! }
+//! ```
diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs
index 688339b7e38..c69c7936542 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 cd39b083172..e5fff76d75c 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 f38755836fb..d815ea6ac11 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 2aaea044636..2b5fe8b6041 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<T: Config> {
 	/// The identifier of the pool to which `who` belongs.
diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs
index e41f7f1c0ef..e09d8fc4fa1 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<T>(_);
+//! }
+//!
+//! 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 4ec649f9080..22ad640d3bd 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<MaxKeyLen: Get<u32>> {
 		/// 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<T: Config> {
 		/// The current top trie migration progress.
diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs
index 138091689a5..94f4b9dd4bd 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: <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_storage_derives/index.html>.
+	///
+	/// 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 eaa21a05ee8..b556a453608 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 713f014bbe6..92b90ad4412 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
+//! <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html>
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
diff --git a/templates/parachain/pallets/template/Cargo.toml b/templates/parachain/pallets/template/Cargo.toml
index 250895843e4..3b651ce427f 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 d1a9554aed6..5acad6e60de 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::<T>::get(), Some(value));
+		assert_eq!(Something::<T>::get().map(|v| v.block_number), Some(100u32.into()));
 	}
 
 	#[benchmark]
 	fn cause_error() {
-		Something::<T>::put(100u32);
+		Something::<T>::put(CompositeStruct { block_number: 100u32.into() });
 		let caller: T::AccountId = whitelisted_caller();
 		#[extrinsic_call]
 		cause_error(RawOrigin::Signed(caller));
 
-		assert_eq!(Something::<T>::get(), Some(101u32));
+		assert_eq!(Something::<T>::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 0420e2b9082..6bfb98972ae 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:
+//!
+//! <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html>
+//!
+//! And reading the main documentation of the `frame` crate:
+//!
+//! <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html>
+//!
+//! 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;
 // <https://paritytech.github.io/polkadot-sdk/master/frame_support/pallet_macros/index.html>
 #[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<T>(_);
 
+	/// A struct to store a single block-number. Has all the right derives to store it in storage.
+	/// <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_storage_derives/index.html>
+	#[derive(
+		Encode, Decode, MaxEncodedLen, TypeInfo, CloneNoBound, PartialEqNoBound, DefaultNoBound,
+	)]
+	#[scale_info(skip_type_params(T))]
+	pub struct CompositeStruct<T: Config> {
+		/// A block number.
+		pub(crate) block_number: BlockNumberFor<T>,
+	}
+
 	/// The pallet's storage items.
 	/// <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html#storage>
 	/// <https://paritytech.github.io/polkadot-sdk/master/frame_support/pallet_macros/attr.storage.html>
 	#[pallet::storage]
-	pub type Something<T> = StorageValue<_, u32>;
+	pub type Something<T: Config> = StorageValue<_, CompositeStruct<T>>;
 
 	/// Pallets use events to inform users when important changes are made.
 	/// <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html#event-and-error>
@@ -50,7 +109,7 @@ pub mod pallet {
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
 	pub enum Event<T: Config> {
 		/// We usually use passive tense for events.
-		SomethingStored { something: u32, who: T::AccountId },
+		SomethingStored { block_number: BlockNumberFor<T>, 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<T>, something: u32) -> DispatchResultWithPostInfo {
+		pub fn do_something(origin: OriginFor<T>, 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.
 			// <https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_origin/index.html>
 			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<T> = bn.into();
+
 			// Update storage.
-			<Something<T>>::put(something);
+			<Something<T>>::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 <Something<T>>::get() {
 				// Return an error if the value has not been set.
 				None => Err(Error::<T>::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::<T>::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::<T>::StorageOverflow)?;
 					// Update the value in storage with the incremented result.
-					<Something<T>>::put(new);
+					<Something<T>>::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 9ad3076be2c..a4a41af63c2 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::<Test>::get(), Some(42));
+		assert_eq!(Something::<Test>::get().map(|v| v.block_number), Some(42));
 	});
 }
 
-- 
GitLab