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}