#
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/issues/251) 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 pezpallet 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: [`pezpallet_foo`] and [`pezpallet_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", pezpallet_foo)]
    36#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pezpallet_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 pezpallet and one for
    42//! `pezframe_system`. If you explore further, you will soon realize that each variant is merely a
    43//! pointer to the `Call` type in each pezpallet, for example [`pezpallet_foo::Call`].
    44//!
    45//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for
    46//! `pezpallet_foo` which utilized [`pezframe::pezpallet_macros::origin`].
    47//!
    48//! Finally, [`RuntimeGenesisConfig`] is composed of `pezframe_system` and a variant for
    49//! `pezpallet_bar`'s [`pezpallet_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 pezpallet 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//! [`pezframe_system::Config::RuntimeCall`], and all pallets have access to this type, because they
    65//! have access to [`pezframe_system::Config`]. Finally, this type is meant to be set to outer call
    66//! of 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//! [`pezframe_system::Config::RuntimeCall`] are specifying:
    71#![doc = docify::embed!("../../bizinikiwi/pezframe/system/src/lib.rs", system_runtime_call)]
    72//!
    73//! So, when at a given pezpallet, one accesses `<T as pezframe_system::Config>::RuntimeCall`, the
    74//! type is extremely opaque from the perspective of the Rust compiler.
    75//!
    76//! How can a pezpallet access the `RuntimeCall` type with further constraints? For example, each
    77//! pezpallet 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 pezpallet to **define its
    81//! own 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 [`pezframe::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 `pezframe_system`.
    89#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pezpallet_with_specific_runtime_call_impl)]
    90//!
    91//! > In other words, the degree of specificity that [`pezframe_system::Config::RuntimeCall`] has is
    92//! > not enough for the pezpallet to work with. Therefore, the pezpallet has to define its own
    93//! > associated
    94//! > type representing `RuntimeCall`.
    95//!
    96//! Another way to look at this is:
    97//!
    98//! `pezpallet_with_specific_runtime_call::Config::RuntimeCall` and
    99//! `pezframe_system::Config::RuntimeCall` are two different representations of the same concrete
    100//! type that is only known when the runtime is being constructed.
    101//!
    102//! Now, within this pezpallet, this new `RuntimeCall` can be used, and it can use its new trait
    103//! bounds, such as being [`pezframe::traits::IsSubType`]:
    104#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)]
    105//!
    106//! > Once Rust's "_Associated Type Bounds RFC_" is usable, this syntax can be used to
    107//! > simplify the above scenario. See [this](https://github.com/pezkuwichain/pezkuwi-sdk/issues/278)
    108//! > issue for more information.
    109//!
    110//! ### Asserting Equality of Multiple Runtime Composite Enums
    111//!
    112//! Recall that in the above example, `<T as Config>::RuntimeCall` and `<T as
    113//! pezframe_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time
    114//! we have to represent them with two different associated types with different bounds. Would it
    115//! not be cool if we had a test to make sure they actually resolve to the same concrete type once
    116//! the runtime is constructed? The following snippet exactly does that:
    117#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
    118//!
    119//! We leave it to the reader to further explore what [`pezframe::traits::Hooks::integrity_test`] is,
    120//! and what [`core::any::TypeId`] is. Another way to assert this is using
    121//! [`pezframe::traits::IsType`].
    122//!
    123//! ## Type Aliases
    124//!
    125//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy:
    126//!
    127//! * [`runtime::PalletFoo`] is an alias to [`pezpallet_foo::Pezpallet`]. Same for `PalletBar`, and
    128//!   `System`
    129//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is
    130//!   important to FRAME internals such as `executive`, as it implements traits such as
    131//!   [`pezframe::traits::Hooks`].
    132//!
    133//! ## Further Details
    134//!
    135//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of
    136//!   `RuntimeOrigin`.
    137//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an
    138//!   extrinsic. See [`crate::reference_docs::transaction_extensions`] for more information.
    139//! * See the documentation of [`construct_runtime`].
    140//! * See the corresponding lecture in the [PBA Lectures](https://www.youtube.com/watch?v=OCBC1pMYPoc&list=PL-w_i5kwVqbni1Ch2j_RwTIXiB-bwnYqq&index=11).
    141//!
    142//!
    143//! [`construct_runtime`]: pezframe::runtime::prelude::construct_runtime
    144//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo
    145//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem
    146//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime
    147//! [`pezpallet_foo`]: crate::reference_docs::frame_runtime_types::pezpallet_foo
    148//! [`pezpallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pezpallet_foo::Call
    149//! [`pezpallet_foo::Pezpallet`]: crate::reference_docs::frame_runtime_types::pezpallet_foo::Pezpallet
    150//! [`pezpallet_bar`]: crate::reference_docs::frame_runtime_types::pezpallet_bar
    151//! [`pezpallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pezpallet_bar::GenesisConfig
    152//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent
    153//! [`RuntimeGenesisConfig`]:
    154//!     crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig
    155//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin
    156//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller
    157//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError
    158//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall
    159//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason
    160
    161use pezframe::prelude::*;
    162
    163#[docify::export]
    164#[pezframe::pezpallet(dev_mode)]
    165pub mod pezpallet_foo {
    166	use super::*;
    167
    168	#[pezpallet::config]
    169	pub trait Config: pezframe_system::Config {}
    170
    171	#[pezpallet::origin]
    172	#[derive(
    173		PartialEq,
    174		Eq,
    175		Clone,
    176		RuntimeDebug,
    177		Encode,
    178		Decode,
    179		DecodeWithMemTracking,
    180		TypeInfo,
    181		MaxEncodedLen,
    182	)]
    183	pub enum Origin {
    184		A,
    185		B,
    186	}
    187
    188	#[pezpallet::pezpallet]
    189	pub struct Pezpallet<T>(_);
    190
    191	#[pezpallet::call]
    192	impl<T: Config> Pezpallet<T> {
    193		pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
    194			todo!();
    195		}
    196
    197		pub fn other(_origin: OriginFor<T>) -> DispatchResult {
    198			todo!();
    199		}
    200	}
    201}
    202
    203#[docify::export]
    204#[pezframe::pezpallet(dev_mode)]
    205pub mod pezpallet_bar {
    206	use super::*;
    207
    208	#[pezpallet::config]
    209	pub trait Config: pezframe_system::Config {}
    210
    211	#[pezpallet::pezpallet]
    212	pub struct Pezpallet<T>(_);
    213
    214	#[pezpallet::genesis_config]
    215	#[derive(DefaultNoBound)]
    216	pub struct GenesisConfig<T: Config> {
    217		pub initial_account: Option<T::AccountId>,
    218	}
    219
    220	#[pezpallet::genesis_build]
    221	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
    222		fn build(&self) {}
    223	}
    224
    225	#[pezpallet::call]
    226	impl<T: Config> Pezpallet<T> {
    227		pub fn bar(_origin: OriginFor<T>) -> DispatchResult {
    228			todo!();
    229		}
    230	}
    231}
    232
    233pub mod runtime {
    234	use super::{pezpallet_bar, pezpallet_foo};
    235	use pezframe::{runtime::prelude::*, testing_prelude::*};
    236
    237	#[docify::export(runtime_exp)]
    238	construct_runtime!(
    239		pub struct Runtime {
    240			System: pezframe_system,
    241			PalletFoo: pezpallet_foo,
    242			PalletBar: pezpallet_bar,
    243		}
    244	);
    245
    246	#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
    247	impl pezframe_system::Config for Runtime {
    248		type Block = MockBlock<Self>;
    249	}
    250
    251	impl pezpallet_foo::Config for Runtime {}
    252	impl pezpallet_bar::Config for Runtime {}
    253}
    254
    255#[pezframe::pezpallet(dev_mode)]
    256pub mod pezpallet_with_specific_runtime_call {
    257	use super::*;
    258	use pezframe::traits::IsSubType;
    259
    260	#[docify::export(custom_runtime_call)]
    261	/// A pezpallet that wants to further narrow down what `RuntimeCall` is.
    262	#[pezpallet::config]
    263	pub trait Config: pezframe_system::Config {
    264		type RuntimeCall: IsSubType<Call<Self>>;
    265	}
    266
    267	#[pezpallet::pezpallet]
    268	pub struct Pezpallet<T>(_);
    269
    270	// note that this pezpallet needs some `call` to have a `enum Call`.
    271	#[pezpallet::call]
    272	impl<T: Config> Pezpallet<T> {
    273		pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
    274			todo!();
    275		}
    276	}
    277
    278	#[docify::export(custom_runtime_call_usages)]
    279	impl<T: Config> Pezpallet<T> {
    280		fn _do_something_useful_with_runtime_call(call: <T as Config>::RuntimeCall) {
    281			// check if the runtime call given is of this pezpallet's variant.
    282			let _maybe_my_call: Option<&Call<T>> = call.is_sub_type();
    283			todo!();
    284		}
    285	}
    286
    287	#[docify::export(assert_equality)]
    288	#[pezpallet::hooks]
    289	impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
    290		fn integrity_test() {
    291			use core::any::TypeId;
    292			assert_eq!(
    293				TypeId::of::<<T as Config>::RuntimeCall>(),
    294				TypeId::of::<<T as pezframe_system::Config>::RuntimeCall>()
    295			);
    296		}
    297	}
    298}
    299
    300pub mod runtime_with_specific_runtime_call {
    301	use super::pezpallet_with_specific_runtime_call;
    302	use pezframe::{runtime::prelude::*, testing_prelude::*};
    303
    304	construct_runtime!(
    305		pub struct Runtime {
    306			System: pezframe_system,
    307			PalletWithSpecificRuntimeCall: pezpallet_with_specific_runtime_call,
    308		}
    309	);
    310
    311	#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
    312	impl pezframe_system::Config for Runtime {
    313		type Block = MockBlock<Self>;
    314	}
    315
    316	#[docify::export(pezpallet_with_specific_runtime_call_impl)]
    317	impl pezpallet_with_specific_runtime_call::Config for Runtime {
    318		// an implementation of `IsSubType` is provided by `construct_runtime`.
    319		type RuntimeCall = RuntimeCall;
    320	}
    321}