diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs
index 65652b38577b361353aca1920255e9788c838028..42a9c23e5aa1132c5f0ff4044b53df2407efdc1e 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 2f767ab7e1b19d311f53d194638bb23066c37d31..acfaa8f2d290510d9d11ed85872c281e8b9c7d85 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 97a75d47ff775401ec549c1fcfd7a247664449fb..a32c9d11b36e737e436d998ebddea1e5d38143b2 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 90a0fe41d4abe6727a8b25b6200476128c7693e4..3b2cbc88dc3fbcb24f8d819237ee9d51231093c0 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 1d83e97ef0e5be54fe5298933d3959663c1ef771..572ecc7d4110f0335b18a3c276032b4f6d285db5 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 b56e2f52f5f6bc0d6d4d4dd79a538a146634166a..0509ba382b2e8b262a391af97dae42cb24291c23 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 05a417e17f927e29c5d328589db8d83acdf4a6ae..8ae95e6e1a83af1e5a7a586f6a206e62d413fbeb 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 529bdf7610550441a53810c0733b94be436f50ea..f1d7932fe8b7c09d069144f624c0f29bce7c4e5b 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 f698eba41d445d5eb70e59873b5c64c50c4a2810..c843f52d41a97f6e70a2320bb3f60a31a9504b50 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 47209b765d15b7dc00b82e45b9dcdd92947efed5..cf3ca0de2bb46a8abe6f5fcd28dbf19ce5faf41b 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 0000000000000000000000000000000000000000..ab2156a9505a666c16e1c079a4cff071a271b21b
--- /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