#
pezkuwi_sdk_docs/reference_docs/
frame_runtime_types.rs

    1//! # FRAME Runtime Types
    2//!
    3//! This reference document briefly explores the idea around types generated at the runtime level by
    4//! the FRAME macros.
    5//!
    6//! > As of now, many of these important types are generated within the internals of
    7//! > [`construct_runtime`], and there is no easy way for you to visually know they exist.
    8//! > [#pezkuwi-sdk#1378](https://github.com/pezkuwichain/pezkuwi-sdk/pull/1378) is meant to
    9//! > significantly improve this. Exploring the rust-docs of a runtime, such as [`runtime`] which is
    10//! > defined in this module is as of now the best way to learn about these types.
    11//!
    12//! ## Composite Enums
    13//!
    14//! Many types within a FRAME runtime follow the following structure:
    15//!
    16//! * Each individual pallet defines a type, for example `Foo`.
    17//! * At the runtime level, these types are amalgamated into a single type, for example
    18//!   `RuntimeFoo`.
    19//!
    20//! As the names suggest, all composite enums in a FRAME runtime start their name with `Runtime`.
    21//! For example, `RuntimeCall` is a representation of the most high level `Call`-able type in the
    22//! runtime.
    23//!
    24//! Composite enums are generally convertible to their individual parts as such:
    25#![doc = simple_mermaid::mermaid!("../../../mermaid/outer_runtime_types.mmd")]
    26//!
    27//! In that one can always convert from the inner type into the outer type, but not vice versa. This
    28//! is usually expressed by implementing `From`, `TryFrom`, `From<Result<_>>` and similar traits.
    29//!
    30//! ### Example
    31//!
    32//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a
    33//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional
    34//! `GenesisConfig`.
    35#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)]
    36#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)]
    37//!
    38//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and
    39//! [`RuntimeGenesisConfig`] generated in [`runtime`] respectively.
    40//!
    41//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If
    42//! you explore further, you will soon realize that each variant is merely a pointer to the `Call`
    43//! type in each pallet, for example [`pallet_foo::Call`].
    44//!
    45//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo`
    46//! which utilized [`frame::pallet_macros::origin`].
    47//!
    48//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s
    49//! [`pallet_bar::GenesisConfig`].
    50//!
    51//! You can find other composite enums by scanning [`runtime`] for other types who's name starts
    52//! with `Runtime`. Some of the more noteworthy ones are:
    53//!
    54//! - [`RuntimeEvent`]
    55//! - [`RuntimeError`]
    56//! - [`RuntimeHoldReason`]
    57//!
    58//! ### Adding Further Constraints to Runtime Composite Enums
    59//!
    60//! This section explores a common scenario where a pallet has access to one of these runtime
    61//! composite enums, but it wishes to further specify it by adding more trait bounds to it.
    62//!
    63//! Let's take the example of `RuntimeCall`. This is an associated type in
    64//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they
    65//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of
    66//! the entire runtime.
    67//!
    68//! But, let's not forget that this is information that *we know*, and the Rust compiler does not.
    69//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of
    70//! [`frame_system::Config::RuntimeCall`] are specifying:
    71#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)]
    72//!
    73//! So, when at a given pallet, one accesses `<T as frame_system::Config>::RuntimeCall`, the type is
    74//! extremely opaque from the perspective of the Rust compiler.
    75//!
    76//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each
    77//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`,
    78//! therefore there should be a `impl From<Call<_>> for RuntimeCall`.
    79//!
    80//! The only way to express this using Rust's associated types is for the pallet to **define its own
    81//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
    82//!
    83//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is
    84//! very similar to [`TryFrom`].
    85#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)]
    86//!
    87//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is
    88//! passed to `frame_system`.
    89#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)]
    90//!
    91//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is
    92//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated
    93//! > type representing `RuntimeCall`.
    94//!
    95//! Another way to look at this is:
    96//!
    97//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall`
    98//! are two different representations of the same concrete type that is only known when the runtime
    99//! is being constructed.
    100//!
    101//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait
    102//! bounds, such as being [`frame::traits::IsSubType`]:
    103#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)]
    104//!
    105//! > Once Rust's "_Associated Type Bounds RFC_" is usable, this syntax can be used to
    106//! > simplify the above scenario. See [this](https://github.com/pezkuwichain/pezkuwi-sdk/issues/133)
    107//! > issue for more information.
    108//!
    109//! ### Asserting Equality of Multiple Runtime Composite Enums
    110//!
    111//! Recall that in the above example, `<T as Config>::RuntimeCall` and `<T as
    112//! frame_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time we
    113//! have to represent them with two different associated types with different bounds. Would it not
    114//! be cool if we had a test to make sure they actually resolve to the same concrete type once the
    115//! runtime is constructed? The following snippet exactly does that:
    116#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
    117//!
    118//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is,
    119//! and what [`core::any::TypeId`] is. Another way to assert this is using
    120//! [`frame::traits::IsType`].
    121//!
    122//! ## Type Aliases
    123//!
    124//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy:
    125//!
    126//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and
    127//!   `System`
    128//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is
    129//!   important to FRAME internals such as `executive`, as it implements traits such as
    130//!   [`frame::traits::Hooks`].
    131//!
    132//! ## Further Details
    133//!
    134//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of
    135//!   `RuntimeOrigin`.
    136//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an
    137//!   extrinsic. See [`crate::reference_docs::transaction_extensions`] for more information.
    138//! * See the documentation of [`construct_runtime`].
    139//! * See the corresponding lecture in the [PBA Lectures](https://www.youtube.com/watch?v=OCBC1pMYPoc&list=PL-w_i5kwVqbni1Ch2j_RwTIXiB-bwnYqq&index=11).
    140//!
    141//!
    142//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime
    143//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo
    144//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem
    145//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime
    146//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo
    147//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call
    148//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet
    149//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar
    150//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig
    151//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent
    152//! [`RuntimeGenesisConfig`]:
    153//!     crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig
    154//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin
    155//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller
    156//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError
    157//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall
    158//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason
    159
    160use frame::prelude::*;
    161
    162#[docify::export]
    163#[frame::pallet(dev_mode)]
    164pub mod pallet_foo {
    165	use super::*;
    166
    167	#[pallet::config]
    168	pub trait Config: frame_system::Config {}
    169
    170	#[pallet::origin]
    171	#[derive(
    172		PartialEq,
    173		Eq,
    174		Clone,
    175		RuntimeDebug,
    176		Encode,
    177		Decode,
    178		DecodeWithMemTracking,
    179		TypeInfo,
    180		MaxEncodedLen,
    181	)]
    182	pub enum Origin {
    183		A,
    184		B,
    185	}
    186
    187	#[pallet::pallet]
    188	pub struct Pallet<T>(_);
    189
    190	#[pallet::call]
    191	impl<T: Config> Pallet<T> {
    192		pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
    193			todo!();
    194		}
    195
    196		pub fn other(_origin: OriginFor<T>) -> DispatchResult {
    197			todo!();
    198		}
    199	}
    200}
    201
    202#[docify::export]
    203#[frame::pallet(dev_mode)]
    204pub mod pallet_bar {
    205	use super::*;
    206
    207	#[pallet::config]
    208	pub trait Config: frame_system::Config {}
    209
    210	#[pallet::pallet]
    211	pub struct Pallet<T>(_);
    212
    213	#[pallet::genesis_config]
    214	#[derive(DefaultNoBound)]
    215	pub struct GenesisConfig<T: Config> {
    216		pub initial_account: Option<T::AccountId>,
    217	}
    218
    219	#[pallet::genesis_build]
    220	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
    221		fn build(&self) {}
    222	}
    223
    224	#[pallet::call]
    225	impl<T: Config> Pallet<T> {
    226		pub fn bar(_origin: OriginFor<T>) -> DispatchResult {
    227			todo!();
    228		}
    229	}
    230}
    231
    232pub mod runtime {
    233	use super::{pallet_bar, pallet_foo};
    234	use frame::{runtime::prelude::*, testing_prelude::*};
    235
    236	#[docify::export(runtime_exp)]
    237	construct_runtime!(
    238		pub struct Runtime {
    239			System: frame_system,
    240			PalletFoo: pallet_foo,
    241			PalletBar: pallet_bar,
    242		}
    243	);
    244
    245	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
    246	impl frame_system::Config for Runtime {
    247		type Block = MockBlock<Self>;
    248	}
    249
    250	impl pallet_foo::Config for Runtime {}
    251	impl pallet_bar::Config for Runtime {}
    252}
    253
    254#[frame::pallet(dev_mode)]
    255pub mod pallet_with_specific_runtime_call {
    256	use super::*;
    257	use frame::traits::IsSubType;
    258
    259	#[docify::export(custom_runtime_call)]
    260	/// A pallet that wants to further narrow down what `RuntimeCall` is.
    261	#[pallet::config]
    262	pub trait Config: frame_system::Config {
    263		type RuntimeCall: IsSubType<Call<Self>>;
    264	}
    265
    266	#[pallet::pallet]
    267	pub struct Pallet<T>(_);
    268
    269	// note that this pallet needs some `call` to have a `enum Call`.
    270	#[pallet::call]
    271	impl<T: Config> Pallet<T> {
    272		pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
    273			todo!();
    274		}
    275	}
    276
    277	#[docify::export(custom_runtime_call_usages)]
    278	impl<T: Config> Pallet<T> {
    279		fn _do_something_useful_with_runtime_call(call: <T as Config>::RuntimeCall) {
    280			// check if the runtime call given is of this pallet's variant.
    281			let _maybe_my_call: Option<&Call<T>> = call.is_sub_type();
    282			todo!();
    283		}
    284	}
    285
    286	#[docify::export(assert_equality)]
    287	#[pallet::hooks]
    288	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
    289		fn integrity_test() {
    290			use core::any::TypeId;
    291			assert_eq!(
    292				TypeId::of::<<T as Config>::RuntimeCall>(),
    293				TypeId::of::<<T as frame_system::Config>::RuntimeCall>()
    294			);
    295		}
    296	}
    297}
    298
    299pub mod runtime_with_specific_runtime_call {
    300	use super::pallet_with_specific_runtime_call;
    301	use frame::{runtime::prelude::*, testing_prelude::*};
    302
    303	construct_runtime!(
    304		pub struct Runtime {
    305			System: frame_system,
    306			PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call,
    307		}
    308	);
    309
    310	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
    311	impl frame_system::Config for Runtime {
    312		type Block = MockBlock<Self>;
    313	}
    314
    315	#[docify::export(pallet_with_specific_runtime_call_impl)]
    316	impl pallet_with_specific_runtime_call::Config for Runtime {
    317		// an implementation of `IsSubType` is provided by `construct_runtime`.
    318		type RuntimeCall = RuntimeCall;
    319	}
    320}