diff --git a/Cargo.lock b/Cargo.lock
index ded2cc53293342930d0d4b7271ce57e0e2ed061b..642fe88db006f10be2baf6b3b6fd222a6da95fef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8409,7 +8409,7 @@ dependencies = [
  "pallet-minimal-template",
  "polkadot-sdk-docs",
  "polkadot-sdk-frame",
- "simple-mermaid",
+ "simple-mermaid 0.1.1",
 ]
 
 [[package]]
@@ -13900,7 +13900,7 @@ dependencies = [
  "sc-rpc-api",
  "sc-service",
  "scale-info",
- "simple-mermaid",
+ "simple-mermaid 0.1.1",
  "sp-api",
  "sp-arithmetic",
  "sp-core",
@@ -18353,6 +18353,11 @@ dependencies = [
  "bitflags 2.4.0",
 ]
 
+[[package]]
+name = "simple-mermaid"
+version = "0.1.0"
+source = "git+https://github.com/kianenigma/simple-mermaid.git?branch=main#e48b187bcfd5cc75111acd9d241f1bd36604344b"
+
 [[package]]
 name = "simple-mermaid"
 version = "0.1.1"
@@ -19645,7 +19650,7 @@ dependencies = [
  "scale-info",
  "serde",
  "serde_json",
- "simple-mermaid",
+ "simple-mermaid 0.1.1",
  "sp-api",
  "sp-application-crypto",
  "sp-arithmetic",
@@ -21068,6 +21073,28 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
 
+[[package]]
+name = "test-log"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93"
+dependencies = [
+ "env_logger 0.11.3",
+ "test-log-macros",
+ "tracing-subscriber 0.3.18",
+]
+
+[[package]]
+name = "test-log-macros"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5"
+dependencies = [
+ "proc-macro2 1.0.82",
+ "quote 1.0.35",
+ "syn 2.0.61",
+]
+
 [[package]]
 name = "test-parachain-adder"
 version = "1.0.0"
@@ -23281,6 +23308,31 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "xcm-docs"
+version = "0.1.0"
+dependencies = [
+ "docify",
+ "pallet-balances",
+ "pallet-message-queue",
+ "pallet-xcm",
+ "parity-scale-codec",
+ "polkadot-parachain-primitives",
+ "polkadot-primitives",
+ "polkadot-runtime-parachains",
+ "polkadot-sdk-frame",
+ "scale-info",
+ "simple-mermaid 0.1.0",
+ "sp-io",
+ "sp-runtime",
+ "sp-std 14.0.0",
+ "staging-xcm",
+ "staging-xcm-builder",
+ "staging-xcm-executor",
+ "test-log",
+ "xcm-simulator",
+]
+
 [[package]]
 name = "xcm-emulator"
 version = "0.5.0"
@@ -23379,12 +23431,16 @@ name = "xcm-simulator"
 version = "7.0.0"
 dependencies = [
  "frame-support",
+ "frame-system",
  "parity-scale-codec",
  "paste",
  "polkadot-core-primitives",
  "polkadot-parachain-primitives",
+ "polkadot-primitives",
  "polkadot-runtime-parachains",
+ "scale-info",
  "sp-io",
+ "sp-runtime",
  "sp-std 14.0.0",
  "staging-xcm",
  "staging-xcm-builder",
diff --git a/Cargo.toml b/Cargo.toml
index dcf410daa1f00b7c858724bb6b2dc2c9776ed4c9..1d02b701d2316ae09e84e3813304e43fee034ec5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -218,6 +218,7 @@ members = [
 	"polkadot/utils/generate-bags",
 	"polkadot/utils/remote-ext-tests/bags-list",
 	"polkadot/xcm",
+	"polkadot/xcm/docs",
 	"polkadot/xcm/pallet-xcm",
 	"polkadot/xcm/pallet-xcm-benchmarks",
 	"polkadot/xcm/procedural",
diff --git a/polkadot/xcm/docs/Cargo.toml b/polkadot/xcm/docs/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..9820bd36dc0b1fb84fb6c4e43e4b1608308432a2
--- /dev/null
+++ b/polkadot/xcm/docs/Cargo.toml
@@ -0,0 +1,39 @@
+[package]
+name = "xcm-docs"
+description = "Documentation and guides for XCM"
+version = "0.1.0"
+license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
+repository.workspace = true
+authors.workspace = true
+edition.workspace = true
+publish = false
+
+[dependencies]
+# For XCM stuff
+xcm = { path = "../../xcm", package = "staging-xcm" }
+xcm-executor = { path = "../../xcm/xcm-executor", package = "staging-xcm-executor" }
+xcm-builder = { path = "../../xcm/xcm-builder", package = "staging-xcm-builder" }
+xcm-simulator = { path = "../../xcm/xcm-simulator" }
+pallet-xcm = { path = "../../xcm/pallet-xcm" }
+
+# For building FRAME runtimes
+frame = { package = "polkadot-sdk-frame", path = "../../../substrate/frame", features = ["experimental", "runtime"] }
+codec = { package = "parity-scale-codec", version = "3.6.9" }
+scale-info = { version = "2.6.0", default-features = false }
+polkadot-parachain-primitives = { path = "../../../polkadot/parachain" }
+polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains" }
+polkadot-primitives = { path = "../../../polkadot/primitives" }
+sp-runtime = { path = "../../../substrate/primitives/runtime" }
+sp-std = { path = "../../../substrate/primitives/std" }
+sp-io = { path = "../../../substrate/primitives/io" }
+
+# Some pallets
+pallet-message-queue = { path = "../../../substrate/frame/message-queue" }
+pallet-balances = { path = "../../../substrate/frame/balances" }
+
+# For building docs
+simple-mermaid = { git = "https://github.com/kianenigma/simple-mermaid.git", branch = "main" }
+docify = "0.2.6"
+
+[dev-dependencies]
+test-log = "0.2.14"
diff --git a/polkadot/xcm/docs/mermaid/location_hierarchy.mmd b/polkadot/xcm/docs/mermaid/location_hierarchy.mmd
new file mode 100644
index 0000000000000000000000000000000000000000..54fcfc8072a9aa08032da54f3b4332ef7db5d7c1
--- /dev/null
+++ b/polkadot/xcm/docs/mermaid/location_hierarchy.mmd
@@ -0,0 +1,9 @@
+flowchart
+	relay[Relaychain] --> paraA["Parachain(1000)"]
+	relay --> paraB["Parachain(2000)"]
+
+	paraA --> pallet[Pallet]
+	pallet --> indexA[Index 1]
+	pallet --> indexB[Index 2]
+
+	paraA --> account[Account]
diff --git a/polkadot/xcm/docs/mermaid/structure.mmd b/polkadot/xcm/docs/mermaid/structure.mmd
new file mode 100644
index 0000000000000000000000000000000000000000..17f60467241a351ab3c623b451b8ff4d79df2b9b
--- /dev/null
+++ b/polkadot/xcm/docs/mermaid/structure.mmd
@@ -0,0 +1,4 @@
+flowchart
+	docs[xcm_docs] --> fundamentals
+	docs --> guides
+	docs --> cookbook
diff --git a/polkadot/xcm/docs/mermaid/transport_protocols.mmd b/polkadot/xcm/docs/mermaid/transport_protocols.mmd
new file mode 100644
index 0000000000000000000000000000000000000000..c0340db0651a3f273bbd35dd5fa51afe15a11c24
--- /dev/null
+++ b/polkadot/xcm/docs/mermaid/transport_protocols.mmd
@@ -0,0 +1,6 @@
+flowchart
+	relay[Relaychain] --"DMP"--> paraA["Parachain(2000)"]
+	relay --"DMP"--> paraB["Parachain(2001)"]
+
+	paraA --"UMP"--> relay
+	paraB --"UMP"--> relay
diff --git a/polkadot/xcm/docs/mermaid/universal_location.mmd b/polkadot/xcm/docs/mermaid/universal_location.mmd
new file mode 100644
index 0000000000000000000000000000000000000000..97bfa747319db21dfe07032a3e7fea4d7a54f056
--- /dev/null
+++ b/polkadot/xcm/docs/mermaid/universal_location.mmd
@@ -0,0 +1,3 @@
+flowchart
+	universe[Universal Location] --> polkadot[Polkadot]
+	universe --> ethereum[Ethereum]
diff --git a/polkadot/xcm/docs/mermaid/usdt_location.mmd b/polkadot/xcm/docs/mermaid/usdt_location.mmd
new file mode 100644
index 0000000000000000000000000000000000000000..5e9222f6098ec900ed18b99a16a51c9e2584ee6e
--- /dev/null
+++ b/polkadot/xcm/docs/mermaid/usdt_location.mmd
@@ -0,0 +1,6 @@
+flowchart
+	relay[Polkadot] --> assetHub["Asset Hub"]
+	relay --> anotherPara["Another parachain"]
+
+	assetHub --> assetsPallet["Assets Pallet"]
+	assetsPallet --> usdt[1984]
diff --git a/polkadot/xcm/docs/src/cookbook/mod.rs b/polkadot/xcm/docs/src/cookbook/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1c69bf0ead6f81e4a11010cbf314f9638dda9ccb
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/mod.rs
@@ -0,0 +1,27 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # XCM Cookbook
+//!
+//! A collection of XCM recipes.
+//!
+//! Each recipe is tested and explains all the code necessary to run it -- they're not just snippets
+//! to copy and paste.
+
+/// Configuring a parachain that only uses the Relay Chain native token.
+/// In the case of Polkadot, this recipe will show you how to launch a parachain with no native
+/// token -- dealing only on DOT.
+pub mod relay_token_transactor;
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..279dd71a35f7410b6a6899e5c018c4f55ca18d15
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/mod.rs
@@ -0,0 +1,51 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # Relay Asset Transactor
+//!
+//! This example shows how to configure a parachain to only deal with the Relay Chain token.
+//!
+//! The first step is using the [`xcm_builder::FungibleAdapter`] to create an `AssetTransactor` that
+//! can handle the relay chain token.
+#![doc = docify::embed!("src/cookbook/relay_token_transactor/parachain/xcm_config.rs", asset_transactor)]
+//!
+//! The second step is to configure `IsReserve` to recognize the relay chain as a reserve for its
+//! own asset.
+//! With this, you'll be able to easily mint a derivative asset, backed one-to-one from the Relay
+//! Chain, by using the xcm pallet's `transfer_assets` extrinsic.
+//!
+//! The `IsReserve` type takes a type that implements `ContainsPair<MultiAsset, MultiLocation>`.
+//! In this case, we want a type that contains the pair `(relay_chain_native_token, relay_chain)`.
+#![doc = docify::embed!("src/cookbook/relay_token_transactor/parachain/xcm_config.rs", is_reserve)]
+//!
+//! With this setup, we are able to do a reserve asset transfer to and from the parachain and relay
+//! chain.
+#![doc = docify::embed!("src/cookbook/relay_token_transactor/tests.rs", reserve_asset_transfers_work)]
+//!
+//! For the rest of the code, be sure to check the contents of this module.
+
+/// The parachain runtime for this example
+pub mod parachain;
+
+/// The relay chain runtime for this example.
+pub mod relay_chain;
+
+/// The network for this example.
+pub mod network;
+
+/// Tests for this example.
+#[cfg(test)]
+pub mod tests;
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs
new file mode 100644
index 0000000000000000000000000000000000000000..46ac0e5df6372babf84b3494436e554353ba2820
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/network.rs
@@ -0,0 +1,90 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Mock network
+
+use frame::deps::{
+	frame_system,
+	sp_io::TestExternalities,
+	sp_runtime::{AccountId32, BuildStorage},
+};
+use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt};
+
+use super::{parachain, relay_chain};
+
+pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]);
+pub const BOB: AccountId32 = AccountId32::new([1u8; 32]);
+pub const UNITS: u64 = 10_000_000_000;
+pub const CENTS: u64 = 100_000_000;
+pub const INITIAL_BALANCE: u64 = UNITS;
+
+decl_test_parachain! {
+	pub struct ParaA {
+		Runtime = parachain::Runtime,
+		XcmpMessageHandler = parachain::MessageQueue,
+		DmpMessageHandler = parachain::MessageQueue,
+		new_ext = para_ext(),
+	}
+}
+
+decl_test_relay_chain! {
+	pub struct Relay {
+		Runtime = relay_chain::Runtime,
+		RuntimeCall = relay_chain::RuntimeCall,
+		RuntimeEvent = relay_chain::RuntimeEvent,
+		XcmConfig = relay_chain::XcmConfig,
+		MessageQueue = relay_chain::MessageQueue,
+		System = relay_chain::System,
+		new_ext = relay_ext(),
+	}
+}
+
+decl_test_network! {
+	pub struct MockNet {
+		relay_chain = Relay,
+		parachains = vec![
+			(2222, ParaA),
+		],
+	}
+}
+
+pub fn para_ext() -> TestExternalities {
+	use parachain::{MessageQueue, Runtime, System};
+
+	let t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
+	let mut ext = frame::deps::sp_io::TestExternalities::new(t);
+	ext.execute_with(|| {
+		System::set_block_number(1);
+		MessageQueue::set_para_id(2222.into());
+	});
+	ext
+}
+
+pub fn relay_ext() -> TestExternalities {
+	use relay_chain::{Runtime, System};
+
+	let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
+
+	pallet_balances::GenesisConfig::<Runtime> { balances: vec![(ALICE, INITIAL_BALANCE)] }
+		.assimilate_storage(&mut t)
+		.unwrap();
+
+	let mut ext = TestExternalities::new(t);
+	ext.execute_with(|| {
+		System::set_block_number(1);
+	});
+	ext
+}
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e3fdda2e733376ca3eb780acbc17256c12e6acac
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/mod.rs
@@ -0,0 +1,56 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # Runtime
+
+use frame::{deps::frame_system, prelude::*, runtime::prelude::*, traits::IdentityLookup};
+use xcm_executor::XcmExecutor;
+use xcm_simulator::mock_message_queue;
+
+mod xcm_config;
+use xcm_config::XcmConfig;
+
+pub type Block = frame_system::mocking::MockBlock<Runtime>;
+pub type AccountId = frame::deps::sp_runtime::AccountId32;
+pub type Balance = u64;
+
+construct_runtime! {
+	pub struct Runtime {
+		System: frame_system,
+		MessageQueue: mock_message_queue,
+		Balances: pallet_balances,
+		XcmPallet: pallet_xcm,
+	}
+}
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+impl frame_system::Config for Runtime {
+	type Block = Block;
+	type AccountId = AccountId;
+	type Lookup = IdentityLookup<AccountId>;
+	type AccountData = pallet_balances::AccountData<Balance>;
+}
+
+impl mock_message_queue::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type XcmExecutor = XcmExecutor<XcmConfig>;
+}
+
+#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)]
+impl pallet_balances::Config for Runtime {
+	type Balance = Balance;
+	type AccountStore = System;
+}
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs
new file mode 100644
index 0000000000000000000000000000000000000000..99f17693093e7f0472d78caf54f842847a8a3e84
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs
@@ -0,0 +1,189 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # XCM Configuration
+
+use frame::{
+	deps::frame_system,
+	runtime::prelude::*,
+	traits::{Everything, Nothing},
+};
+use xcm::v4::prelude::*;
+use xcm_builder::{
+	AccountId32Aliases, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin,
+	FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete,
+	SignedToAccountId32,
+};
+use xcm_executor::XcmExecutor;
+
+use super::{AccountId, Balances, MessageQueue, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin};
+
+parameter_types! {
+	pub RelayLocation: Location = Location::parent();
+	pub ThisNetwork: NetworkId = NetworkId::Polkadot;
+}
+
+pub type LocationToAccountId = (
+	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
+	AccountId32Aliases<ThisNetwork, AccountId>,
+);
+
+/// Configuration related to asset transactors
+#[docify::export]
+mod asset_transactor {
+	use super::*;
+
+	parameter_types! {
+		pub ParentRelayLocation: Location = Location::parent();
+	}
+
+	/// AssetTransactor for handling the relay chain token
+	pub type FungibleTransactor = FungibleAdapter<
+		// Use this implementation of the `fungible::*` traits.
+		// `Balances` is the name given to the balances pallet in this particular recipe.
+		// Any implementation of the traits would suffice.
+		Balances,
+		// This transactor deals with the native token of the Relay Chain.
+		// This token is referenced by the Location of the Relay Chain relative to this chain
+		// -- Location::parent().
+		IsConcrete<ParentRelayLocation>,
+		// How to convert an XCM Location into a local account id.
+		// This is also something that's configured in the XCM executor.
+		LocationToAccountId,
+		// The type for account ids, only needed because `fungible` is generic over it.
+		AccountId,
+		// Not tracking teleports.
+		// This recipe only uses reserve asset transfers to handle the Relay Chain token.
+		(),
+	>;
+
+	/// Actual configuration item that'll be set in the XCM config.
+	/// A tuple could be used here to have multiple transactors, each (potentially) handling
+	/// different assets.
+	/// In this recipe, we only have one.
+	pub type AssetTransactor = FungibleTransactor;
+}
+
+/// Configuration related to token reserves
+#[docify::export]
+mod is_reserve {
+	use super::*;
+
+	parameter_types! {
+		/// Reserves are specified using a pair `(AssetFilter, Location)`.
+		/// Each pair means that the specified Location is a reserve for all the assets in AssetsFilter.
+		/// Here, we are specifying that the Relay Chain is the reserve location for its native token.
+		pub RelayTokenForRelay: (AssetFilter, Location) =
+		  (Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible }), Parent.into());
+	}
+
+	/// The wrapper type xcm_builder::Case is needed in order to use this in the configuration.
+	pub type IsReserve = xcm_builder::Case<RelayTokenForRelay>;
+}
+
+mod weigher {
+	use super::*;
+	use xcm_builder::FixedWeightBounds;
+
+	parameter_types! {
+		pub const WeightPerInstruction: Weight = Weight::from_parts(1, 1);
+		pub const MaxInstructions: u32 = 100;
+	}
+
+	pub type Weigher = FixedWeightBounds<WeightPerInstruction, RuntimeCall, MaxInstructions>;
+}
+
+parameter_types! {
+	pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Polkadot), Parachain(2222)].into();
+}
+
+pub struct XcmConfig;
+impl xcm_executor::Config for XcmConfig {
+	type RuntimeCall = RuntimeCall;
+	type XcmSender = ();
+	type AssetTransactor = asset_transactor::AssetTransactor;
+	type OriginConverter = ();
+	// The declaration of which Locations are reserves for which Assets.
+	type IsReserve = is_reserve::IsReserve;
+	type IsTeleporter = ();
+	type UniversalLocation = UniversalLocation;
+	// This is not safe, you should use `xcm_builder::AllowTopLevelPaidExecutionFrom<T>` in a
+	// production chain
+	type Barrier = xcm_builder::AllowUnpaidExecutionFrom<Everything>;
+	type Weigher = weigher::Weigher;
+	type Trader = ();
+	type ResponseHandler = ();
+	type AssetTrap = ();
+	type AssetLocker = ();
+	type AssetExchanger = ();
+	type AssetClaims = ();
+	type SubscriptionService = ();
+	type PalletInstancesInfo = ();
+	type FeeManager = ();
+	type MaxAssetsIntoHolding = frame::traits::ConstU32<1>;
+	type MessageExporter = ();
+	type UniversalAliases = Nothing;
+	type CallDispatcher = RuntimeCall;
+	type SafeCallFilter = Everything;
+	type Aliasers = Nothing;
+	type TransactionalProcessor = FrameTransactionalProcessor;
+	type HrmpNewChannelOpenRequestHandler = ();
+	type HrmpChannelAcceptedHandler = ();
+	type HrmpChannelClosingHandler = ();
+	type XcmRecorder = ();
+}
+
+pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>;
+
+impl pallet_xcm::Config for Runtime {
+	// We turn off sending for these tests
+	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, ()>;
+	type XcmRouter = super::super::network::ParachainXcmRouter<MessageQueue>; // Provided by xcm-simulator
+																		  // Anyone can execute XCM programs
+	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
+	// We execute any type of program
+	type XcmExecuteFilter = Everything;
+	// How we execute programs
+	type XcmExecutor = XcmExecutor<XcmConfig>;
+	// We don't allow teleports
+	type XcmTeleportFilter = Nothing;
+	// We allow all reserve transfers
+	type XcmReserveTransferFilter = Everything;
+	// Same weigher executor uses to weigh XCM programs
+	type Weigher = weigher::Weigher;
+	// Same universal location
+	type UniversalLocation = UniversalLocation;
+	// No version discovery needed
+	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0;
+	type AdvertisedXcmVersion = frame::traits::ConstU32<3>;
+	type AdminOrigin = frame_system::EnsureRoot<AccountId>;
+	// No locking
+	type TrustedLockers = ();
+	type MaxLockers = frame::traits::ConstU32<0>;
+	type MaxRemoteLockConsumers = frame::traits::ConstU32<0>;
+	type RemoteLockConsumerIdentifier = ();
+	// How to turn locations into accounts
+	type SovereignAccountOf = LocationToAccountId;
+	// A currency to pay for things and its matcher, we are using the relay token
+	type Currency = Balances;
+	type CurrencyMatcher = IsConcrete<RelayLocation>;
+	// Pallet benchmarks, no need for this recipe
+	type WeightInfo = pallet_xcm::TestWeightInfo;
+	// Runtime types
+	type RuntimeOrigin = RuntimeOrigin;
+	type RuntimeCall = RuntimeCall;
+	type RuntimeEvent = RuntimeEvent;
+}
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..25c35dd4aaa83c12b0176ea528d09f7d84bc87ce
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs
@@ -0,0 +1,103 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Relay chain runtime mock.
+
+use frame::{
+	deps::{frame_support::weights::WeightMeter, sp_runtime::AccountId32},
+	prelude::*,
+	runtime::prelude::*,
+	traits::{IdentityLookup, ProcessMessage, ProcessMessageError},
+};
+use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
+use xcm::v4::prelude::*;
+
+mod xcm_config;
+pub use xcm_config::LocationToAccountId;
+use xcm_config::XcmConfig;
+
+pub type AccountId = AccountId32;
+pub type Balance = u64;
+
+parameter_types! {
+	pub const BlockHashCount: u64 = 250;
+}
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+impl frame_system::Config for Runtime {
+	type AccountId = AccountId;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Block = Block;
+	type AccountData = pallet_balances::AccountData<Balance>;
+}
+
+#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)]
+impl pallet_balances::Config for Runtime {
+	type AccountStore = System;
+}
+
+type Block = frame_system::mocking::MockBlock<Runtime>;
+
+parameter_types! {
+	/// Amount of weight that can be spent per block to service messages.
+	pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000);
+	pub const MessageQueueHeapSize: u32 = 65_536;
+	pub const MessageQueueMaxStale: u32 = 16;
+}
+
+/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet.
+pub struct MessageProcessor;
+impl ProcessMessage for MessageProcessor {
+	type Origin = AggregateMessageOrigin;
+
+	fn process_message(
+		message: &[u8],
+		origin: Self::Origin,
+		meter: &mut WeightMeter,
+		id: &mut [u8; 32],
+	) -> Result<bool, ProcessMessageError> {
+		let para = match origin {
+			AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para,
+		};
+		xcm_builder::ProcessXcmMessage::<
+			Junction,
+			xcm_executor::XcmExecutor<XcmConfig>,
+			RuntimeCall,
+		>::process_message(message, Junction::Parachain(para.into()), meter, id)
+	}
+}
+
+impl pallet_message_queue::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type Size = u32;
+	type HeapSize = MessageQueueHeapSize;
+	type MaxStale = MessageQueueMaxStale;
+	type ServiceWeight = MessageQueueServiceWeight;
+	type MessageProcessor = MessageProcessor;
+	type QueueChangeHandler = ();
+	type QueuePausedQuery = ();
+	type WeightInfo = ();
+	type IdleMaxServiceWeight = MessageQueueServiceWeight;
+}
+
+construct_runtime! {
+	pub struct Runtime {
+		System: frame_system,
+		Balances: pallet_balances,
+		MessageQueue: pallet_message_queue,
+		XcmPallet: pallet_xcm,
+	}
+}
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs
new file mode 100644
index 0000000000000000000000000000000000000000..987bb3f9ab6649bc299edafa97dc1d06166db440
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs
@@ -0,0 +1,163 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Relay chain XCM configuration
+
+use frame::{
+	deps::frame_system,
+	runtime::prelude::*,
+	traits::{Everything, Nothing},
+};
+use xcm::v4::prelude::*;
+use xcm_builder::{
+	AccountId32Aliases, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin,
+	FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete,
+	SignedToAccountId32,
+};
+use xcm_executor::XcmExecutor;
+
+use super::{AccountId, Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin};
+
+parameter_types! {
+	pub HereLocation: Location = Location::here();
+	pub ThisNetwork: NetworkId = NetworkId::Polkadot;
+}
+
+/// Converter from XCM Locations to accounts.
+/// This generates sovereign accounts for Locations and converts
+/// local AccountId32 junctions to local accounts.
+pub type LocationToAccountId = (
+	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
+	AccountId32Aliases<ThisNetwork, AccountId>,
+);
+
+mod asset_transactor {
+	use super::*;
+
+	/// AssetTransactor for handling the Relay Chain token.
+	pub type FungibleTransactor = FungibleAdapter<
+		// Use this `fungible` implementation.
+		Balances,
+		// This transactor handles the native token.
+		IsConcrete<HereLocation>,
+		// How to convert an XCM Location into a local account id.
+		// Whenever assets are handled, the location is turned into an account.
+		// This account is the one where balances are withdrawn/deposited.
+		LocationToAccountId,
+		// The account id type, needed because `fungible` is generic over it.
+		AccountId,
+		// Not tracking teleports.
+		(),
+	>;
+
+	/// All asset transactors, in this case only one
+	pub type AssetTransactor = FungibleTransactor;
+}
+
+mod weigher {
+	use super::*;
+	use xcm_builder::FixedWeightBounds;
+
+	parameter_types! {
+		pub const WeightPerInstruction: Weight = Weight::from_parts(1, 1);
+		pub const MaxInstructions: u32 = 100;
+	}
+
+	pub type Weigher = FixedWeightBounds<WeightPerInstruction, RuntimeCall, MaxInstructions>;
+}
+
+parameter_types! {
+	pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Polkadot)].into();
+}
+
+pub struct XcmConfig;
+impl xcm_executor::Config for XcmConfig {
+	type RuntimeCall = RuntimeCall;
+	type XcmSender = ();
+	type AssetTransactor = asset_transactor::AssetTransactor;
+	type OriginConverter = ();
+	// We don't need to recognize anyone as a reserve
+	type IsReserve = ();
+	type IsTeleporter = ();
+	type UniversalLocation = UniversalLocation;
+	// This is not safe, you should use `xcm_builder::AllowTopLevelPaidExecutionFrom<T>` in a
+	// production chain
+	type Barrier = xcm_builder::AllowUnpaidExecutionFrom<Everything>;
+	type Weigher = weigher::Weigher;
+	type Trader = ();
+	type ResponseHandler = ();
+	type AssetTrap = ();
+	type AssetLocker = ();
+	type AssetExchanger = ();
+	type AssetClaims = ();
+	type SubscriptionService = ();
+	type PalletInstancesInfo = ();
+	type FeeManager = ();
+	type MaxAssetsIntoHolding = frame::traits::ConstU32<1>;
+	type MessageExporter = ();
+	type UniversalAliases = Nothing;
+	type CallDispatcher = RuntimeCall;
+	type SafeCallFilter = Everything;
+	type Aliasers = Nothing;
+	type TransactionalProcessor = FrameTransactionalProcessor;
+	type HrmpNewChannelOpenRequestHandler = ();
+	type HrmpChannelAcceptedHandler = ();
+	type HrmpChannelClosingHandler = ();
+	type XcmRecorder = ();
+}
+
+pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>;
+
+impl pallet_xcm::Config for Runtime {
+	// No one can call `send`
+	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, ()>;
+	type XcmRouter = super::super::network::RelayChainXcmRouter; // Provided by xcm-simulator
+															 // Anyone can execute XCM programs
+	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
+	// We execute any type of program
+	type XcmExecuteFilter = Everything;
+	// How we execute programs
+	type XcmExecutor = XcmExecutor<XcmConfig>;
+	// We don't allow teleports
+	type XcmTeleportFilter = Nothing;
+	// We allow all reserve transfers.
+	// This is so it can act as a reserve for its native token.
+	type XcmReserveTransferFilter = Everything;
+	// Same weigher executor uses to weigh XCM programs
+	type Weigher = weigher::Weigher;
+	// Same universal location
+	type UniversalLocation = UniversalLocation;
+	// No version discovery needed
+	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0;
+	type AdvertisedXcmVersion = frame::traits::ConstU32<3>;
+	type AdminOrigin = frame_system::EnsureRoot<AccountId>;
+	// No locking
+	type TrustedLockers = ();
+	type MaxLockers = frame::traits::ConstU32<0>;
+	type MaxRemoteLockConsumers = frame::traits::ConstU32<0>;
+	type RemoteLockConsumerIdentifier = ();
+	// How to turn locations into accounts
+	type SovereignAccountOf = LocationToAccountId;
+	// A currency to pay for things and its matcher, we are using the relay token
+	type Currency = Balances;
+	type CurrencyMatcher = IsConcrete<HereLocation>;
+	// Pallet benchmarks, no need for this example
+	type WeightInfo = pallet_xcm::TestWeightInfo;
+	// Runtime types
+	type RuntimeOrigin = RuntimeOrigin;
+	type RuntimeCall = RuntimeCall;
+	type RuntimeEvent = RuntimeEvent;
+}
diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..792cf6149e7cb418f5ffa5720f41ae44956ff036
--- /dev/null
+++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs
@@ -0,0 +1,128 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+use frame::testing_prelude::*;
+use test_log::test;
+use xcm::prelude::*;
+use xcm_executor::traits::ConvertLocation;
+use xcm_simulator::TestExt;
+
+use super::{
+	network::{MockNet, ParaA, Relay, ALICE, BOB, CENTS, INITIAL_BALANCE},
+	parachain, relay_chain,
+};
+
+#[docify::export]
+#[test]
+fn reserve_asset_transfers_work() {
+	// Scenario:
+	// ALICE on the relay chain holds some of Relay Chain's native tokens.
+	// She transfers them to BOB's account on the parachain using a reserve transfer.
+	// BOB receives Relay Chain native token derivatives on the parachain,
+	// which are backed one-to-one with the real tokens on the Relay Chain.
+	//
+	// NOTE: We could've used ALICE on both chains because it's a different account,
+	// but using ALICE and BOB makes it clearer.
+
+	// We restart the mock network.
+	MockNet::reset();
+
+	// ALICE starts with INITIAL_BALANCE on the relay chain
+	Relay::execute_with(|| {
+		assert_eq!(relay_chain::Balances::free_balance(&ALICE), INITIAL_BALANCE);
+	});
+
+	// BOB starts with 0 on the parachain
+	ParaA::execute_with(|| {
+		assert_eq!(parachain::Balances::free_balance(&BOB), 0);
+	});
+
+	// ALICE on the Relay Chain sends some Relay Chain native tokens to BOB on the parachain.
+	// The transfer is done with the `transfer_assets` extrinsic in the XCM pallet.
+	// The extrinsic figures out it should do a reserve asset transfer
+	// with the local chain as reserve.
+	Relay::execute_with(|| {
+		// The parachain id is specified in the network.rs file in this recipe.
+		let destination: Location = Parachain(2222).into();
+		let beneficiary: Location =
+			AccountId32 { id: BOB.clone().into(), network: Some(NetworkId::Polkadot) }.into();
+		// We need to use `u128` here for the conversion to work properly.
+		// If we don't specify anything, it will be a `u64`, which the conversion
+		// will turn into a non-fungible token instead of a fungible one.
+		let assets: Assets = (Here, 50u128 * CENTS as u128).into();
+		assert_ok!(relay_chain::XcmPallet::transfer_assets(
+			relay_chain::RuntimeOrigin::signed(ALICE),
+			Box::new(VersionedLocation::V4(destination.clone())),
+			Box::new(VersionedLocation::V4(beneficiary)),
+			Box::new(VersionedAssets::V4(assets)),
+			0,
+			WeightLimit::Unlimited,
+		));
+
+		// ALICE now has less Relay Chain tokens.
+		assert_eq!(relay_chain::Balances::free_balance(&ALICE), INITIAL_BALANCE - 50 * CENTS);
+
+		// The funds of the sovereign account of the parachain increase by 50 cents,
+		// the ones transferred over to BOB.
+		// The funds in this sovereign account represent how many Relay Chain tokens
+		// have been sent to this parachain.
+		// If the parachain wants to send those assets somewhere else they have to go
+		// via the reserve, and this balance is updated accordingly.
+		// This is why the derivatives are backed one-to-one.
+		let parachains_sovereign_account =
+			relay_chain::LocationToAccountId::convert_location(&destination).unwrap();
+		assert_eq!(relay_chain::Balances::free_balance(parachains_sovereign_account), 50 * CENTS);
+	});
+
+	ParaA::execute_with(|| {
+		// On the parachain, BOB has received the derivative tokens
+		assert_eq!(parachain::Balances::free_balance(&BOB), 50 * CENTS);
+
+		// BOB gives back half to ALICE in the relay chain
+		let destination: Location = Parent.into();
+		let beneficiary: Location =
+			AccountId32 { id: ALICE.clone().into(), network: Some(NetworkId::Polkadot) }.into();
+		// We specify `Parent` because we are referencing the Relay Chain token.
+		// This chain doesn't have a token of its own, so we always refer to this token,
+		// and we do so by the Location of the Relay Chain.
+		let assets: Assets = (Parent, 25u128 * CENTS as u128).into();
+		assert_ok!(parachain::XcmPallet::transfer_assets(
+			parachain::RuntimeOrigin::signed(BOB),
+			Box::new(VersionedLocation::V4(destination)),
+			Box::new(VersionedLocation::V4(beneficiary)),
+			Box::new(VersionedAssets::V4(assets)),
+			0,
+			WeightLimit::Unlimited,
+		));
+
+		// BOB's balance decreased
+		assert_eq!(parachain::Balances::free_balance(&BOB), 25 * CENTS);
+	});
+
+	Relay::execute_with(|| {
+		// ALICE's balance increases
+		assert_eq!(
+			relay_chain::Balances::free_balance(&ALICE),
+			INITIAL_BALANCE - 50 * CENTS + 25 * CENTS
+		);
+
+		// The funds in the parachain's sovereign account decrease.
+		let parachain: Location = Parachain(2222).into();
+		let parachains_sovereign_account =
+			relay_chain::LocationToAccountId::convert_location(&parachain).unwrap();
+		assert_eq!(relay_chain::Balances::free_balance(parachains_sovereign_account), 25 * CENTS);
+	});
+}
diff --git a/polkadot/xcm/docs/src/fundamentals.rs b/polkadot/xcm/docs/src/fundamentals.rs
new file mode 100644
index 0000000000000000000000000000000000000000..28899df801aa4f533cdfedaea8815804dc8bd7c2
--- /dev/null
+++ b/polkadot/xcm/docs/src/fundamentals.rs
@@ -0,0 +1,177 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # XCM Fundamentals
+//!
+//! XCM standardizes usual actions users take in consensus systems, for example
+//! dealing with assets locally, on other chains, and locking them.
+//! XCM programs can both be executed locally or sent to a different consensus system.
+//! Examples of consensus systems are blockchains and smart contracts.
+//!
+//! The goal of XCM is to allow multi-chain ecosystems to thrive via specialization.
+//! Very specific functionalities can be abstracted away and standardized in this common language.
+//! Then, every member of the ecosystem can implement the subset of the language that makes sense
+//! for them.
+//!
+//! The language evolves over time to accomodate the needs of the community
+//! via the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md).
+//!
+//! XCM is the language, it deals with interpreting and executing programs.
+//! It does not deal with actually **sending** these programs from one consensus system to another.
+//! This responsibility falls to a transport protocol.
+//! XCM can even be interpreted on the local system, with no need of a transport protocol.
+//! However, automatic and composable workflows can be achieved via the use of one.
+//!
+//! At the core of XCM lies the XCVM, the Cross-Consensus Virtual Machine.
+//! It's the virtual machine that executes XCM programs.
+//! It is a specification that comes with the language.
+//!
+//! For these docs, we'll use a Rust implementation of XCM and the XCVM, consisting of the following
+//! parts:
+//! - [`XCM`](xcm): Holds the definition of an XCM program, the instructions and main concepts.
+//! - [`Executor`](xcm_executor): Implements the XCVM, capable of executing XCMs. Highly
+//!   configurable.
+//! - [`Builder`](xcm_builder): A collection of types used to configure the executor.
+//! - [`XCM Pallet`](pallet_xcm): A FRAME pallet for interacting with the executor.
+//! - [`Simulator`](xcm_simulator): A playground to tinker with different XCM programs and executor
+//!   configurations.
+//!
+//! XCM programs are composed of Instructions, which reference Locations and Assets.
+//!
+//! ## Locations
+//!
+//! Locations are XCM's vocabulary of places we want to talk about in our XCM programs.
+//! They are used to reference things like 32-byte accounts, governance bodies, smart contracts,
+//! blockchains and more.
+//!
+//! Locations are hierarchical.
+//! This means some places in consensus are wholly encapsulated in other places.
+//! Say we have two systems A and B.
+//! If any change in A's state implies a change in B's state, then we say A is interior to B.
+#![doc = simple_mermaid::mermaid!("../mermaid/location_hierarchy.mmd")]
+//!
+//! Parachains are interior to their Relay Chain, since a change in their state implies a change in
+//! the Relay Chain's state.
+//!
+//! Because of this hierarchy, the way we represent locations is with both a number of **parents**,
+//! times we move __up__ the hierarchy, and a sequence of **junctions**, the steps we take __down__
+//! the hierarchy after going up the specified number of parents.
+//!
+//! In Rust, this is specified with the following datatype:
+//! ```ignore
+//! pub struct Location {
+//!   parents: u8,
+//!   interior: Junctions,
+//! }
+//! ```
+//!
+//! Many junctions are available; parachains, pallets, 32 and 20 byte accounts, governance bodies,
+//! and arbitrary indices are the most common.
+//! A full list of available junctions can be found in the [format](https://github.com/paritytech/xcm-format#interior-locations--junctions)
+//! and [Junction enum](xcm::v4::prelude::Junction).
+//!
+//! We'll use a file system notation to represent locations, and start with relative locations.
+//! In the diagram, the location of parachain 1000 as seen from all other locations is as follows:
+//! - From the relaychain: `Parachain(1000)`
+//! - From parachain 1000 itself: `Here`
+//! - From parachain 2000: `../Parachain(1000)`
+//!
+//! Relative locations are interpreted by the system that is executing an XCM program, which is the
+//! receiver of a message in the case where it's sent.
+//!
+//! Locations can also be absolute.
+//! Keeping in line with our filesystem analogy, we can imagine the root of our filesystem to exist.
+//! This would be a location with no parents, that is also the parent of all systems that derive
+//! their own consensus, say Polkadot or Ethereum or Bitcoin.
+//! Such a location does not exist concretely, but we can still use this definition for it.
+//! This is the **universal location**.
+//! We need the universal location to be able to describe locations in an absolute way.
+#![doc = simple_mermaid::mermaid!("../mermaid/universal_location.mmd")]
+//!
+//! Here, the absolute location of parachain 1000 would be
+//! `GlobalConsensus(Polkadot)/Parachain(1000)`.
+//!
+//! ## Assets
+//!
+//! We want to be able to reference assets in our XCM programs, if only to be able to pay for fees.
+//! Assets are represented using locations.
+//!
+//! The native asset of a chain is represented by the location of that chain.
+//! For example, DOT is represented by the location of the Polkadot relaychain.
+//! If the interpreting chain has its own asset, it would be represented by `Here`.
+//!
+//! How do we represent other assets?
+//! The asset hub system parachain in Polkadot, for example, holds a lot of assets.
+//! To represent each of them, it uses the indices we mentioned, and it makes them interior to the
+//! assets pallet instance it uses.
+//! USDT, an example asset that lives on asset hub, is identified by the location
+//! `Parachain(1000)/PalletInstance(53)/GeneralIndex(1984)`, when seen from the Polkadot relaychain.
+#![doc = simple_mermaid::mermaid!("../mermaid/usdt_location.mmd")]
+//!
+//! Asset Hub also has another type of assets called `ForeignAssets`.
+//! These assets are identified by the XCM Location to their origin.
+//! Two such assets are a Parachain asset, like Moonbeam's GLMR, and KSM, from the cousin Kusama
+//! network. These are represented as `../Parachain(2004)/PalletInstance(10)` and
+//! `../../GlobalConsensus(Kusama)` respectively.
+//!
+//! The whole type can be seen in the [format](https://github.com/paritytech/xcm-format#6-universal-asset-identifiers)
+//! and [rust docs](xcm::v4::prelude::Asset).
+//!
+//! ## Instructions
+//!
+//! Given the vocabulary to talk about both locations -- chains and accounts -- and assets, we now
+//! need a way to express what we want the consensus system to do when executing our programs.
+//! We need a way of writing our programs.
+//!
+//! XCM programs are composed of a sequence of instructions.
+//!
+//! All available instructions can be seen in the [format](https://github.com/paritytech/xcm-format#5-the-xcvm-instruction-set)
+//! and the [Instruction enum](xcm::v4::prelude::Instruction).
+//!
+//! A very simple example is the following:
+//!
+//! ```ignore
+//! let message = Xcm(vec![
+//!   TransferAsset { assets, beneficiary },
+//! ]);
+//! ```
+//!
+//! This instruction is enough to transfer `assets` from the account of the **origin** of a message
+//! to the `beneficiary` account. However, because of XCM's generality, fees need to be paid
+//! explicitly. This next example sheds more light on this:
+//!
+//! ```ignore
+//! let message = Xcm(vec![
+//!   WithdrawAsset(assets),
+//!   BuyExecution { fees: assets, weight_limit },
+//!   DepositAsset { assets: AssetFilter(Wild(All)), beneficiary },
+//! ]);
+//! ```
+//!
+//! Here we see the process of transferring assets was broken down into smaller instructions, and we
+//! add the explicit fee payment step in the middle.
+//! `WithdrawAsset` withdraws assets from the account of the **origin** of the message for usage
+//! inside this message's execution. `BuyExecution` explicitly buys execution for this program using
+//! the assets specified in `fees`, with a sanity check of `weight_limit`. `DepositAsset` uses a
+//! wildcard, specifying all remaining `assets` after subtracting the fees and a `beneficiary`
+//! account.
+//!
+//! ## Next steps
+//!
+//! Continue with the [guides](crate::guides) for step-by-step tutorials on XCM,
+//! or jump to the [cookbook](crate::cookbook) to see examples.
+//!
+//! The [glossary](crate::glossary) can be useful if some of the terms are confusing.
diff --git a/polkadot/xcm/docs/src/glossary.rs b/polkadot/xcm/docs/src/glossary.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6035888ab733b2751a4f24d51336132111d6ef57
--- /dev/null
+++ b/polkadot/xcm/docs/src/glossary.rs
@@ -0,0 +1,123 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # Glossary
+//!
+//! ## XCM (Cross-Consensus Messaging)
+//!
+//! A messaging format meant to communicate intentions between consensus systems.
+//! XCM could also refer to a single message.
+//!
+//! ## Instructions
+//!
+//! XCMs are composed of a sequence of instructions.
+//! Each instruction aims to convey a particular intention.
+//! There are instructions for transferring and locking assets, handling fees, calling arbitrary
+//! blobs, and more.
+//!
+//! ## Consensus system
+//!
+//! A system that can reach any kind of consensus.
+//! For example, relay chains, parachains, smart contracts.
+//! Most messaging between consensus systems has to be done asynchronously, for this, XCM is used.
+//! Between two smart contracts on the same parachain, however, communication can be done
+//! synchronously.
+//!
+//! ## [`Location`](xcm::v4::prelude::Location)
+//!
+//! A way of addressing consensus systems.
+//! These could be relative or absolute.
+//!
+//! ## [`Junction`](xcm::v4::prelude::Junction)
+//!
+//! The different ways of descending down a [`Location`](xcm::v4::prelude::Location) hierarchy.
+//! A junction can be a Parachain, an Account, or more.
+//!
+//! ## [`Asset`](xcm::v4::prelude::Asset)
+//!
+//! A way of identifying assets in the same or another consensus system, by using a
+//! [`Location`](xcm::v4::prelude::Location).
+//!
+//! ## Sovereign account
+//!
+//! An account in a consensus system that is controlled by an account in another consensus system.
+//!
+//! Runtimes use a converter between a [`Location`](xcm::v4::prelude::Location) and an account.
+//! These converters implement the [`ConvertLocation`](xcm_executor::traits::ConvertLocation) trait.
+//!
+//! ## Teleport
+//!
+//! A way of transferring assets between two consensus systems without the need of a third party.
+//! It consists of the sender system burning the asset that wants to be sent over and the recipient
+//! minting an equivalent amount of that asset. It requires a lot of trust between the two systems,
+//! since failure to mint or burn will reduce or increase the total issuance of the token.
+//!
+//! ## Reserve asset transfer
+//!
+//! A way of transferring assets between two consensus systems that don't trust each other, by using
+//! a third system they both trust, called the reserve. The real asset only exists on the reserve,
+//! both sender and recipient only deal with derivatives. It consists of the sender burning a
+//! certain amount of derivatives, telling the reserve to move real assets from its sovereign
+//! account to the destination's sovereign account, and then telling the recipient to mint the right
+//! amount of derivatives.
+//! In practice, the reserve chain can also be one of the source or destination.
+//!
+//! ## XCVM
+//!
+//! The virtual machine behind XCM.
+//! Every XCM is an XCVM programme.
+//! Holds state in registers.
+//!
+//! An implementation of the virtual machine is the [`xcm-executor`](xcm_executor::XcmExecutor).
+//!
+//! ## Holding register
+//!
+//! An XCVM register used to hold arbitrary `Asset`s during the execution of an XCVM programme.
+//!
+//! ## Barrier
+//!
+//! An XCM executor configuration item that works as a firewall for incoming XCMs.
+//! All XCMs have to pass the barrier to be executed, else they are dropped.
+//! It can be used for whitelisting only certain types or messages or messages from certain senders.
+//!
+//! Lots of barrier definitions exist in [`xcm-builder`](xcm_builder).
+//!
+//! ## VMP (Vertical Message Passing)
+//!
+//! Umbrella term for both UMP (Upward Message Passing) and DMP (Downward Message Passing).
+//!
+//! The following diagram shows the uses of both protocols:
+#![doc = simple_mermaid::mermaid!("../mermaid/transport_protocols.mmd")]
+//!
+//! ## UMP (Upward Message Passing)
+//!
+//! Transport-layer protocol that allows parachains to send messages upwards to their relay chain.
+//!
+//! ## DMP (Downward Message Passing)
+//!
+//! Transport-layer protocol that allows the relay chain to send messages downwards to one of their
+//! parachains.
+//!
+//! ## XCMP (Cross-Consensus Message Passing)
+//!
+//! Transport-layer protocol that allows parachains to send messages between themselves, without
+//! going through the relay chain.
+//!
+//! ## HRMP (Horizontal Message Passing)
+//!
+//! Transport-layer protocol that allows a parachain to send messages to a sibling parachain going
+//! through the relay chain. It's a precursor to XCMP, also known as XCMP-lite.
+//! It uses a mixture of UMP and DMP.
diff --git a/polkadot/xcm/docs/src/guides/mod.rs b/polkadot/xcm/docs/src/guides/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5af89428d9a4c570831c17f48b5974e5ded5fd58
--- /dev/null
+++ b/polkadot/xcm/docs/src/guides/mod.rs
@@ -0,0 +1,25 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # XCM Guides
+//!
+//! These guides aim to get you up and running with XCM.
+//!
+//! Coming soon.
+//!
+//! ## Next steps
+//!
+//! Jump to the [cookbook](crate::cookbook) for different examples.
diff --git a/polkadot/xcm/docs/src/lib.rs b/polkadot/xcm/docs/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..287c97140c91b371765257475b0d7a3ade2e2f06
--- /dev/null
+++ b/polkadot/xcm/docs/src/lib.rs
@@ -0,0 +1,63 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! # XCM Docs
+//!
+//! Documentation and guides for XCM
+//!
+//! Welcome to the Cross-Consensus Messaging documentation!
+//!
+//! XCM is a **language** for communicating **intentions** between **consensus systems**.
+//! Whether you're a developer, a blockchain enthusiast, or just interested in Polkadot, this guide
+//! aims to provide you with an easy-to-understand and comprehensive introduction to XCM.
+//!
+//! ## Getting started
+//!
+//! Head over to the [fundamentals](fundamentals) section.
+//! Then, go to the [guides](guides), to learn about how to do things with XCM.
+//!
+//! ## Cookbook
+//!
+//! There's also the [cookbook](cookbook) for useful recipes for XCM.
+//!
+//! ## Glossary
+//!
+//! There's a [glossary](glossary) with common terms used throughout the docs.
+//!
+//! ## Contribute
+//!
+//! To contribute to the format, check out the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md).
+//! To contribute to these docs, [make a PR](https://github.com/paritytech/polkadot-sdk).
+//!
+//! ## Why Rust Docs?
+//!
+//! Rust Docs allow docs to be as close to the source as possible.
+//! They're also available offline automatically for anyone who has the `polkadot-sdk` repo locally.
+//!
+//! ## Docs structure
+#![doc = simple_mermaid::mermaid!("../mermaid/structure.mmd")]
+
+/// Fundamentals of the XCM language. The virtual machine, instructions, locations and assets.
+pub mod fundamentals;
+
+/// Step-by-step guides to set up an XCM environment and start hacking.
+pub mod guides;
+
+/// Useful recipes for programs and configurations.
+pub mod cookbook;
+
+/// Glossary
+pub mod glossary;
diff --git a/polkadot/xcm/xcm-simulator/Cargo.toml b/polkadot/xcm/xcm-simulator/Cargo.toml
index 9324359d365dc22238c880242614ff5ad27ff259..fc09b5e31861c0cb6470e7d7c121e8c94e1f60f8 100644
--- a/polkadot/xcm/xcm-simulator/Cargo.toml
+++ b/polkadot/xcm/xcm-simulator/Cargo.toml
@@ -11,15 +11,19 @@ workspace = true
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.6.12" }
+scale-info = { version = "2.6.0", default-features = false }
 paste = "1.0.7"
 
 frame-support = { path = "../../../substrate/frame/support" }
+frame-system = { path = "../../../substrate/frame/system" }
 sp-io = { path = "../../../substrate/primitives/io" }
 sp-std = { path = "../../../substrate/primitives/std" }
+sp-runtime = { path = "../../../substrate/primitives/runtime" }
 
 xcm = { package = "staging-xcm", path = ".." }
 xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor" }
 xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder" }
+polkadot-primitives = { path = "../../primitives" }
 polkadot-core-primitives = { path = "../../core-primitives" }
 polkadot-parachain-primitives = { path = "../../parachain" }
 polkadot-runtime-parachains = { path = "../../runtime/parachains" }
diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs
index 8021f9551658075fa672c02306ade1bd12298e4d..93c8302757cb061bc1f97d003ce8b7de2374448e 100644
--- a/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs
@@ -16,7 +16,6 @@
 
 //! Parachain runtime mock.
 
-mod mock_msg_queue;
 mod xcm_config;
 pub use xcm_config::*;
 
@@ -36,6 +35,7 @@ use sp_std::prelude::*;
 use xcm::latest::prelude::*;
 use xcm_builder::{EnsureXcmOrigin, SignedToAccountId32};
 use xcm_executor::{traits::ConvertLocation, XcmExecutor};
+use xcm_simulator::mock_message_queue;
 
 pub type AccountId = AccountId32;
 pub type Balance = u128;
@@ -121,7 +121,7 @@ parameter_types! {
 	pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0);
 }
 
-impl mock_msg_queue::Config for Runtime {
+impl mock_message_queue::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type XcmExecutor = XcmExecutor<XcmConfig>;
 }
@@ -175,7 +175,7 @@ construct_runtime!(
 	pub struct Runtime {
 		System: frame_system,
 		Balances: pallet_balances,
-		MsgQueue: mock_msg_queue,
+		MsgQueue: mock_message_queue,
 		PolkadotXcm: pallet_xcm,
 		ForeignUniques: pallet_uniques,
 	}
diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs
index f6d0174def8f40162f8f2c75231f9b2bad7df0fd..0769507ec37b70e5f6d60886c4db7db9a866dc0d 100644
--- a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs
@@ -14,9 +14,10 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-use crate::parachain::MsgQueue;
+use crate::parachain::Runtime;
 use frame_support::parameter_types;
 use xcm::latest::prelude::*;
+use xcm_simulator::mock_message_queue::ParachainId;
 
 parameter_types! {
 	pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1);
@@ -26,5 +27,5 @@ parameter_types! {
 parameter_types! {
 	pub const KsmLocation: Location = Location::parent();
 	pub const RelayNetwork: NetworkId = NetworkId::Kusama;
-	pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into();
+	pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainId::<Runtime>::get().into())].into();
 }
diff --git a/polkadot/xcm/xcm-simulator/example/src/tests.rs b/polkadot/xcm/xcm-simulator/example/src/tests.rs
index 6486a849af363af6b41bb40ff440efa5424e92eb..34c1feb6e946876bb62a5624915e414df65ed35e 100644
--- a/polkadot/xcm/xcm-simulator/example/src/tests.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/tests.rs
@@ -19,7 +19,7 @@ use crate::*;
 use codec::Encode;
 use frame_support::{assert_ok, weights::Weight};
 use xcm::latest::QueryResponseInfo;
-use xcm_simulator::TestExt;
+use xcm_simulator::{mock_message_queue::ReceivedDmp, TestExt};
 
 // Helper function for forming buy execution message
 fn buy_execution<C>(fees: impl Into<Asset>) -> Instruction<C> {
@@ -171,7 +171,7 @@ fn remote_locking_and_unlocking() {
 
 	ParaA::execute_with(|| {
 		assert_eq!(
-			parachain::MsgQueue::received_dmp(),
+			ReceivedDmp::<parachain::Runtime>::get(),
 			vec![Xcm(vec![NoteUnlockable {
 				owner: (Parent, Parachain(2)).into(),
 				asset: (Parent, locked_amount).into()
@@ -501,7 +501,7 @@ fn query_holding() {
 	// Check that QueryResponse message was received
 	ParaA::execute_with(|| {
 		assert_eq!(
-			parachain::MsgQueue::received_dmp(),
+			ReceivedDmp::<parachain::Runtime>::get(),
 			vec![Xcm(vec![QueryResponse {
 				query_id: query_id_set,
 				response: Response::Assets(Assets::new()),
diff --git a/polkadot/xcm/xcm-simulator/src/lib.rs b/polkadot/xcm/xcm-simulator/src/lib.rs
index 7efbc658bbfb8bc3fadfa037ae966ca935816ba6..a6747a4789edf8065462b831893ebc8b0284dd18 100644
--- a/polkadot/xcm/xcm-simulator/src/lib.rs
+++ b/polkadot/xcm/xcm-simulator/src/lib.rs
@@ -16,6 +16,10 @@
 
 //! Test kit to simulate cross-chain message passing and XCM execution.
 
+/// Implementation of a simple message queue.
+/// Used for sending messages.
+pub mod mock_message_queue;
+
 pub use codec::Encode;
 pub use paste;
 
diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs b/polkadot/xcm/xcm-simulator/src/mock_message_queue.rs
similarity index 72%
rename from polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs
rename to polkadot/xcm/xcm-simulator/src/mock_message_queue.rs
index 17cde921f3e20cbfe4da23c68009e30afbd6fca6..96b47999fe952145b09faa93aaf2cefd5143acb9 100644
--- a/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs
+++ b/polkadot/xcm/xcm-simulator/src/mock_message_queue.rs
@@ -1,4 +1,4 @@
-// Copyright (C) Parity Technologies (UK) Ltd.
+// Copyright Parity Technologies (UK) Ltd.
 // This file is part of Polkadot.
 
 // Polkadot is free software: you can redistribute it and/or modify
@@ -14,14 +14,21 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-pub use pallet::*;
-use polkadot_core_primitives::BlockNumber as RelayBlockNumber;
+//! Simple mock message queue.
+
+use codec::{Decode, Encode};
+
 use polkadot_parachain_primitives::primitives::{
 	DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler,
 };
+use polkadot_primitives::BlockNumber as RelayBlockNumber;
 use sp_runtime::traits::{Get, Hash};
+
+use sp_std::prelude::*;
 use xcm::{latest::prelude::*, VersionedXcm};
 
+pub use pallet::*;
+
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
@@ -41,15 +48,15 @@ pub mod pallet {
 	pub struct Pallet<T>(_);
 
 	#[pallet::storage]
-	pub(super) type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
+	pub type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
 
 	#[pallet::storage]
 	/// A queue of received DMP messages
-	pub(super) type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
+	pub type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
 
 	impl<T: Config> Get<ParaId> for Pallet<T> {
 		fn get() -> ParaId {
-			Self::parachain_id()
+			ParachainId::<T>::get()
 		}
 	}
 
@@ -60,45 +67,34 @@ pub mod pallet {
 	pub enum Event<T: Config> {
 		// XCMP
 		/// Some XCM was executed OK.
-		Success(Option<T::Hash>),
+		Success { message_id: Option<T::Hash> },
 		/// Some XCM failed.
-		Fail(Option<T::Hash>, XcmError),
+		Fail { message_id: Option<T::Hash>, error: XcmError },
 		/// Bad XCM version used.
-		BadVersion(Option<T::Hash>),
+		BadVersion { message_id: Option<T::Hash> },
 		/// Bad XCM format used.
-		BadFormat(Option<T::Hash>),
+		BadFormat { message_id: Option<T::Hash> },
 
 		// DMP
 		/// Downward message is invalid XCM.
-		InvalidFormat(MessageId),
+		InvalidFormat { message_id: MessageId },
 		/// Downward message is unsupported version of XCM.
-		UnsupportedVersion(MessageId),
+		UnsupportedVersion { message_id: MessageId },
 		/// Downward message executed with the given outcome.
-		ExecutedDownward(MessageId, Outcome),
+		ExecutedDownward { message_id: MessageId, outcome: Outcome },
 	}
 
 	impl<T: Config> Pallet<T> {
-		/// Get the Parachain Id.
-		pub fn parachain_id() -> ParaId {
-			ParachainId::<T>::get()
-		}
-
-		/// Set the Parachain Id.
 		pub fn set_para_id(para_id: ParaId) {
 			ParachainId::<T>::put(para_id);
 		}
 
-		/// Get the queue of receieved DMP messages.
-		pub fn received_dmp() -> Vec<Xcm<T::RuntimeCall>> {
-			ReceivedDmp::<T>::get()
-		}
-
 		fn handle_xcmp_message(
 			sender: ParaId,
 			_sent_at: RelayBlockNumber,
 			xcm: VersionedXcm<T::RuntimeCall>,
-			max_weight: Weight,
-		) -> Result<Weight, XcmError> {
+			max_weight: xcm::latest::Weight,
+		) -> Result<xcm::latest::Weight, XcmError> {
 			let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
 			let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256);
 			let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) {
@@ -111,15 +107,20 @@ pub mod pallet {
 						max_weight,
 						Weight::zero(),
 					) {
-						Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)),
-						Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))),
+						Outcome::Error { error } =>
+							(Err(error), Event::Fail { message_id: Some(hash), error }),
+						Outcome::Complete { used } =>
+							(Ok(used), Event::Success { message_id: Some(hash) }),
 						// As far as the caller is concerned, this was dispatched without error, so
 						// we just report the weight used.
 						Outcome::Incomplete { used, error } =>
-							(Ok(used), Event::Fail(Some(hash), error)),
+							(Ok(used), Event::Fail { message_id: Some(hash), error }),
 					}
 				},
-				Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))),
+				Err(()) => (
+					Err(XcmError::UnhandledXcmVersion),
+					Event::BadVersion { message_id: Some(hash) },
+				),
 			};
 			Self::deposit_event(event);
 			result
@@ -129,8 +130,8 @@ pub mod pallet {
 	impl<T: Config> XcmpMessageHandler for Pallet<T> {
 		fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>(
 			iter: I,
-			max_weight: Weight,
-		) -> Weight {
+			max_weight: xcm::latest::Weight,
+		) -> xcm::latest::Weight {
 			for (sender, sent_at, data) in iter {
 				let mut data_ref = data;
 				let _ = XcmpMessageFormat::decode(&mut data_ref)
@@ -156,15 +157,16 @@ pub mod pallet {
 			iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
 			limit: Weight,
 		) -> Weight {
-			for (_i, (_sent_at, data)) in iter.enumerate() {
+			for (_sent_at, data) in iter {
 				let mut id = sp_io::hashing::blake2_256(&data[..]);
 				let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]);
 				match maybe_versioned {
 					Err(_) => {
-						Self::deposit_event(Event::InvalidFormat(id));
+						Self::deposit_event(Event::InvalidFormat { message_id: id });
 					},
 					Ok(versioned) => match Xcm::try_from(versioned) {
-						Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)),
+						Err(()) =>
+							Self::deposit_event(Event::UnsupportedVersion { message_id: id }),
 						Ok(x) => {
 							let outcome = T::XcmExecutor::prepare_and_execute(
 								Parent,
@@ -173,8 +175,11 @@ pub mod pallet {
 								limit,
 								Weight::zero(),
 							);
-							<ReceivedDmp<T>>::append(x);
-							Self::deposit_event(Event::ExecutedDownward(id, outcome));
+							ReceivedDmp::<T>::append(x);
+							Self::deposit_event(Event::ExecutedDownward {
+								message_id: id,
+								outcome,
+							});
 						},
 					},
 				}