diff --git a/.gitlab/pipeline/build.yml b/.gitlab/pipeline/build.yml
index 15b4869997be1..423587c1fb576 100644
--- a/.gitlab/pipeline/build.yml
+++ b/.gitlab/pipeline/build.yml
@@ -91,7 +91,7 @@ build-rustdoc:
- .run-immediately
variables:
SKIP_WASM_BUILD: 1
- RUSTDOCFLAGS: ""
+ RUSTDOCFLAGS: "--default-theme=ayu --html-in-header ./docs/sdk/headers/header.html --extend-css ./docs/sdk/headers/theme.css"
artifacts:
name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc"
when: on_success
diff --git a/Cargo.lock b/Cargo.lock
index 410b45d00183a..c52feafe7a706 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13384,11 +13384,20 @@ dependencies = [
"cumulus-pallet-parachain-system",
"docify",
"frame",
+ "frame-system",
"kitchensink-runtime",
"pallet-aura",
+ "pallet-authorship",
+ "pallet-balances",
+ "pallet-collective",
"pallet-default-config-example",
+ "pallet-democracy",
"pallet-examples",
+ "pallet-multisig",
+ "pallet-proxy",
"pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-utility",
"parity-scale-codec",
"sc-cli",
"sc-client-db",
diff --git a/docs/mermaid/IA.mmd b/docs/mermaid/IA.mmd
index 93d3e92814cf1..4eb50bcf96a89 100644
--- a/docs/mermaid/IA.mmd
+++ b/docs/mermaid/IA.mmd
@@ -3,7 +3,7 @@ flowchart
devhub --> polkadot_sdk
devhub --> reference_docs
- devhub --> tutorial
+ devhub --> guides
polkadot_sdk --> substrate
polkadot_sdk --> frame
diff --git a/docs/mermaid/outer_runtime_types.mmd b/docs/mermaid/outer_runtime_types.mmd
new file mode 100644
index 0000000000000..c909df16af1ff
--- /dev/null
+++ b/docs/mermaid/outer_runtime_types.mmd
@@ -0,0 +1,3 @@
+flowchart LR
+ RuntimeCall --"TryInto"--> PalletCall
+ PalletCall --"Into"--> RuntimeCall
diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml
index 05aced8751aee..576a81834f8c5 100644
--- a/docs/sdk/Cargo.toml
+++ b/docs/sdk/Cargo.toml
@@ -17,7 +17,10 @@ workspace = true
# Needed for all FRAME-based code
parity-scale-codec = { version = "3.0.0", default-features = false }
scale-info = { version = "2.6.0", default-features = false }
-frame = { path = "../../substrate/frame", features = ["experimental", "runtime"] }
+frame = { path = "../../substrate/frame", features = [
+ "experimental",
+ "runtime",
+] }
pallet-examples = { path = "../../substrate/frame/examples" }
pallet-default-config-example = { path = "../../substrate/frame/examples/default-config" }
@@ -52,7 +55,18 @@ cumulus-pallet-parachain-system = { path = "../../cumulus/pallets/parachain-syst
] }
parachain-info = { package = "staging-parachain-info", path = "../../cumulus/parachains/pallets/parachain-info" }
pallet-aura = { path = "../../substrate/frame/aura", default-features = false }
+
+# Pallets and FRAME internals
pallet-timestamp = { path = "../../substrate/frame/timestamp" }
+pallet-balances = { path = "../../substrate/frame/balances" }
+pallet-transaction-payment = { path = "../../substrate/frame/transaction-payment" }
+pallet-utility = { path = "../../substrate/frame/utility" }
+pallet-multisig = { path = "../../substrate/frame/multisig" }
+pallet-proxy = { path = "../../substrate/frame/proxy" }
+pallet-authorship = { path = "../../substrate/frame/authorship" }
+pallet-collective = { path = "../../substrate/frame/collective" }
+pallet-democracy = { path = "../../substrate/frame/democracy" }
+frame-system = { path = "../../substrate/frame/system" }
# Primitives
sp-io = { path = "../../substrate/primitives/io" }
@@ -64,9 +78,5 @@ sp-runtime = { path = "../../substrate/primitives/runtime" }
# XCM
xcm = { package = "staging-xcm", path = "../../polkadot/xcm" }
-[dev-dependencies]
-parity-scale-codec = "3.6.5"
-scale-info = "2.9.0"
-
[features]
experimental = ["pallet-aura/experimental"]
diff --git a/docs/sdk/headers/header.html b/docs/sdk/headers/header.html
new file mode 100644
index 0000000000000..68dc5d5509e67
--- /dev/null
+++ b/docs/sdk/headers/header.html
@@ -0,0 +1,139 @@
+
+
+
diff --git a/docs/sdk/headers/theme.css b/docs/sdk/headers/theme.css
new file mode 100644
index 0000000000000..bb9254ec4a824
--- /dev/null
+++ b/docs/sdk/headers/theme.css
@@ -0,0 +1,16 @@
+:root {
+ --polkadot-pink: #E6007A ;
+ --polkadot-green: #56F39A ;
+ --polkadot-lime: #D3FF33 ;
+ --polkadot-cyan: #00B2FF ;
+ --polkadot-purple: #552BBF ;
+ }
+
+body > nav.sidebar > div.sidebar-crate > a > img {
+ /* logo width, given that the sidebar width is 200px; */
+ width: 190px;
+}
+
+body nav.sidebar {
+ flex: 0 0 250px;
+}
diff --git a/docs/sdk/headers/toc.html b/docs/sdk/headers/toc.html
deleted file mode 100644
index a4a074cb4f315..0000000000000
--- a/docs/sdk/headers/toc.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs
index 29cdda36ed15b..20a8639b27008 100644
--- a/docs/sdk/src/guides/your_first_pallet/mod.rs
+++ b/docs/sdk/src/guides/your_first_pallet/mod.rs
@@ -128,8 +128,8 @@
//!
//! Recall that within our pallet, (almost) all blocks of code are generic over ``. And,
//! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or
-//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how Rust
-//! traits and generics work. If unfamiliar with this pattern, read
+//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
+//! Rust traits and generics work. If unfamiliar with this pattern, read
//! [`crate::reference_docs::trait_based_programming`] before going further.
//!
//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
@@ -270,7 +270,7 @@
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)]
//!
//! > These `Runtime*` types are better explained in
-//! > [`crate::reference_docs::frame_composite_enums`].
+//! > [`crate::reference_docs::frame_runtime_types`].
//!
//! Then, we can rewrite the `transfer` dispatchable as such:
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)]
@@ -285,7 +285,6 @@
//! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`].
//!
//!
-//!
//! ## What Next?
//!
//! The following topics where used in this guide, but not covered in depth. It is suggested to
@@ -293,7 +292,7 @@
//!
//! - [`crate::reference_docs::safe_defensive_programming`].
//! - [`crate::reference_docs::frame_origin`].
-//! - [`crate::reference_docs::frame_composite_enums`].
+//! - [`crate::reference_docs::frame_runtime_types`].
//! - The pallet we wrote in this guide was using `dev_mode`, learn more in
//! [`frame::pallet_macros::config`].
//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs
index 075d9ddaffe5b..e211476d25144 100644
--- a/docs/sdk/src/lib.rs
+++ b/docs/sdk/src/lib.rs
@@ -15,7 +15,7 @@
//! - Start by learning about the the [`polkadot_sdk`], its structure and context.
//! - Then, head over the [`guides`]. This modules contains in-depth guides about the most important
//! user-journeys of the Polkadot SDK.
-//! - Whilst reading the guides, you might find back-links to [`crate::reference_docs`].
+//! - Whilst reading the guides, you might find back-links to [`reference_docs`].
//! - Finally, is the parent website of this crate that contains the
//! list of further tools related to the Polkadot SDK.
//!
@@ -25,6 +25,11 @@
#![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")]
#![warn(rustdoc::broken_intra_doc_links)]
#![warn(rustdoc::private_intra_doc_links)]
+#![doc(html_favicon_url = "https://polkadot.network/favicon-32x32.png")]
+#![doc(
+ html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg"
+)]
+#![doc(issue_tracker_base_url = "https://github.com/paritytech/polkadot-sdk/issues")]
/// Meta information about this crate, how it is built, what principles dictates its evolution and
/// how one can contribute to it.
diff --git a/docs/sdk/src/meta_contributing.rs b/docs/sdk/src/meta_contributing.rs
index 7ecf8b0adfd3a..fcdcea9934bb6 100644
--- a/docs/sdk/src/meta_contributing.rs
+++ b/docs/sdk/src/meta_contributing.rs
@@ -101,7 +101,7 @@
//! * Before even getting started, what is with all of this ``? We link to
//! [`crate::reference_docs::trait_based_programming`].
//! * First, the name. Why is this called `pallet::call`? This goes back to `enum Call`, which is
-//! explained in [`crate::reference_docs::frame_composite_enums`]. Build on top of this!
+//! explained in [`crate::reference_docs::frame_runtime_types`]. Build on top of this!
//! * Then, what is `origin`? Just an account id? [`crate::reference_docs::frame_origin`].
//! * Then, what is `DispatchResult`? Why is this called *dispatch*? Probably something that can be
//! explained in the documentation of [`frame::prelude::DispatchResult`].
@@ -138,7 +138,9 @@
//! injected, run:
//!
//! ```sh
-//! SKIP_WASM_BUILD=1 RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/toc.html" cargo doc -p polkadot-sdk-docs --no-deps --open
+//! SKIP_WASM_BUILD=1 \
+//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/header.html --extend-css $(pwd)/docs/sdk/headers/theme.css --default-theme=ayu" \
+//! cargo doc -p polkadot-sdk-docs --no-deps --open
//! ```
//!
//! If even faster build time for docs is needed, you can temporarily remove most of the
diff --git a/docs/sdk/src/reference_docs/extrinsic_encoding.rs b/docs/sdk/src/reference_docs/extrinsic_encoding.rs
index 9008f8f835f5d..8c8568a228fad 100644
--- a/docs/sdk/src/reference_docs/extrinsic_encoding.rs
+++ b/docs/sdk/src/reference_docs/extrinsic_encoding.rs
@@ -127,7 +127,7 @@
//! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME
//! pallet being called, and the inner enum represents the call being made within that pallet, and
//! any arguments to it. Read more about the call enum
-//! [here][crate::reference_docs::frame_composite_enums].
+//! [here][crate::reference_docs::frame_runtime_types].
//!
//! FRAME `Call` enums are automatically generated, and end up looking something like this:
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", call_data)]
diff --git a/docs/sdk/src/reference_docs/frame_composite_enums.rs b/docs/sdk/src/reference_docs/frame_composite_enums.rs
deleted file mode 100644
index 6051cd5344676..0000000000000
--- a/docs/sdk/src/reference_docs/frame_composite_enums.rs
+++ /dev/null
@@ -1 +0,0 @@
-//! # FRAME Composite Enums
diff --git a/docs/sdk/src/reference_docs/frame_origin.rs b/docs/sdk/src/reference_docs/frame_origin.rs
index a4078377cd77d..49533157b014d 100644
--- a/docs/sdk/src/reference_docs/frame_origin.rs
+++ b/docs/sdk/src/reference_docs/frame_origin.rs
@@ -1,14 +1,260 @@
//! # FRAME Origin
//!
-//! Notes:
-//!
-//! - Def talk about account abstraction and how it is a solved issue in frame. See Gav's talk in
-//! Protocol Berg 2023
-//! - system's raw origin, how it is amalgamated with other origins into one type
-//! [`frame_composite_enums`]
-//! - signed origin
-//! - unsigned origin, link to [`fee_less_runtime`]
-//! - Root origin, how no one can obtain it.
-//! - Abstract origin: how FRAME allows you to express "origin is 2/3 of the this body or 1/2 of
-//! that body or half of the token holders".
-//! - `type CustomOrigin: EnsureOrigin<_>` in pallets.
+//! Let's start by clarifying a common wrong assumption about Origin:
+//!
+//! **ORIGIN IS NOT AN ACCOUNT ID**.
+//!
+//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the
+//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the
+//! meanings that an origin can convey. This is the commonly used [`frame_system::ensure_signed`],
+//! where the return value happens to be an account-id.
+//!
+//! Instead, let's establish the following as the correct definition of an origin:
+//!
+//! > The origin type represents the privilege level of the caller of an extrinsic.
+//!
+//! That is, an extrinsic, through checking the origin, can *express what privilege level it wishes
+//! to impose on the caller of the extrinsic*. One of those checks can be as simple as "*any account
+//! that has signed a statement can pass*".
+//!
+//! But the origin system can also express more abstract and complicated privilege levels. For
+//! example:
+//!
+//! * If the majority of token holders agreed upon this. This is more or less what the
+//! [`pallet_democracy`] does under the hood ([reference](https://github.com/paritytech/polkadot-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/substrate/frame/democracy/src/lib.rs#L1603-L1633)).
+//! * If a specific ratio of an instance of [`pallet_collective`]/DAO agrees upon this.
+//! * If another consensus system, for example a bridged network or a parachain, agrees upon this.
+//! * If the majority of validator/authority set agrees upon this[^1].
+//! * If caller holds a particular NFT.
+//!
+//! and many more.
+//!
+//! ## Context
+//!
+//! First, let's look at where the `origin` type is encountered in a typical pallet. The `origin:
+//! OriginFor` has to be the first argument of any given callable extrinsic in FRAME:
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)]
+//!
+//! Typically, the code of an extrinsic starts with an origin check, such as
+//! [`frame_system::ensure_signed`].
+//!
+//! Note that [`OriginFor`](frame_system::pallet_prelude::OriginFor) is merely a shorthand for
+//! [`frame_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that
+//! `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is
+//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to
+//! familiarize yourself with these types.
+//!
+//! To understand this better, we will next create a pallet with a custom origin, which will add a
+//! new variant to `RuntimeOrigin`.
+//!
+//! ## Adding Custom Pallet Origin to the Runtime
+//!
+//! For example, given a pallet that defines the following custom origin:
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)]
+//!
+//! And a runtime with the following pallets:
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)]
+//!
+//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded.
+//! This `RuntimeOrigin` contains a variant for the [`frame_system::RawOrigin`] and the custom
+//! origin of the pallet.
+//!
+//! > Notice how the [`frame_system::ensure_signed`] is nothing more than a `match` statement. If
+//! > you want to know where the actual origin of an extrinsic is set (and the signature
+//! > verification happens, if any), see
+//! > [`sp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically
+//! > [`sp_runtime::traits::Applyable`]'s implementation.
+//!
+//! ## Asserting on a Custom Internal Origin
+//!
+//! In order to assert on a custom origin that is defined within your pallet, we need a way to first
+//! convert the `::RuntimeOrigin` into the local `enum Origin` of the
+//! current pallet. This is a common process that is explained in
+//! [`crate::reference_docs::frame_runtime_types#
+//! adding-further-constraints-to-runtime-composite-enums`].
+//!
+//! We use the same process here to express that `RuntimeOrigin` has a number of additional bounds,
+//! as follows.
+//!
+//! 1. Defining a custom `RuntimeOrigin` with further bounds in the pallet.
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)]
+//!
+//! 2. Using it in the pallet.
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)]
+//!
+//! ## Asserting on a Custom External Origin
+//!
+//! Very often, a pallet wants to have a parameterized origin that is **NOT** defined within the
+//! pallet. In other words, a pallet wants to delegate an origin check to something that is
+//! specified later at the runtime level. Like many other parameterizations in FRAME, this implies
+//! adding a new associated type to `trait Config`.
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)]
+//!
+//! Then, within the pallet, we can simply use this "unknown" origin check type:
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)]
+//!
+//! Finally, at the runtime, any implementation of [`frame::traits::EnsureOrigin`] can be passed.
+#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)]
+//!
+//! Indeed, some of these implementations of [`frame::traits::EnsureOrigin`] are similar to the ones
+//! that we know about: [`frame::runtime::prelude::EnsureSigned`],
+//! [`frame::runtime::prelude::EnsureSignedBy`], [`frame::runtime::prelude::EnsureRoot`],
+//! [`frame::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known
+//! to us, and are defined in other pallets.
+//!
+//! For example, [`pallet_collective`] defines [`pallet_collective::EnsureMember`] and
+//! [`pallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we alluded
+//! to earlier in this document.
+//!
+//! Make sure to check the full list of [implementors of
+//! `EnsureOrigin`](frame::traits::EnsureOrigin#implementors) for more inspiration.
+//!
+//! ## Obtaining Abstract Origins
+//!
+//! So far we have learned that FRAME pallets can assert on custom and abstract origin types,
+//! whether they are defined within the pallet or not. But how can we obtain these abstract origins?
+//!
+//! > All extrinsics that come from the outer world can generally only be obtained as either
+//! > `signed` or `none` origin.
+//!
+//! Generally, these abstract origins are only obtained within the runtime, when a call is
+//! dispatched within the runtime.
+//!
+//! ## Further References
+//!
+//! - [Gavin Wood's speech about FRAME features at Protocol Berg 2023.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195)
+//! - [A related StackExchange question.](https://substrate.stackexchange.com/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin)
+//!
+//! [^1]: Inherents are essentially unsigned extrinsics that need an [`frame_system::ensure_none`]
+//! origin check, and through the virtue of being an inherent, are agreed upon by all validators.
+
+use frame::prelude::*;
+
+#[frame::pallet(dev_mode)]
+pub mod pallet_for_origin {
+ use super::*;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ #[docify::export(call_simple)]
+ #[pallet::call]
+ impl Pallet {
+ pub fn do_something(_origin: OriginFor) -> DispatchResult {
+ // ^^^^^^^^^^^^^^^^^^^^^
+ todo!();
+ }
+ }
+}
+
+#[frame::pallet(dev_mode)]
+pub mod pallet_with_custom_origin {
+ use super::*;
+
+ #[docify::export(custom_origin_bound)]
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type RuntimeOrigin: From<::RuntimeOrigin>
+ + Into::RuntimeOrigin>>;
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ #[docify::export(custom_origin)]
+ /// A dummy custom origin.
+ #[pallet::origin]
+ #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
+ pub enum Origin {
+ /// If all holders of a particular NFT have agreed upon this.
+ AllNftHolders,
+ /// If all validators have agreed upon this.
+ ValidatorSet,
+ }
+
+ #[docify::export(custom_origin_usage)]
+ #[pallet::call]
+ impl Pallet {
+ pub fn only_validators(origin: OriginFor) -> DispatchResult {
+ // first, we convert from `::RuntimeOrigin` to `::RuntimeOrigin`
+ let local_runtime_origin = <::RuntimeOrigin as From<
+ ::RuntimeOrigin,
+ >>::from(origin);
+ // then we convert to `origin`, if possible
+ let local_origin =
+ local_runtime_origin.into().map_err(|_| "invalid origin type provided")?;
+ ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized");
+ todo!();
+ }
+ }
+}
+
+pub mod runtime_for_origin {
+ use super::pallet_with_custom_origin;
+ use frame::{runtime::prelude::*, testing_prelude::*};
+
+ #[docify::export(runtime_exp)]
+ construct_runtime!(
+ pub struct Runtime {
+ System: frame_system,
+ PalletWithCustomOrigin: pallet_with_custom_origin,
+ }
+ );
+
+ #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+ impl frame_system::Config for Runtime {
+ type Block = MockBlock;
+ }
+
+ impl pallet_with_custom_origin::Config for Runtime {
+ type RuntimeOrigin = RuntimeOrigin;
+ }
+}
+
+#[frame::pallet(dev_mode)]
+pub mod pallet_with_external_origin {
+ use super::*;
+ #[docify::export(external_origin_def)]
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type ExternalOrigin: EnsureOrigin;
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ #[docify::export(external_origin_usage)]
+ #[pallet::call]
+ impl Pallet {
+ pub fn externally_checked_ext(origin: OriginFor) -> DispatchResult {
+ let _ = T::ExternalOrigin::ensure_origin(origin)?;
+ todo!();
+ }
+ }
+}
+
+pub mod runtime_for_external_origin {
+ use super::*;
+ use frame::{runtime::prelude::*, testing_prelude::*};
+
+ construct_runtime!(
+ pub struct Runtime {
+ System: frame_system,
+ PalletWithExternalOrigin: pallet_with_external_origin,
+ }
+ );
+
+ #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+ impl frame_system::Config for Runtime {
+ type Block = MockBlock;
+ }
+
+ #[docify::export(external_origin_provide)]
+ impl pallet_with_external_origin::Config for Runtime {
+ type ExternalOrigin = EnsureSigned<::AccountId>;
+ }
+}
diff --git a/docs/sdk/src/reference_docs/frame_runtime_types.rs b/docs/sdk/src/reference_docs/frame_runtime_types.rs
new file mode 100644
index 0000000000000..b5838b79e5183
--- /dev/null
+++ b/docs/sdk/src/reference_docs/frame_runtime_types.rs
@@ -0,0 +1,306 @@
+//! # FRAME Runtime Types
+//!
+//! This reference document briefly explores the idea around types generated at the runtime level by
+//! the FRAME macros.
+//!
+//! > As of now, many of these important types are generated within the internals of
+//! > [`construct_runtime`], and there is no easy way for you to visually know they exist.
+//! > [#polkadot-sdk#1378](https://github.com/paritytech/polkadot-sdk/pull/1378) is meant to
+//! > significantly improve this. Exploring the rust-docs of a runtime, such as [`runtime`] which is
+//! > defined in this module is as of now the best way to learn about these types.
+//!
+//! ## Composite Enums
+//!
+//! Many types within a FRAME runtime follow the following structure:
+//!
+//! * Each individual pallet defines a type, for example `Foo`.
+//! * At the runtime level, these types are amalgamated into a single type, for example
+//! `RuntimeFoo`.
+//!
+//! As the names suggest, all composite enums in a FRAME runtime start their name with `Runtime`.
+//! For example, `RuntimeCall` is a representation of the most high level `Call`-able type in the
+//! runtime.
+//!
+//! Composite enums are generally convertible to their individual parts as such:
+#![doc = simple_mermaid::mermaid!("../../../mermaid/outer_runtime_types.mmd")]
+//!
+//! In that one can always convert from the inner type into the outer type, but not vice versa. This
+//! is usually expressed by implementing `From`, `TryFrom`, `From>` and similar traits.
+//!
+//! ### Example
+//!
+//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a
+//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional
+//! `GenesisConfig`.
+#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)]
+#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)]
+//!
+//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and
+//! [`RuntimeGenesisConfig`] generated in [`runtime`] by respectively.
+//!
+//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If
+//! you explore further, you will soon realize that each variant is merely a pointer to the `Call`
+//! type in each pallet, for example [`pallet_foo::Call`].
+//!
+//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo`
+//! which utilized [`frame::pallet_macros::origin`].
+//!
+//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s
+//! [`pallet_bar::GenesisConfig`].
+//!
+//! You can find other composite enums by scanning [`runtime`] for other types who's name starts
+//! with `Runtime`. Some of the more noteworthy ones are:
+//!
+//! - [`RuntimeEvent`]
+//! - [`RuntimeError`]
+//! - [`RuntimeHoldReason`]
+//!
+//! ### Adding Further Constraints to Runtime Composite Enums
+//!
+//! This section explores a common scenario where a pallet has access to one of these runtime
+//! composite enums, but it wishes to further specify it by adding more trait bounds to it.
+//!
+//! Let's take the example of `RuntimeCall`. This is an associated type in
+//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they
+//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of
+//! the entire runtime.
+//!
+//! But, let's not forget that this is information that *we know*, and the Rust compiler does not.
+//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of
+//! [`frame_system::Config::RuntimeCall`] are specifying:
+#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)]
+//!
+//! So, when at a given pallet, one accesses `::RuntimeCall`, the type is
+//! extremely opaque from the perspective of the Rust compiler.
+//!
+//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each
+//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`,
+//! therefore there should be a `impl From> for RuntimeCall`.
+//!
+//! The only way to express this using Rust's associated types is for the pallet to **define its own
+//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
+//!
+//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is
+//! very similar to [`TryFrom`].
+#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)]
+//!
+//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is
+//! passed to `frame_system`.
+#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)]
+//!
+//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is
+//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated
+//! > type representing `RuntimeCall`.
+//!
+//! Another way to look at this is:
+//!
+//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall`
+//! are two different representations of the same concrete type that is only known when the runtime
+//! is being constructed.
+//!
+//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait
+//! bounds, such as being [`frame::traits::IsSubType`]:
+#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)]
+//!
+//! ### Asserting Equality of Multiple Runtime Composite Enums
+//!
+//! Recall that in the above example, `::RuntimeCall` and `::RuntimeCall` are expected to be equal types, but at the compile-time we
+//! have to represent them with two different associated types with different bounds. Would it not
+//! be cool if we had a test to make sure they actually resolve to the same concrete type once the
+//! runtime is constructed? The following snippet exactly does that:
+#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
+//!
+//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is,
+//! and what [`core::any::TypeId`] is. Another way to assert this is using
+//! [`frame::traits::IsType`].
+//!
+//! ## Type Aliases
+//!
+//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy:
+//!
+//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and
+//! `System`
+//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is
+//! important to FRAME internals such as `executive`, as it implements traits such as
+//! [`frame::traits::Hooks`].
+//!
+//! ## Further Details
+//!
+//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of
+//! `RuntimeOrigin`.
+//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an
+//! extrinsic. See [`crate::reference_docs::signed_extensions`] for more information.
+//! * See the documentation of [`construct_runtime`].
+//! * See the corresponding lecture in the [pba-book](https://polkadot-blockchain-academy.github.io/pba-book/frame/outer-enum/page.html).
+//!
+//!
+//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime
+//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo
+//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem
+//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime
+//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo
+//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call
+//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet
+//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar
+//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig
+//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent
+//! [`RuntimeGenesisConfig`]:
+//! crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig
+//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin
+//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller
+//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError
+//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall
+//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason
+
+use frame::prelude::*;
+
+#[docify::export]
+#[frame::pallet(dev_mode)]
+pub mod pallet_foo {
+ use super::*;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
+
+ #[pallet::origin]
+ #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
+ pub enum Origin {
+ A,
+ B,
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ #[pallet::call]
+ impl Pallet {
+ pub fn foo(_origin: OriginFor) -> DispatchResult {
+ todo!();
+ }
+
+ pub fn other(_origin: OriginFor) -> DispatchResult {
+ todo!();
+ }
+ }
+}
+
+#[docify::export]
+#[frame::pallet(dev_mode)]
+pub mod pallet_bar {
+ use super::*;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ #[pallet::genesis_config]
+ #[derive(DefaultNoBound)]
+ pub struct GenesisConfig {
+ pub initial_account: Option,
+ }
+
+ #[pallet::genesis_build]
+ impl BuildGenesisConfig for GenesisConfig {
+ fn build(&self) {}
+ }
+
+ #[pallet::call]
+ impl Pallet {
+ pub fn bar(_origin: OriginFor) -> DispatchResult {
+ todo!();
+ }
+ }
+}
+
+pub mod runtime {
+ use super::{pallet_bar, pallet_foo};
+ use frame::{runtime::prelude::*, testing_prelude::*};
+
+ #[docify::export(runtime_exp)]
+ construct_runtime!(
+ pub struct Runtime {
+ System: frame_system,
+ PalletFoo: pallet_foo,
+ PalletBar: pallet_bar,
+ }
+ );
+
+ #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+ impl frame_system::Config for Runtime {
+ type Block = MockBlock;
+ }
+
+ impl pallet_foo::Config for Runtime {}
+ impl pallet_bar::Config for Runtime {}
+}
+
+#[frame::pallet(dev_mode)]
+pub mod pallet_with_specific_runtime_call {
+ use super::*;
+ use frame::traits::IsSubType;
+
+ #[docify::export(custom_runtime_call)]
+ /// A pallet that wants to further narrow down what `RuntimeCall` is.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type RuntimeCall: IsSubType>;
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ // note that this pallet needs some `call` to have a `enum Call`.
+ #[pallet::call]
+ impl Pallet {
+ pub fn foo(_origin: OriginFor) -> DispatchResult {
+ todo!();
+ }
+ }
+
+ #[docify::export(custom_runtime_call_usages)]
+ impl Pallet {
+ fn _do_something_useful_with_runtime_call(call: ::RuntimeCall) {
+ // check if the runtime call given is of this pallet's variant.
+ let _maybe_my_call: Option<&Call> = call.is_sub_type();
+ todo!();
+ }
+ }
+
+ #[docify::export(assert_equality)]
+ #[pallet::hooks]
+ impl Hooks> for Pallet {
+ fn integrity_test() {
+ use core::any::TypeId;
+ assert_eq!(
+ TypeId::of::<::RuntimeCall>(),
+ TypeId::of::<::RuntimeCall>()
+ );
+ }
+ }
+}
+
+pub mod runtime_with_specific_runtime_call {
+ use super::pallet_with_specific_runtime_call;
+ use frame::{runtime::prelude::*, testing_prelude::*};
+
+ construct_runtime!(
+ pub struct Runtime {
+ System: frame_system,
+ PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call,
+ }
+ );
+
+ #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+ impl frame_system::Config for Runtime {
+ type Block = MockBlock;
+ }
+
+ #[docify::export(pallet_with_specific_runtime_call_impl)]
+ impl pallet_with_specific_runtime_call::Config for Runtime {
+ // an implementation of `IsSubType` is provided by `construct_runtime`.
+ type RuntimeCall = RuntimeCall;
+ }
+}
diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs
index c16122ee4287b..760bb442c1664 100644
--- a/docs/sdk/src/reference_docs/mod.rs
+++ b/docs/sdk/src/reference_docs/mod.rs
@@ -43,16 +43,16 @@ pub mod extrinsic_encoding;
// TODO: @jsdw https://github.com/paritytech/polkadot-sdk-docs/issues/42
pub mod signed_extensions;
-/// Learn about *"Origin"* A topic in FRAME that enables complex account abstractions to be built.
-// TODO: @shawntabrizi https://github.com/paritytech/polkadot-sdk-docs/issues/43
+/// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built.
pub mod frame_origin;
/// Learn about how to write safe and defensive code in your FRAME runtime.
// TODO: @CrackTheCode016 https://github.com/paritytech/polkadot-sdk-docs/issues/44
pub mod safe_defensive_programming;
-/// Learn about composite enums in FRAME-based runtimes, such as "RuntimeEvent" and "RuntimeCall".
-pub mod frame_composite_enums;
+/// Learn about composite enums and other runtime level types, such as "RuntimeEvent" and
+/// "RuntimeCall".
+pub mod frame_runtime_types;
/// Learn about how to make a pallet/runtime that is fee-less and instead uses another mechanism to
/// control usage and sybil attacks.
diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs
index a1715ba490010..a2e5d726fdd00 100644
--- a/substrate/frame/src/lib.rs
+++ b/substrate/frame/src/lib.rs
@@ -163,6 +163,12 @@ pub mod runtime {
ConstU32, ConstU64, ConstU8,
};
+ /// Primary types used to parameterize `EnsureOrigin` and `EnsureRootWithArg`.
+ pub use frame_system::{
+ EnsureNever, EnsureNone, EnsureRoot, EnsureRootWithSuccess, EnsureSigned,
+ EnsureSignedBy,
+ };
+
/// Types to define your runtime version.
pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion};
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs
index b421d2aaffabe..83049919d01c3 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs
@@ -104,7 +104,7 @@ pub fn expand_outer_origin(
#[doc = #doc_string]
#[derive(Clone)]
pub struct RuntimeOrigin {
- caller: OriginCaller,
+ pub caller: OriginCaller,
filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>,
}
diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs
index 20b8d74310f3e..441b21e26ff21 100644
--- a/substrate/frame/support/procedural/src/lib.rs
+++ b/substrate/frame/support/procedural/src/lib.rs
@@ -1480,22 +1480,11 @@ pub fn validate_unsigned(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}
-/// The `#[pallet::origin]` attribute allows you to define some origin for the pallet.
///
-/// Item must be either a type alias, an enum, or a struct. It needs to be public.
-///
-/// E.g.:
-///
-/// ```ignore
-/// #[pallet::origin]
-/// pub struct Origin(PhantomData<(T)>);
-/// ```
-///
-/// **WARNING**: modifying origin changes the outer runtime origin. This outer runtime origin
-/// can be stored on-chain (e.g. in `pallet-scheduler`), thus any change must be done with care
-/// as it might require some migration.
+/// ---
///
-/// NOTE: for instantiable pallets, the origin must be generic over `T` and `I`.
+/// **Rust-Analyzer users**: See the documentation of the Rust item in
+/// `frame_support::pallet_macros::origin`.
#[proc_macro_attribute]
pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs
index cd12da6de54e7..26fc1fe42c118 100644
--- a/substrate/frame/support/src/lib.rs
+++ b/substrate/frame/support/src/lib.rs
@@ -2274,9 +2274,8 @@ pub mod pallet_macros {
pub use frame_support_procedural::{
composite_enum, config, disable_frame_system_supertrait_check, error, event,
extra_constants, feeless_if, generate_deposit, generate_store, getter, hooks,
- import_section, inherent, no_default, no_default_bounds, origin, pallet_section,
- storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight,
- whitelist_storage,
+ import_section, inherent, no_default, no_default_bounds, pallet_section, storage_prefix,
+ storage_version, type_value, unbounded, validate_unsigned, weight, whitelist_storage,
};
/// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. In
@@ -2718,7 +2717,7 @@ pub mod pallet_macros {
/// }
/// ```
pub use frame_support_procedural::storage;
- /// This attribute is attached to a function inside an `impl` block annoated with
+ /// This attribute is attached to a function inside an `impl` block annotated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a
/// given work item to be valid.
///
@@ -2726,21 +2725,21 @@ pub mod pallet_macros {
/// should have the same signature as the function it is attached to, except that it should
/// return a `bool` instead.
pub use frame_support_procedural::task_condition;
- /// This attribute is attached to a function inside an `impl` block annoated with
+ /// This attribute is attached to a function inside an `impl` block annotated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given
/// work item.
///
/// It takes an integer literal as input, which is then used to define the index. This
/// index should be unique for each function in the `impl` block.
pub use frame_support_procedural::task_index;
- /// This attribute is attached to a function inside an `impl` block annoated with
+ /// This attribute is attached to a function inside an `impl` block annotated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define an iterator over the
/// available work items for a task.
///
/// It takes an iterator as input that yields a tuple with same types as the function
/// arguments.
pub use frame_support_procedural::task_list;
- /// This attribute is attached to a function inside an `impl` block annoated with
+ /// This attribute is attached to a function inside an `impl` block annotated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work
/// item.
///
@@ -2773,6 +2772,61 @@ pub mod pallet_macros {
/// Now, this can be executed as follows:
#[doc = docify::embed!("src/tests/tasks.rs", tasks_work)]
pub use frame_support_procedural::tasks_experimental;
+
+ /// Allows a pallet to declare a type as an origin.
+ ///
+ /// If defined as such, this type will be amalgamated at the runtime level into
+ /// `RuntimeOrigin`, very similar to [`call`], [`error`] and [`event`]. See
+ /// [`composite_enum`] for similar cases.
+ ///
+ /// Origin is a complex FRAME topics and is further explained in `polkadot_sdk_docs`.
+ ///
+ /// ## Syntax Variants
+ ///
+ /// ```
+ /// #[frame_support::pallet]
+ /// mod pallet {
+ /// # use frame_support::pallet_prelude::*;
+ /// # #[pallet::config]
+ /// # pub trait Config: frame_system::Config {}
+ /// # #[pallet::pallet]
+ /// # pub struct Pallet(_);
+ /// /// On the spot declaration.
+ /// #[pallet::origin]
+ /// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
+ /// pub enum Origin {
+ /// Foo,
+ /// Bar,
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Or, more commonly used:/
+ ///
+ /// ```
+ /// #[frame_support::pallet]
+ /// mod pallet {
+ /// # use frame_support::pallet_prelude::*;
+ /// # #[pallet::config]
+ /// # pub trait Config: frame_system::Config {}
+ /// # #[pallet::pallet]
+ /// # pub struct Pallet(_);
+ /// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
+ /// pub enum RawOrigin {
+ /// Foo,
+ /// Bar,
+ /// }
+ ///
+ /// #[pallet::origin]
+ /// pub type Origin = RawOrigin;
+ /// }
+ /// ```
+ ///
+ /// ## Warning
+ ///
+ /// Modifying any pallet's origin type will cause the runtime level origin type to also
+ /// change in encoding. If stored anywhere on-chain, this will require a data migration.
+ pub use frame_support_procedural::origin;
}
#[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")]
diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs
index 1405c7303f870..aa6841c008a9f 100644
--- a/substrate/frame/system/src/lib.rs
+++ b/substrate/frame/system/src/lib.rs
@@ -452,6 +452,7 @@ pub mod pallet {
+ Clone
+ OriginTrait;
+ #[docify::export(system_runtime_call)]
/// The aggregated `RuntimeCall` type.
#[pallet::no_default_bounds]
type RuntimeCall: Parameter