From b709dccd063507d468db3e10f491bb60cd80ac64 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Tue, 7 May 2024 10:33:32 +0200
Subject: [PATCH] Add support for versioned notification for HRMP pallet
 (#4281)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes: https://github.com/paritytech/polkadot-sdk/issues/4003 (please
see for the problem description)

## TODO
- [x] add more tests covering `WrapVersion` corner cases (e.g. para has
lower version, ...)
- [x] regenerate benchmarks `runtime_parachains::hrmp` (fix for Rococo
is here: https://github.com/paritytech/polkadot-sdk/pull/4332)

## Questions / possible improvements
- [ ] A `WrapVersion` implementation for `pallet_xcm` initiates version
discovery with
[note_unknown_version](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/pallet-xcm/src/lib.rs#L2527C5-L2527C25),
there is possibility to avoid this overhead in this HRMP case to create
new `WrapVersion` adapter for `pallet_xcm` which would not use
`note_unknown_version`. Is it worth to do it or not?
- [ ] There's a possibility to decouple XCM functionality from the HRMP
pallet, allowing any relay chain to generate its own notifications. This
approach wouldn't restrict notifications solely to the XCM. However,
it's uncertain whether it's worthwhile or desirable to do so? It means
making HRMP pallet more generic. E.g. hiding HRMP notifications behind
some trait:
	```
	trait HrmpNotifications {

		fn on_channel_open_request(
			sender: ParaId,
			proposed_max_capacity: u32,
			proposed_max_message_size: u32) -> primitives::DownwardMessage;

fn on_channel_accepted(recipient: ParaId) ->
primitives::DownwardMessage;

fn on_channel_closing(initiator: ParaId, sender: ParaId, recipient:
ParaId) -> primitives::DownwardMessage;
	}
	```
and then we could have whatever adapter, `impl HrmpNotifications for
VersionedXcmHrmpNotifications {...}`,
	```
	impl parachains_hrmp::Config for Runtime {
	..
		type HrmpNotifications = VersionedXcmHrmpNotifications;
	..
	}
	```

---------

Co-authored-by: command-bot <>
Co-authored-by: Bastian Köcher <git@kchr.de>
---
 polkadot/runtime/parachains/src/hrmp.rs       | 158 +++++++++++-------
 polkadot/runtime/parachains/src/hrmp/tests.rs | 143 +++++++++++++++-
 polkadot/runtime/parachains/src/mock.rs       |  40 ++++-
 polkadot/runtime/rococo/src/lib.rs            |   1 +
 .../src/weights/runtime_parachains_hrmp.rs    | 102 ++++++-----
 polkadot/runtime/test-runtime/src/lib.rs      |   2 +-
 polkadot/runtime/westend/src/lib.rs           |   1 +
 .../src/weights/runtime_parachains_hrmp.rs    | 102 ++++++-----
 .../example/src/relay_chain/mod.rs            |   4 -
 .../xcm-simulator/fuzzer/src/relay_chain.rs   |   4 -
 prdoc/pr_4281.prdoc                           |  16 ++
 11 files changed, 406 insertions(+), 167 deletions(-)
 create mode 100644 prdoc/pr_4281.prdoc

diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs
index 65652b38577..42a9c23e5aa 100644
--- a/polkadot/runtime/parachains/src/hrmp.rs
+++ b/polkadot/runtime/parachains/src/hrmp.rs
@@ -278,6 +278,14 @@ pub mod pallet {
 		/// parachain.
 		type DefaultChannelSizeAndCapacityWithSystem: Get<(u32, u32)>;
 
+		/// Means of converting an `Xcm` into a `VersionedXcm`. This pallet sends HRMP XCM
+		/// notifications to the channel-related parachains, while the `WrapVersion` implementation
+		/// attempts to wrap them into the most suitable XCM version for the destination parachain.
+		///
+		/// NOTE: For example, `pallet_xcm` provides an accurate implementation (recommended), or
+		/// the default `()` implementation uses the latest XCM version for all parachains.
+		type VersionWrapper: xcm::WrapVersion;
+
 		/// Something that provides the weight of this pallet.
 		type WeightInfo: WeightInfo;
 	}
@@ -1499,28 +1507,19 @@ impl<T: Config> Pallet<T> {
 		);
 		HrmpOpenChannelRequestsList::<T>::append(channel_id);
 
-		let notification_bytes = {
-			use parity_scale_codec::Encode as _;
-			use xcm::opaque::{latest::prelude::*, VersionedXcm};
-
-			VersionedXcm::from(Xcm(vec![HrmpNewChannelOpenRequest {
-				sender: u32::from(origin),
-				max_capacity: proposed_max_capacity,
-				max_message_size: proposed_max_message_size,
-			}]))
-			.encode()
-		};
-		if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) =
-			dmp::Pallet::<T>::queue_downward_message(&config, recipient, notification_bytes)
-		{
-			// this should never happen unless the max downward message size is configured to a
-			// jokingly small number.
-			log::error!(
-				target: "runtime::hrmp",
-				"sending 'init_open_channel::notification_bytes' failed."
-			);
-			debug_assert!(false);
-		}
+		Self::send_to_para(
+			"init_open_channel",
+			&config,
+			recipient,
+			Self::wrap_notification(|| {
+				use xcm::opaque::latest::{prelude::*, Xcm};
+				Xcm(vec![HrmpNewChannelOpenRequest {
+					sender: origin.into(),
+					max_capacity: proposed_max_capacity,
+					max_message_size: proposed_max_message_size,
+				}])
+			}),
+		);
 
 		Ok(())
 	}
@@ -1562,23 +1561,15 @@ impl<T: Config> Pallet<T> {
 		HrmpOpenChannelRequests::<T>::insert(&channel_id, channel_req);
 		HrmpAcceptedChannelRequestCount::<T>::insert(&origin, accepted_cnt + 1);
 
-		let notification_bytes = {
-			use parity_scale_codec::Encode as _;
-			use xcm::opaque::{latest::prelude::*, VersionedXcm};
-			let xcm = Xcm(vec![HrmpChannelAccepted { recipient: u32::from(origin) }]);
-			VersionedXcm::from(xcm).encode()
-		};
-		if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) =
-			dmp::Pallet::<T>::queue_downward_message(&config, sender, notification_bytes)
-		{
-			// this should never happen unless the max downward message size is configured to an
-			// jokingly small number.
-			log::error!(
-				target: "runtime::hrmp",
-				"sending 'accept_open_channel::notification_bytes' failed."
-			);
-			debug_assert!(false);
-		}
+		Self::send_to_para(
+			"accept_open_channel",
+			&config,
+			sender,
+			Self::wrap_notification(|| {
+				use xcm::opaque::latest::{prelude::*, Xcm};
+				Xcm(vec![HrmpChannelAccepted { recipient: origin.into() }])
+			}),
+		);
 
 		Ok(())
 	}
@@ -1633,30 +1624,22 @@ impl<T: Config> Pallet<T> {
 		HrmpCloseChannelRequestsList::<T>::append(channel_id.clone());
 
 		let config = configuration::ActiveConfig::<T>::get();
-		let notification_bytes = {
-			use parity_scale_codec::Encode as _;
-			use xcm::opaque::{latest::prelude::*, VersionedXcm};
-
-			VersionedXcm::from(Xcm(vec![HrmpChannelClosing {
-				initiator: u32::from(origin),
-				sender: u32::from(channel_id.sender),
-				recipient: u32::from(channel_id.recipient),
-			}]))
-			.encode()
-		};
 		let opposite_party =
 			if origin == channel_id.sender { channel_id.recipient } else { channel_id.sender };
-		if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) =
-			dmp::Pallet::<T>::queue_downward_message(&config, opposite_party, notification_bytes)
-		{
-			// this should never happen unless the max downward message size is configured to an
-			// jokingly small number.
-			log::error!(
-				target: "runtime::hrmp",
-				"sending 'close_channel::notification_bytes' failed."
-			);
-			debug_assert!(false);
-		}
+
+		Self::send_to_para(
+			"close_channel",
+			&config,
+			opposite_party,
+			Self::wrap_notification(|| {
+				use xcm::opaque::latest::{prelude::*, Xcm};
+				Xcm(vec![HrmpChannelClosing {
+					initiator: origin.into(),
+					sender: channel_id.sender.into(),
+					recipient: channel_id.recipient.into(),
+				}])
+			}),
+		);
 
 		Ok(())
 	}
@@ -1875,3 +1858,56 @@ impl<T: Config> Pallet<T> {
 		}
 	}
 }
+
+impl<T: Config> Pallet<T> {
+	/// Wraps HRMP XCM notifications to the most suitable XCM version for the destination para.
+	/// If the XCM version is unknown, the latest XCM version is used as a best effort.
+	fn wrap_notification(
+		mut notification: impl FnMut() -> xcm::opaque::latest::opaque::Xcm,
+	) -> impl FnOnce(ParaId) -> primitives::DownwardMessage {
+		use xcm::{
+			opaque::VersionedXcm,
+			prelude::{Junction, Location},
+			WrapVersion,
+		};
+
+		// Return a closure that can prepare notifications.
+		move |dest| {
+			// Attempt to wrap the notification for the destination parachain.
+			T::VersionWrapper::wrap_version(
+				&Location::new(0, [Junction::Parachain(dest.into())]),
+				notification(),
+			)
+			.unwrap_or_else(|_| {
+				// As a best effort, if we cannot resolve the version, fallback to using the latest
+				// version.
+				VersionedXcm::from(notification())
+			})
+			.encode()
+		}
+	}
+
+	/// Sends/enqueues notification to the destination parachain.
+	fn send_to_para(
+		log_label: &str,
+		config: &HostConfiguration<BlockNumberFor<T>>,
+		dest: ParaId,
+		notification_bytes_for: impl FnOnce(ParaId) -> primitives::DownwardMessage,
+	) {
+		// prepare notification
+		let notification_bytes = notification_bytes_for(dest);
+
+		// try to enqueue
+		if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) =
+			dmp::Pallet::<T>::queue_downward_message(&config, dest, notification_bytes)
+		{
+			// this should never happen unless the max downward message size is configured to a
+			// jokingly small number.
+			log::error!(
+				target: "runtime::hrmp",
+				"sending '{log_label}::notification_bytes' failed."
+			);
+			debug_assert!(false);
+		}
+	}
+}
diff --git a/polkadot/runtime/parachains/src/hrmp/tests.rs b/polkadot/runtime/parachains/src/hrmp/tests.rs
index 2f767ab7e1b..acfaa8f2d29 100644
--- a/polkadot/runtime/parachains/src/hrmp/tests.rs
+++ b/polkadot/runtime/parachains/src/hrmp/tests.rs
@@ -22,13 +22,13 @@ use super::*;
 use crate::{
 	mock::{
 		deregister_parachain, new_test_ext, register_parachain, register_parachain_with_balance,
-		Hrmp, MockGenesisConfig, Paras, ParasShared, RuntimeEvent as MockEvent, RuntimeOrigin,
-		System, Test,
+		Dmp, Hrmp, MockGenesisConfig, Paras, ParasShared, RuntimeEvent as MockEvent, RuntimeOrigin,
+		System, Test, TestUsesOnlyStoredVersionWrapper,
 	},
 	shared,
 };
 use frame_support::{assert_noop, assert_ok, error::BadOrigin};
-use primitives::BlockNumber;
+use primitives::{BlockNumber, InboundDownwardMessage};
 use std::collections::BTreeMap;
 
 pub(crate) fn run_to_block(to: BlockNumber, new_session: Option<Vec<BlockNumber>>) {
@@ -1004,3 +1004,140 @@ fn establish_channel_with_system_with_invalid_args() {
 		Hrmp::assert_storage_consistency_exhaustive();
 	});
 }
+
+#[test]
+fn hrmp_notifications_works() {
+	use xcm::{
+		opaque::{
+			latest::{prelude::*, Xcm},
+			VersionedXcm,
+		},
+		IntoVersion,
+	};
+
+	let para_a = 2001.into();
+	let para_a_origin: crate::Origin = 2001.into();
+	let para_b = 2003.into();
+	let para_b_origin: crate::Origin = 2003.into();
+
+	new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
+		// We need both A & B to be registered and alive parachains.
+		register_parachain(para_a);
+		register_parachain(para_b);
+		run_to_block(5, Some(vec![4, 5]));
+
+		// set XCM versions for wrapper
+
+		// for para_a -> `None`, means we will use latest.
+		TestUsesOnlyStoredVersionWrapper::set_version(
+			Location::new(0, [Junction::Parachain(para_a.into())]),
+			None,
+		);
+		// for para_b -> `Some(latest - 1)`, means we will use latest-1 XCM version.
+		let previous_version = XCM_VERSION - 1;
+		TestUsesOnlyStoredVersionWrapper::set_version(
+			Location::new(0, [Junction::Parachain(para_b.into())]),
+			Some(previous_version),
+		);
+
+		let assert_notification_for = |sent_at, para_id, expected| {
+			assert_eq!(
+				Dmp::dmq_contents(para_id),
+				vec![InboundDownwardMessage { sent_at, msg: expected }]
+			);
+		};
+
+		// init open channel requests
+		assert_ok!(Hrmp::hrmp_init_open_channel(para_a_origin.clone().into(), para_b, 2, 8));
+		assert_ok!(Hrmp::hrmp_init_open_channel(para_b_origin.clone().into(), para_a, 2, 8));
+		Hrmp::assert_storage_consistency_exhaustive();
+
+		// check dmp notications
+		assert_notification_for(
+			5,
+			para_b,
+			VersionedXcm::from(Xcm(vec![HrmpNewChannelOpenRequest {
+				sender: u32::from(para_a),
+				max_capacity: 2,
+				max_message_size: 8,
+			}]))
+			.into_version(previous_version)
+			.expect("compatible")
+			.encode(),
+		);
+		assert_notification_for(
+			5,
+			para_a,
+			VersionedXcm::from(Xcm(vec![HrmpNewChannelOpenRequest {
+				sender: u32::from(para_b),
+				max_capacity: 2,
+				max_message_size: 8,
+			}]))
+			.encode(),
+		);
+		let _ = Dmp::prune_dmq(para_a, 1000);
+		let _ = Dmp::prune_dmq(para_b, 1000);
+
+		// accept open channel requests
+		assert_ok!(Hrmp::hrmp_accept_open_channel(para_a_origin.clone().into(), para_b));
+		assert_ok!(Hrmp::hrmp_accept_open_channel(para_b_origin.clone().into(), para_a));
+		Hrmp::assert_storage_consistency_exhaustive();
+
+		// check dmp notications
+		assert_notification_for(
+			5,
+			para_b,
+			VersionedXcm::from(Xcm(vec![HrmpChannelAccepted { recipient: u32::from(para_a) }]))
+				.into_version(previous_version)
+				.expect("compatible")
+				.encode(),
+		);
+		assert_notification_for(
+			5,
+			para_a,
+			VersionedXcm::from(Xcm(vec![HrmpChannelAccepted { recipient: u32::from(para_b) }]))
+				.encode(),
+		);
+		let _ = Dmp::prune_dmq(para_a, 1000);
+		let _ = Dmp::prune_dmq(para_b, 1000);
+
+		// On Block 6: session change - creates channel.
+		run_to_block(6, Some(vec![6]));
+		assert!(channel_exists(para_a, para_b));
+
+		// close channel requests
+		assert_ok!(Hrmp::hrmp_close_channel(
+			para_a_origin.into(),
+			HrmpChannelId { sender: para_a, recipient: para_b }
+		));
+		assert_ok!(Hrmp::hrmp_close_channel(
+			para_b_origin.into(),
+			HrmpChannelId { sender: para_b, recipient: para_a }
+		));
+		Hrmp::assert_storage_consistency_exhaustive();
+
+		// check dmp notications
+		assert_notification_for(
+			6,
+			para_b,
+			VersionedXcm::from(Xcm(vec![HrmpChannelClosing {
+				initiator: u32::from(para_a),
+				sender: u32::from(para_a),
+				recipient: u32::from(para_b),
+			}]))
+			.into_version(previous_version)
+			.expect("compatible")
+			.encode(),
+		);
+		assert_notification_for(
+			6,
+			para_a,
+			VersionedXcm::from(Xcm(vec![HrmpChannelClosing {
+				initiator: u32::from(para_b),
+				sender: u32::from(para_b),
+				recipient: u32::from(para_a),
+			}]))
+			.encode(),
+		);
+	});
+}
diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs
index 97a75d47ff7..a32c9d11b36 100644
--- a/polkadot/runtime/parachains/src/mock.rs
+++ b/polkadot/runtime/parachains/src/mock.rs
@@ -50,9 +50,16 @@ use sp_runtime::{
 	transaction_validity::TransactionPriority,
 	BuildStorage, FixedU128, Perbill, Permill,
 };
-use sp_std::collections::vec_deque::VecDeque;
-use std::{cell::RefCell, collections::HashMap};
-use xcm::v4::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash};
+use sp_std::{
+	cell::RefCell,
+	collections::{btree_map::BTreeMap, vec_deque::VecDeque},
+};
+use std::collections::HashMap;
+use xcm::{
+	prelude::XcmVersion,
+	v4::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash},
+	IntoVersion, VersionedXcm, WrapVersion,
+};
 
 type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
 type Block = frame_system::mocking::MockBlockU32<Test>;
@@ -247,16 +254,41 @@ impl crate::paras::Config for Test {
 impl crate::dmp::Config for Test {}
 
 parameter_types! {
-	pub const FirstMessageFactorPercent: u64 = 100;
 	pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (4, 1);
 }
 
+thread_local! {
+	pub static VERSION_WRAPPER: RefCell<BTreeMap<Location, Option<XcmVersion>>> = RefCell::new(BTreeMap::new());
+}
+/// Mock implementation of the [`WrapVersion`] trait which wraps XCM only for known/stored XCM
+/// versions in the `VERSION_WRAPPER`.
+pub struct TestUsesOnlyStoredVersionWrapper;
+impl WrapVersion for TestUsesOnlyStoredVersionWrapper {
+	fn wrap_version<RuntimeCall>(
+		dest: &Location,
+		xcm: impl Into<VersionedXcm<RuntimeCall>>,
+	) -> Result<VersionedXcm<RuntimeCall>, ()> {
+		match VERSION_WRAPPER.with(|r| r.borrow().get(dest).map_or(None, |v| *v)) {
+			Some(v) => xcm.into().into_version(v),
+			None => return Err(()),
+		}
+	}
+}
+impl TestUsesOnlyStoredVersionWrapper {
+	pub fn set_version(location: Location, version: Option<XcmVersion>) {
+		VERSION_WRAPPER.with(|r| {
+			let _ = r.borrow_mut().entry(location).and_modify(|v| *v = version).or_insert(version);
+		});
+	}
+}
+
 impl crate::hrmp::Config for Test {
 	type RuntimeOrigin = RuntimeOrigin;
 	type RuntimeEvent = RuntimeEvent;
 	type ChannelManager = frame_system::EnsureRoot<u64>;
 	type Currency = pallet_balances::Pallet<Test>;
 	type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem;
+	type VersionWrapper = TestUsesOnlyStoredVersionWrapper;
 	type WeightInfo = crate::hrmp::TestWeightInfo;
 }
 
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index 90a0fe41d4a..3b2cbc88dc3 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -1046,6 +1046,7 @@ impl parachains_hrmp::Config for Runtime {
 		Runtime,
 		HrmpChannelSizeAndCapacityWithSystemRatio,
 	>;
+	type VersionWrapper = crate::XcmPallet;
 	type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo<Runtime>;
 }
 
diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs
index 1d83e97ef0e..572ecc7d411 100644
--- a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs
+++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `runtime_parachains::hrmp`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-04-30, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-05-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024
@@ -60,6 +60,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
@@ -68,10 +70,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `488`
 		//  Estimated: `3953`
-		// Minimum execution time: 34_034_000 picoseconds.
-		Weight::from_parts(35_191_000, 0)
+		// Minimum execution time: 37_574_000 picoseconds.
+		Weight::from_parts(38_789_000, 0)
 			.saturating_add(Weight::from_parts(0, 3953))
-			.saturating_add(T::DbWeight::get().reads(8))
+			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
 	/// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1)
@@ -80,6 +82,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
@@ -88,10 +92,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `478`
 		//  Estimated: `3943`
-		// Minimum execution time: 30_115_000 picoseconds.
-		Weight::from_parts(31_060_000, 0)
+		// Minimum execution time: 34_560_000 picoseconds.
+		Weight::from_parts(35_760_000, 0)
 			.saturating_add(Weight::from_parts(0, 3943))
-			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
 	/// Storage: `Hrmp::HrmpChannels` (r:1 w:0)
@@ -100,6 +104,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpCloseChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpCloseChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpCloseChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
@@ -108,10 +114,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `591`
 		//  Estimated: `4056`
-		// Minimum execution time: 30_982_000 picoseconds.
-		Weight::from_parts(32_034_000, 0)
+		// Minimum execution time: 35_367_000 picoseconds.
+		Weight::from_parts(37_000_000, 0)
 			.saturating_add(Weight::from_parts(0, 4056))
-			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
 	/// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:128 w:128)
@@ -132,13 +138,13 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `297 + e * (100 ±0) + i * (100 ±0)`
 		//  Estimated: `3759 + e * (2575 ±0) + i * (2575 ±0)`
-		// Minimum execution time: 1_158_665_000 picoseconds.
-		Weight::from_parts(1_164_378_000, 0)
+		// Minimum execution time: 1_134_420_000 picoseconds.
+		Weight::from_parts(1_144_822_000, 0)
 			.saturating_add(Weight::from_parts(0, 3759))
-			// Standard Error: 103_726
-			.saturating_add(Weight::from_parts(3_444_855, 0).saturating_mul(i.into()))
-			// Standard Error: 103_726
-			.saturating_add(Weight::from_parts(3_527_628, 0).saturating_mul(e.into()))
+			// Standard Error: 101_380
+			.saturating_add(Weight::from_parts(3_325_898, 0).saturating_mul(i.into()))
+			// Standard Error: 101_380
+			.saturating_add(Weight::from_parts(3_338_565, 0).saturating_mul(e.into()))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into())))
 			.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(e.into())))
@@ -169,11 +175,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `525 + c * (136 ±0)`
 		//  Estimated: `1980 + c * (5086 ±0)`
-		// Minimum execution time: 5_870_000 picoseconds.
-		Weight::from_parts(2_363_864, 0)
+		// Minimum execution time: 5_652_000 picoseconds.
+		Weight::from_parts(2_857_824, 0)
 			.saturating_add(Weight::from_parts(0, 1980))
-			// Standard Error: 16_657
-			.saturating_add(Weight::from_parts(20_507_232, 0).saturating_mul(c.into()))
+			// Standard Error: 26_044
+			.saturating_add(Weight::from_parts(20_088_467, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(c.into())))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -197,11 +203,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `368 + c * (124 ±0)`
 		//  Estimated: `1828 + c * (2600 ±0)`
-		// Minimum execution time: 4_766_000 picoseconds.
-		Weight::from_parts(4_988_812, 0)
+		// Minimum execution time: 4_692_000 picoseconds.
+		Weight::from_parts(6_637_146, 0)
 			.saturating_add(Weight::from_parts(0, 1828))
-			// Standard Error: 10_606
-			.saturating_add(Weight::from_parts(12_579_429, 0).saturating_mul(c.into()))
+			// Standard Error: 10_238
+			.saturating_add(Weight::from_parts(12_201_629, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(c.into())))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -219,11 +225,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `1059 + c * (13 ±0)`
 		//  Estimated: `4328 + c * (15 ±0)`
-		// Minimum execution time: 17_228_000 picoseconds.
-		Weight::from_parts(27_236_563, 0)
+		// Minimum execution time: 18_920_000 picoseconds.
+		Weight::from_parts(27_314_843, 0)
 			.saturating_add(Weight::from_parts(0, 4328))
-			// Standard Error: 2_419
-			.saturating_add(Weight::from_parts(102_107, 0).saturating_mul(c.into()))
+			// Standard Error: 2_127
+			.saturating_add(Weight::from_parts(90_200, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(3))
 			.saturating_add(Weight::from_parts(0, 15).saturating_mul(c.into()))
@@ -237,11 +243,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `276 + c * (63 ±0)`
 		//  Estimated: `1755 + c * (2538 ±0)`
-		// Minimum execution time: 3_549_000 picoseconds.
-		Weight::from_parts(5_799_542, 0)
+		// Minimum execution time: 3_502_000 picoseconds.
+		Weight::from_parts(6_477_323, 0)
 			.saturating_add(Weight::from_parts(0, 1755))
-			// Standard Error: 3_025
-			.saturating_add(Weight::from_parts(3_173_294, 0).saturating_mul(c.into()))
+			// Standard Error: 3_416
+			.saturating_add(Weight::from_parts(3_149_674, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into())))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -260,6 +266,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0)
 	/// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:2 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
@@ -273,12 +281,12 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `488 + c * (235 ±0)`
 		//  Estimated: `6428 + c * (235 ±0)`
-		// Minimum execution time: 48_392_000 picoseconds.
-		Weight::from_parts(50_509_977, 0)
+		// Minimum execution time: 56_234_000 picoseconds.
+		Weight::from_parts(58_259_646, 0)
 			.saturating_add(Weight::from_parts(0, 6428))
-			// Standard Error: 133_658
-			.saturating_add(Weight::from_parts(10_215_322, 0).saturating_mul(c.into()))
-			.saturating_add(T::DbWeight::get().reads(12))
+			// Standard Error: 160_596
+			.saturating_add(Weight::from_parts(11_178_353, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(14))
 			.saturating_add(T::DbWeight::get().writes(8))
 			.saturating_add(Weight::from_parts(0, 235).saturating_mul(c.into()))
 	}
@@ -294,6 +302,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:2 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
@@ -306,10 +316,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `488`
 		//  Estimated: `6428`
-		// Minimum execution time: 48_465_000 picoseconds.
-		Weight::from_parts(50_433_000, 0)
+		// Minimum execution time: 56_035_000 picoseconds.
+		Weight::from_parts(58_217_000, 0)
 			.saturating_add(Weight::from_parts(0, 6428))
-			.saturating_add(T::DbWeight::get().reads(12))
+			.saturating_add(T::DbWeight::get().reads(14))
 			.saturating_add(T::DbWeight::get().writes(8))
 	}
 	/// Storage: `Hrmp::HrmpChannels` (r:1 w:1)
@@ -318,8 +328,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `296`
 		//  Estimated: `3761`
-		// Minimum execution time: 11_835_000 picoseconds.
-		Weight::from_parts(12_380_000, 0)
+		// Minimum execution time: 11_477_000 picoseconds.
+		Weight::from_parts(11_845_000, 0)
 			.saturating_add(Weight::from_parts(0, 3761))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -336,6 +346,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:2 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
@@ -348,10 +360,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `488`
 		//  Estimated: `6428`
-		// Minimum execution time: 79_633_000 picoseconds.
-		Weight::from_parts(80_846_000, 0)
+		// Minimum execution time: 95_305_000 picoseconds.
+		Weight::from_parts(97_323_000, 0)
 			.saturating_add(Weight::from_parts(0, 6428))
-			.saturating_add(T::DbWeight::get().reads(19))
+			.saturating_add(T::DbWeight::get().reads(21))
 			.saturating_add(T::DbWeight::get().writes(11))
 	}
 }
diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs
index b56e2f52f5f..0509ba382b2 100644
--- a/polkadot/runtime/test-runtime/src/lib.rs
+++ b/polkadot/runtime/test-runtime/src/lib.rs
@@ -558,7 +558,6 @@ parameter_types! {
 impl parachains_dmp::Config for Runtime {}
 
 parameter_types! {
-	pub const FirstMessageFactorPercent: u64 = 100;
 	pub const HrmpChannelSizeAndCapacityWithSystemRatio: Percent = Percent::from_percent(100);
 }
 
@@ -571,6 +570,7 @@ impl parachains_hrmp::Config for Runtime {
 		Runtime,
 		HrmpChannelSizeAndCapacityWithSystemRatio,
 	>;
+	type VersionWrapper = crate::Xcm;
 	type WeightInfo = parachains_hrmp::TestWeightInfo;
 }
 
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index 05a417e17f9..8ae95e6e1a8 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -1176,6 +1176,7 @@ impl parachains_hrmp::Config for Runtime {
 		Runtime,
 		HrmpChannelSizeAndCapacityWithSystemRatio,
 	>;
+	type VersionWrapper = crate::XcmPallet;
 	type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo<Self>;
 }
 
diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs
index 529bdf76105..f1d7932fe8b 100644
--- a/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs
+++ b/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `runtime_parachains::hrmp`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-04-30, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-05-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024
@@ -60,6 +60,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
@@ -68,10 +70,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `455`
 		//  Estimated: `3920`
-		// Minimum execution time: 32_195_000 picoseconds.
-		Weight::from_parts(33_340_000, 0)
+		// Minimum execution time: 35_900_000 picoseconds.
+		Weight::from_parts(37_587_000, 0)
 			.saturating_add(Weight::from_parts(0, 3920))
-			.saturating_add(T::DbWeight::get().reads(8))
+			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
 	/// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1)
@@ -80,6 +82,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
@@ -88,10 +92,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `445`
 		//  Estimated: `3910`
-		// Minimum execution time: 28_644_000 picoseconds.
-		Weight::from_parts(29_581_000, 0)
+		// Minimum execution time: 35_670_000 picoseconds.
+		Weight::from_parts(36_853_000, 0)
 			.saturating_add(Weight::from_parts(0, 3910))
-			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
 	/// Storage: `Hrmp::HrmpChannels` (r:1 w:0)
@@ -100,6 +104,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpCloseChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpCloseChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpCloseChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
@@ -108,10 +114,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `558`
 		//  Estimated: `4023`
-		// Minimum execution time: 31_824_000 picoseconds.
-		Weight::from_parts(33_207_000, 0)
+		// Minimum execution time: 36_953_000 picoseconds.
+		Weight::from_parts(38_638_000, 0)
 			.saturating_add(Weight::from_parts(0, 4023))
-			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
 	/// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:128 w:128)
@@ -132,13 +138,13 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `264 + e * (100 ±0) + i * (100 ±0)`
 		//  Estimated: `3726 + e * (2575 ±0) + i * (2575 ±0)`
-		// Minimum execution time: 1_213_331_000 picoseconds.
-		Weight::from_parts(1_217_120_000, 0)
+		// Minimum execution time: 1_202_266_000 picoseconds.
+		Weight::from_parts(1_217_618_000, 0)
 			.saturating_add(Weight::from_parts(0, 3726))
-			// Standard Error: 108_190
-			.saturating_add(Weight::from_parts(3_485_701, 0).saturating_mul(i.into()))
-			// Standard Error: 108_190
-			.saturating_add(Weight::from_parts(3_564_287, 0).saturating_mul(e.into()))
+			// Standard Error: 113_091
+			.saturating_add(Weight::from_parts(3_550_787, 0).saturating_mul(i.into()))
+			// Standard Error: 113_091
+			.saturating_add(Weight::from_parts(3_615_215, 0).saturating_mul(e.into()))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into())))
 			.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(e.into())))
@@ -169,11 +175,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `492 + c * (136 ±0)`
 		//  Estimated: `1947 + c * (5086 ±0)`
-		// Minimum execution time: 6_040_000 picoseconds.
-		Weight::from_parts(5_644_307, 0)
+		// Minimum execution time: 6_105_000 picoseconds.
+		Weight::from_parts(6_313_000, 0)
 			.saturating_add(Weight::from_parts(0, 1947))
-			// Standard Error: 12_852
-			.saturating_add(Weight::from_parts(21_031_626, 0).saturating_mul(c.into()))
+			// Standard Error: 16_081
+			.saturating_add(Weight::from_parts(21_097_410, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(c.into())))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -197,11 +203,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `335 + c * (124 ±0)`
 		//  Estimated: `1795 + c * (2600 ±0)`
-		// Minimum execution time: 4_950_000 picoseconds.
-		Weight::from_parts(5_215_558, 0)
+		// Minimum execution time: 5_073_000 picoseconds.
+		Weight::from_parts(5_398_000, 0)
 			.saturating_add(Weight::from_parts(0, 1795))
-			// Standard Error: 9_231
-			.saturating_add(Weight::from_parts(12_770_147, 0).saturating_mul(c.into()))
+			// Standard Error: 12_934
+			.saturating_add(Weight::from_parts(13_222_909, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(c.into())))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -219,11 +225,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `1026 + c * (13 ±0)`
 		//  Estimated: `4295 + c * (15 ±0)`
-		// Minimum execution time: 17_550_000 picoseconds.
-		Weight::from_parts(25_522_933, 0)
+		// Minimum execution time: 16_793_000 picoseconds.
+		Weight::from_parts(27_430_638, 0)
 			.saturating_add(Weight::from_parts(0, 4295))
-			// Standard Error: 2_332
-			.saturating_add(Weight::from_parts(121_128, 0).saturating_mul(c.into()))
+			// Standard Error: 2_996
+			.saturating_add(Weight::from_parts(191_905, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(3))
 			.saturating_add(Weight::from_parts(0, 15).saturating_mul(c.into()))
@@ -237,11 +243,11 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `243 + c * (63 ±0)`
 		//  Estimated: `1722 + c * (2538 ±0)`
-		// Minimum execution time: 3_782_000 picoseconds.
-		Weight::from_parts(5_263_610, 0)
+		// Minimum execution time: 3_805_000 picoseconds.
+		Weight::from_parts(445_643, 0)
 			.saturating_add(Weight::from_parts(0, 1722))
-			// Standard Error: 3_152
-			.saturating_add(Weight::from_parts(3_309_777, 0).saturating_mul(c.into()))
+			// Standard Error: 4_991
+			.saturating_add(Weight::from_parts(3_459_894, 0).saturating_mul(c.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into())))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -260,6 +266,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0)
 	/// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:2 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
@@ -273,12 +281,12 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `455 + c * (235 ±0)`
 		//  Estimated: `6395 + c * (235 ±0)`
-		// Minimum execution time: 46_445_000 picoseconds.
-		Weight::from_parts(48_376_448, 0)
+		// Minimum execution time: 53_580_000 picoseconds.
+		Weight::from_parts(55_701_720, 0)
 			.saturating_add(Weight::from_parts(0, 6395))
-			// Standard Error: 130_148
-			.saturating_add(Weight::from_parts(13_606_551, 0).saturating_mul(c.into()))
-			.saturating_add(T::DbWeight::get().reads(12))
+			// Standard Error: 159_757
+			.saturating_add(Weight::from_parts(15_601_979, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(14))
 			.saturating_add(T::DbWeight::get().writes(8))
 			.saturating_add(Weight::from_parts(0, 235).saturating_mul(c.into()))
 	}
@@ -294,6 +302,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:2 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
@@ -306,10 +316,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `455`
 		//  Estimated: `6395`
-		// Minimum execution time: 46_563_000 picoseconds.
-		Weight::from_parts(48_015_000, 0)
+		// Minimum execution time: 54_226_000 picoseconds.
+		Weight::from_parts(55_572_000, 0)
 			.saturating_add(Weight::from_parts(0, 6395))
-			.saturating_add(T::DbWeight::get().reads(12))
+			.saturating_add(T::DbWeight::get().reads(14))
 			.saturating_add(T::DbWeight::get().writes(8))
 	}
 	/// Storage: `Hrmp::HrmpChannels` (r:1 w:1)
@@ -318,8 +328,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `263`
 		//  Estimated: `3728`
-		// Minimum execution time: 12_252_000 picoseconds.
-		Weight::from_parts(12_550_000, 0)
+		// Minimum execution time: 11_850_000 picoseconds.
+		Weight::from_parts(12_428_000, 0)
 			.saturating_add(Weight::from_parts(0, 3728))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -336,6 +346,8 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 	/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
 	/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmPallet::SupportedVersion` (r:2 w:0)
+	/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
 	/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
@@ -348,10 +360,10 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `455`
 		//  Estimated: `6395`
-		// Minimum execution time: 79_503_000 picoseconds.
-		Weight::from_parts(81_630_000, 0)
+		// Minimum execution time: 93_465_000 picoseconds.
+		Weight::from_parts(95_845_000, 0)
 			.saturating_add(Weight::from_parts(0, 6395))
-			.saturating_add(T::DbWeight::get().reads(19))
+			.saturating_add(T::DbWeight::get().reads(21))
 			.saturating_add(T::DbWeight::get().writes(11))
 	}
 }
diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs
index f698eba41d4..c843f52d41a 100644
--- a/polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs
@@ -121,10 +121,6 @@ impl pallet_xcm::Config for Runtime {
 	type AdminOrigin = EnsureRoot<AccountId>;
 }
 
-parameter_types! {
-	pub const FirstMessageFactorPercent: u64 = 100;
-}
-
 impl origin::Config for Runtime {}
 
 type Block = frame_system::mocking::MockBlock<Runtime>;
diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
index 47209b765d1..cf3ca0de2bb 100644
--- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
+++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
@@ -191,10 +191,6 @@ impl pallet_xcm::Config for Runtime {
 	type AdminOrigin = EnsureRoot<AccountId>;
 }
 
-parameter_types! {
-	pub const FirstMessageFactorPercent: u64 = 100;
-}
-
 impl origin::Config for Runtime {}
 
 parameter_types! {
diff --git a/prdoc/pr_4281.prdoc b/prdoc/pr_4281.prdoc
new file mode 100644
index 00000000000..ab2156a9505
--- /dev/null
+++ b/prdoc/pr_4281.prdoc
@@ -0,0 +1,16 @@
+title: "Add support for versioned notification for HRMP pallet"
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      The configuration of the HRMP pallet has been expanded to include the `VersionWrapper` type,
+      which controls the encoding of XCM notifications related to the opening/closing of HRMP channels.
+      If your runtime does not concern itself with the XCM version used for notifications,
+      you can set it as `type VersionWrapper = ()` to always use the latest XCM.
+      If your runtime does care about the XCM version when sending to child parachains,
+      you can provide an instance of the `pallet_xcm` with `type VersionWrapper = XcmPallet`,
+      which can manage XCM versions for destinations.
+
+crates:
+- name: polkadot-runtime-parachains
+  bump: major
-- 
GitLab