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}