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}