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}