Pallet Config

Each pallet includes a trait Config which is used to configure the pallet in the context of your larger runtime.

#![allow(unused)]
fn main() {
#[pallet::config]
pub trait Config: frame_system::Config {
	// -- snip --
}
}

It sucks to keep repeating this about different parts of FRAME development, but the full power of the Config trait can only be understood once you have moved passed the basics.

For now, we just want to focus on the basics.

T as Config

We use our Pallet's Config all over our code, but through a generic parameter T.

This is what is meant with <T: Config> that you see everywhere.

The simplest way to understand is that wherever you see T, you have access to our trait Config and the types and functions inside of it.

Supertraits

To understand how we use the Config trait, we first need to learn about Rust supertraits.

Supertraits are similar to the concept of "inheritance" from other programming languages. In Rust, it allows one trait as being a superset of another trait.

You will notice that our Config trait is a subtrait of the supertrait frame_system::Config.

What is frame_system? What is in frame_system::Config?

FRAME System

In order for our blockchain to function, we need some base level primitives.

  • Account Id
  • Block Number
  • Block Hash
  • Nonce
  • etc...

frame_system provides all of that, and all the basic level functions needed for your blockchain to operate.

These types, and more, are defined within the frame_system::Config:

#![allow(unused)]
fn main() {
pub trait Config: 'static + Eq + Clone {
	type Hash: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + SimpleBitOps + Ord + Default + Copy + CheckEqual + sp_std::hash::Hash + AsRef<[u8]> + AsMut<[u8]> + MaxEncodedLen;
	type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + MaxEncodedLen;
	type Block: Parameter + Member + traits::Block<Hash = Self::Hash>;
	type Nonce: Parameter + Member + MaybeSerializeDeserialize + Debug + Default + MaybeDisplay + AtLeast32Bit + Copy + MaxEncodedLen;
	// -- snip --
}
}

Because our trait Config is a superset of frame_system::Config, we have access to these types too.

This is why you see in our starting code T::AccountId. We are able to access the AccountId type, which is originally defined inside frame_system::Config through our trait Config, via the generic trait T.

Phew.

If this doesn't make sense, that's okay. You should be able to follow the patterns for successfully programming all this, and you can learn the deep Rust stuff later.

Our Config

Our config only includes one item for now: RuntimeEvent.

It has a pretty nasty trait bound:

#![allow(unused)]
fn main() {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}

The main purpose of this trait bound is to allow events of this pallet to be converted to and from an "aggregated" event type, which contains all possible event variants from all possible Pallets in our blockchain.

Remember, our runtime is composed of multiple pallets, some we create, some which come with the polkadot-sdk, some that we import from 3rd parties.

Each of these pallets will want to include their own custom events, and our blockchain as a whole needs to be able to handle all of them.

The RuntimeEvent type, with the help of our macros, aggregates all of these events coming from all of these pallets. These trait bounds help us use this type!

If you want to learn more about this (super optional), check out this video:

#![allow(unused)]
fn main() {
use super::*;
use frame::prelude::*;

impl<T: Config> Pallet<T> {
	pub fn mint(owner: T::AccountId) -> DispatchResult {
		Self::deposit_event(Event::<T>::Created { owner });
		Ok(())
	}
}
}
#![allow(unused)]
#![cfg_attr(not(feature = "std"), no_std)]

fn main() {
mod impls;
mod tests;

use frame::prelude::*;
pub use pallet::*;

#[frame::pallet(dev_mode)]
pub mod pallet {
	use super::*;

	#[pallet::pallet]
	pub struct Pallet<T>(core::marker::PhantomData<T>);

	/* 🚧 TODO 🚧: Learn about Pallet `Config` and `frame_system`. */
	#[pallet::config]
	pub trait Config: frame_system::Config {
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
	}

	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		Created { owner: T::AccountId },
	}

	#[pallet::error]
	pub enum Error<T> {}

	#[pallet::call]
	impl<T: Config> Pallet<T> {
		pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
			let who = ensure_signed(origin)?;
			Self::mint(who)?;
			Ok(())
		}
	}
}
}
diff --git a/src/impls.rs b/src/impls.rs
index ecb2e7d..03abf99 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -2,7 +2,6 @@ use super::*;
 use frame::prelude::*;
 
 impl<T: Config> Pallet<T> {
-	/* 🚧 TODO 🚧: Learn about `AccountId`. */
 	pub fn mint(owner: T::AccountId) -> DispatchResult {
 		Self::deposit_event(Event::<T>::Created { owner });
 		Ok(())
diff --git a/src/lib.rs b/src/lib.rs
index 76a7966..405a6e5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,7 @@ pub mod pallet {
 	#[pallet::pallet]
 	pub struct Pallet<T>(core::marker::PhantomData<T>);
 
+	/* 🚧 TODO 🚧: Learn about Pallet `Config` and `frame_system`. */
 	#[pallet::config]
 	pub trait Config: frame_system::Config {
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
@@ -30,7 +31,6 @@ pub mod pallet {
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
 		pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
-			/* 🚧 TODO 🚧: Learn about origin. */
 			let who = ensure_signed(origin)?;
 			Self::mint(who)?;
 			Ok(())