polkadot_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//! > [#polkadot-sdk#1378](https://github.com/paritytech/polkadot-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//! In that one can always convert from the inner type into the outer type, but not vice versa. This
27//! is usually expressed by implementing `From`, `TryFrom`, `From<Result<_>>` and similar traits.
28//!
29//! ### Example
30//!
31//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a
32//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional
33//! `GenesisConfig`.
34#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)]
35#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)]
36//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and
37//! [`RuntimeGenesisConfig`] generated in [`runtime`] respectively.
38//!
39//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If
40//! you explore further, you will soon realize that each variant is merely a pointer to the `Call`
41//! type in each pallet, for example [`pallet_foo::Call`].
42//!
43//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo`
44//! which utilized [`frame::pallet_macros::origin`].
45//!
46//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s
47//! [`pallet_bar::GenesisConfig`].
48//!
49//! You can find other composite enums by scanning [`runtime`] for other types who's name starts
50//! with `Runtime`. Some of the more noteworthy ones are:
51//!
52//! - [`RuntimeEvent`]
53//! - [`RuntimeError`]
54//! - [`RuntimeHoldReason`]
55//!
56//! ### Adding Further Constraints to Runtime Composite Enums
57//!
58//! This section explores a common scenario where a pallet has access to one of these runtime
59//! composite enums, but it wishes to further specify it by adding more trait bounds to it.
60//!
61//! Let's take the example of `RuntimeCall`. This is an associated type in
62//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they
63//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of
64//! the entire runtime.
65//!
66//! But, let's not forget that this is information that *we know*, and the Rust compiler does not.
67//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of
68//! [`frame_system::Config::RuntimeCall`] are specifying:
69#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)]
70//! So, when at a given pallet, one accesses `<T as frame_system::Config>::RuntimeCall`, the type is
71//! extremely opaque from the perspective of the Rust compiler.
72//!
73//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each
74//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`,
75//! therefore there should be a `impl From<Call<_>> for RuntimeCall`.
76//!
77//! The only way to express this using Rust's associated types is for the pallet to **define its own
78//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
79//!
80//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is
81//! very similar to [`TryFrom`].
82#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)]
83//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is
84//! passed to `frame_system`.
85#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)]
86//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is
87//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated
88//! > type representing `RuntimeCall`.
89//!
90//! Another way to look at this is:
91//!
92//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall`
93//! are two different representations of the same concrete type that is only known when the runtime
94//! is being constructed.
95//!
96//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait
97//! bounds, such as being [`frame::traits::IsSubType`]:
98#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)]
99//! > Once Rust's "_Associated Type Bounds RFC_" is usable, this syntax can be used to
100//! > simplify the above scenario. See [this](https://github.com/paritytech/polkadot-sdk/issues/3743)
101//! > issue for more information.
102//!
103//! ### Asserting Equality of Multiple Runtime Composite Enums
104//!
105//! Recall that in the above example, `<T as Config>::RuntimeCall` and `<T as
106//! frame_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time we
107//! have to represent them with two different associated types with different bounds. Would it not
108//! be cool if we had a test to make sure they actually resolve to the same concrete type once the
109//! runtime is constructed? The following snippet exactly does that:
110#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
111//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is,
112//! and what [`core::any::TypeId`] is. Another way to assert this is using
113//! [`frame::traits::IsType`].
114//!
115//! ## Type Aliases
116//!
117//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy:
118//!
119//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and
120//! `System`
121//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is
122//! important to FRAME internals such as `executive`, as it implements traits such as
123//! [`frame::traits::Hooks`].
124//!
125//! ## Further Details
126//!
127//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of
128//! `RuntimeOrigin`.
129//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an
130//! extrinsic. See [`crate::reference_docs::transaction_extensions`] for more information.
131//! * See the documentation of [`construct_runtime`].
132//! * See the corresponding lecture in the [PBA Lectures](https://www.youtube.com/watch?v=OCBC1pMYPoc&list=PL-w_i5kwVqbni1Ch2j_RwTIXiB-bwnYqq&index=11).
133//!
134//!
135//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime
136//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo
137//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem
138//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime
139//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo
140//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call
141//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet
142//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar
143//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig
144//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent
145//! [`RuntimeGenesisConfig`]:
146//! crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig
147//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin
148//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller
149//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError
150//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall
151//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason
152
153use frame::prelude::*;
154
155#[docify::export]
156#[frame::pallet(dev_mode)]
157pub mod pallet_foo {
158 use super::*;
159
160 #[pallet::config]
161 pub trait Config: frame_system::Config {}
162
163 #[pallet::origin]
164 #[derive(
165 PartialEq, Eq, Clone, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
166 )]
167 pub enum Origin {
168 A,
169 B,
170 }
171
172 #[pallet::pallet]
173 pub struct Pallet<T>(_);
174
175 #[pallet::call]
176 impl<T: Config> Pallet<T> {
177 pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
178 todo!();
179 }
180
181 pub fn other(_origin: OriginFor<T>) -> DispatchResult {
182 todo!();
183 }
184 }
185}
186
187#[docify::export]
188#[frame::pallet(dev_mode)]
189pub mod pallet_bar {
190 use super::*;
191
192 #[pallet::config]
193 pub trait Config: frame_system::Config {}
194
195 #[pallet::pallet]
196 pub struct Pallet<T>(_);
197
198 #[pallet::genesis_config]
199 #[derive(DefaultNoBound)]
200 pub struct GenesisConfig<T: Config> {
201 pub initial_account: Option<T::AccountId>,
202 }
203
204 #[pallet::genesis_build]
205 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
206 fn build(&self) {}
207 }
208
209 #[pallet::call]
210 impl<T: Config> Pallet<T> {
211 pub fn bar(_origin: OriginFor<T>) -> DispatchResult {
212 todo!();
213 }
214 }
215}
216
217pub mod runtime {
218 use super::{pallet_bar, pallet_foo};
219 use frame::{runtime::prelude::*, testing_prelude::*};
220
221 #[docify::export(runtime_exp)]
222 construct_runtime!(
223 pub struct Runtime {
224 System: frame_system,
225 PalletFoo: pallet_foo,
226 PalletBar: pallet_bar,
227 }
228 );
229
230 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
231 impl frame_system::Config for Runtime {
232 type Block = MockBlock<Self>;
233 }
234
235 impl pallet_foo::Config for Runtime {}
236 impl pallet_bar::Config for Runtime {}
237}
238
239#[frame::pallet(dev_mode)]
240pub mod pallet_with_specific_runtime_call {
241 use super::*;
242 use frame::traits::IsSubType;
243
244 #[docify::export(custom_runtime_call)]
245 /// A pallet that wants to further narrow down what `RuntimeCall` is.
246 #[pallet::config]
247 pub trait Config: frame_system::Config {
248 type RuntimeCall: IsSubType<Call<Self>>;
249 }
250
251 #[pallet::pallet]
252 pub struct Pallet<T>(_);
253
254 // note that this pallet needs some `call` to have a `enum Call`.
255 #[pallet::call]
256 impl<T: Config> Pallet<T> {
257 pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
258 todo!();
259 }
260 }
261
262 #[docify::export(custom_runtime_call_usages)]
263 impl<T: Config> Pallet<T> {
264 fn _do_something_useful_with_runtime_call(call: <T as Config>::RuntimeCall) {
265 // check if the runtime call given is of this pallet's variant.
266 let _maybe_my_call: Option<&Call<T>> = call.is_sub_type();
267 todo!();
268 }
269 }
270
271 #[docify::export(assert_equality)]
272 #[pallet::hooks]
273 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
274 fn integrity_test() {
275 use core::any::TypeId;
276 assert_eq!(
277 TypeId::of::<<T as Config>::RuntimeCall>(),
278 TypeId::of::<<T as frame_system::Config>::RuntimeCall>()
279 );
280 }
281 }
282}
283
284pub mod runtime_with_specific_runtime_call {
285 use super::pallet_with_specific_runtime_call;
286 use frame::{runtime::prelude::*, testing_prelude::*};
287
288 construct_runtime!(
289 pub struct Runtime {
290 System: frame_system,
291 PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call,
292 }
293 );
294
295 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
296 impl frame_system::Config for Runtime {
297 type Block = MockBlock<Self>;
298 }
299
300 #[docify::export(pallet_with_specific_runtime_call_impl)]
301 impl pallet_with_specific_runtime_call::Config for Runtime {
302 // an implementation of `IsSubType` is provided by `construct_runtime`.
303 type RuntimeCall = RuntimeCall;
304 }
305}