From 9201f9abbe0b63abbeabc1f6e6799cca030c8c46 Mon Sep 17 00:00:00 2001
From: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Date: Mon, 27 May 2024 07:12:34 +0100
Subject: [PATCH] Deprecate XCMv2 (#4131)

Marked XCMv2 as deprecated now that we have XCMv4.
It will be removed sometime around June 2024.

---------

Co-authored-by: Branislav Kontur <bkontur@gmail.com>
---
 cumulus/pallets/xcmp-queue/src/mock.rs        |  20 +-
 cumulus/pallets/xcmp-queue/src/tests.rs       |  18 +-
 .../bridge-hub-rococo/src/tests/send_xcm.rs   |  68 ++-----
 .../bridge-hub-westend/src/tests/send_xcm.rs  |  68 ++-----
 polkadot/xcm/pallet-xcm/src/benchmarking.rs   |  21 ++-
 .../pallet-xcm/src/tests/assets_transfer.rs   |   2 +-
 polkadot/xcm/pallet-xcm/src/tests/mod.rs      | 142 +++++++-------
 .../procedural/tests/conversion_functions.rs  |   4 +-
 polkadot/xcm/src/lib.rs                       |   6 +
 polkadot/xcm/src/tests.rs                     |  12 --
 polkadot/xcm/src/v2/mod.rs                    |  22 ++-
 polkadot/xcm/src/v3/mod.rs                    | 175 +++++-------------
 polkadot/xcm/src/v3/traits.rs                 |   5 +
 polkadot/xcm/src/v4/mod.rs                    |   2 +-
 .../xcm-builder/src/process_xcm_message.rs    |  16 +-
 prdoc/pr_4131.prdoc                           |  26 +++
 16 files changed, 249 insertions(+), 358 deletions(-)
 create mode 100644 prdoc/pr_4131.prdoc

diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs
index dd87e07c33f..e166a78ee82 100644
--- a/cumulus/pallets/xcmp-queue/src/mock.rs
+++ b/cumulus/pallets/xcmp-queue/src/mock.rs
@@ -321,10 +321,13 @@ impl GetChannelInfo for MockedChannelInfo {
 pub(crate) fn mk_page() -> Vec<u8> {
 	let mut page = Vec::<u8>::new();
 
+	let newer_xcm_version = xcm::prelude::XCM_VERSION;
+	let older_xcm_version = newer_xcm_version - 1;
+
 	for i in 0..100 {
 		page.extend(match i % 2 {
-			0 => v2_xcm().encode(),
-			1 => v3_xcm().encode(),
+			0 => versioned_xcm(older_xcm_version).encode(),
+			1 => versioned_xcm(newer_xcm_version).encode(),
 			// We cannot push an undecodable XCM here since it would break the decode stream.
 			// This is expected and the whole reason to introduce `MaybeDoubleEncodedVersionedXcm`
 			// instead.
@@ -335,12 +338,9 @@ pub(crate) fn mk_page() -> Vec<u8> {
 	page
 }
 
-pub(crate) fn v2_xcm() -> VersionedXcm<()> {
-	let instr = xcm::v2::Instruction::<()>::ClearOrigin;
-	VersionedXcm::V2(xcm::v2::Xcm::<()>(vec![instr; 3]))
-}
-
-pub(crate) fn v3_xcm() -> VersionedXcm<()> {
-	let instr = xcm::v3::Instruction::<()>::Trap(1);
-	VersionedXcm::V3(xcm::v3::Xcm::<()>(vec![instr; 3]))
+pub(crate) fn versioned_xcm(version: XcmVersion) -> VersionedXcm<()> {
+	let instr = Instruction::<()>::Trap(1);
+	VersionedXcm::from(Xcm::<()>(vec![instr; 3]))
+		.into_version(version)
+		.expect("Version conversion should work")
 }
diff --git a/cumulus/pallets/xcmp-queue/src/tests.rs b/cumulus/pallets/xcmp-queue/src/tests.rs
index 7c02059e5a9..cdf41e27f0b 100644
--- a/cumulus/pallets/xcmp-queue/src/tests.rs
+++ b/cumulus/pallets/xcmp-queue/src/tests.rs
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 use super::{
-	mock::{mk_page, v2_xcm, v3_xcm, EnqueuedMessages, HRMP_PARA_ID},
+	mock::{mk_page, versioned_xcm, EnqueuedMessages, HRMP_PARA_ID},
 	*,
 };
 use XcmpMessageFormat::*;
@@ -536,8 +536,8 @@ fn hrmp_signals_are_prioritized() {
 #[test]
 fn maybe_double_encoded_versioned_xcm_works() {
 	// pre conditions
-	assert_eq!(VersionedXcm::<()>::V2(Default::default()).encode(), &[2, 0]);
 	assert_eq!(VersionedXcm::<()>::V3(Default::default()).encode(), &[3, 0]);
+	assert_eq!(VersionedXcm::<()>::V4(Default::default()).encode(), &[4, 0]);
 }
 
 // Now also testing a page instead of just concat messages.
@@ -545,15 +545,18 @@ fn maybe_double_encoded_versioned_xcm_works() {
 fn maybe_double_encoded_versioned_xcm_decode_page_works() {
 	let page = mk_page();
 
+	let newer_xcm_version = xcm::prelude::XCM_VERSION;
+	let older_xcm_version = newer_xcm_version - 1;
+
 	// Now try to decode the page.
 	let input = &mut &page[..];
 	for i in 0..100 {
 		match (i % 2, VersionedXcm::<()>::decode(input)) {
 			(0, Ok(xcm)) => {
-				assert_eq!(xcm, v2_xcm());
+				assert_eq!(xcm, versioned_xcm(older_xcm_version));
 			},
 			(1, Ok(xcm)) => {
-				assert_eq!(xcm, v3_xcm());
+				assert_eq!(xcm, versioned_xcm(newer_xcm_version));
 			},
 			unexpected => unreachable!("{:?}", unexpected),
 		}
@@ -568,14 +571,17 @@ fn take_first_concatenated_xcm_works() {
 	let page = mk_page();
 	let input = &mut &page[..];
 
+	let newer_xcm_version = xcm::prelude::XCM_VERSION;
+	let older_xcm_version = newer_xcm_version - 1;
+
 	for i in 0..100 {
 		let xcm = XcmpQueue::take_first_concatenated_xcm(input, &mut WeightMeter::new()).unwrap();
 		match (i % 2, xcm) {
 			(0, data) | (2, data) => {
-				assert_eq!(data, v2_xcm().encode());
+				assert_eq!(data, versioned_xcm(older_xcm_version).encode());
 			},
 			(1, data) | (3, data) => {
-				assert_eq!(data, v3_xcm().encode());
+				assert_eq!(data, versioned_xcm(newer_xcm_version).encode());
 			},
 			unexpected => unreachable!("{:?}", unexpected),
 		}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs
index a1d871cdb61..78788634e6f 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs
@@ -61,10 +61,13 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable
 #[test]
 fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 	// Initially set only default version on all runtimes
-	AssetHubRococo::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
-	BridgeHubRococo::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
-	BridgeHubWestend::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
-	AssetHubWestend::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
+	let newer_xcm_version = xcm::prelude::XCM_VERSION;
+	let older_xcm_version = newer_xcm_version - 1;
+
+	AssetHubRococo::force_default_xcm_version(Some(older_xcm_version));
+	BridgeHubRococo::force_default_xcm_version(Some(older_xcm_version));
+	BridgeHubWestend::force_default_xcm_version(Some(older_xcm_version));
+	AssetHubWestend::force_default_xcm_version(Some(older_xcm_version));
 
 	// prepare data
 	let destination = asset_hub_westend_location();
@@ -87,42 +90,12 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 	);
 
 	// set destination version
-	AssetHubRococo::force_xcm_version(destination.clone(), xcm::v3::prelude::XCM_VERSION);
-
-	// TODO: remove this block, when removing `xcm:v2`
-	{
-		// send XCM from AssetHubRococo - fails - AssetHubRococo is set to the default/safe `2`
-		// version, which does not have the `ExportMessage` instruction. If the default `2` is
-		// changed to `3`, then this assert can go away"
-		assert_err!(
-			send_asset_from_asset_hub_rococo(destination.clone(), (native_token.clone(), amount)),
-			DispatchError::Module(sp_runtime::ModuleError {
-				index: 31,
-				error: [1, 0, 0, 0],
-				message: Some("SendFailure")
-			})
-		);
-
-		// set exact version for BridgeHubWestend to `2` without `ExportMessage` instruction
-		AssetHubRococo::force_xcm_version(
-			ParentThen(Parachain(BridgeHubRococo::para_id().into()).into()).into(),
-			xcm::v2::prelude::XCM_VERSION,
-		);
-		// send XCM from AssetHubRococo - fails - `ExportMessage` is not in `2`
-		assert_err!(
-			send_asset_from_asset_hub_rococo(destination.clone(), (native_token.clone(), amount)),
-			DispatchError::Module(sp_runtime::ModuleError {
-				index: 31,
-				error: [1, 0, 0, 0],
-				message: Some("SendFailure")
-			})
-		);
-	}
+	AssetHubRococo::force_xcm_version(destination.clone(), newer_xcm_version);
 
 	// set version with `ExportMessage` for BridgeHubRococo
 	AssetHubRococo::force_xcm_version(
 		ParentThen(Parachain(BridgeHubRococo::para_id().into()).into()).into(),
-		xcm::v3::prelude::XCM_VERSION,
+		newer_xcm_version,
 	);
 	// send XCM from AssetHubRococo - ok
 	assert_ok!(send_asset_from_asset_hub_rococo(
@@ -134,14 +107,11 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 	assert_bridge_hub_rococo_message_accepted(false);
 
 	// set version for remote BridgeHub on BridgeHubRococo
-	BridgeHubRococo::force_xcm_version(
-		bridge_hub_westend_location(),
-		xcm::v3::prelude::XCM_VERSION,
-	);
+	BridgeHubRococo::force_xcm_version(bridge_hub_westend_location(), newer_xcm_version);
 	// set version for AssetHubWestend on BridgeHubWestend
 	BridgeHubWestend::force_xcm_version(
 		ParentThen(Parachain(AssetHubWestend::para_id().into()).into()).into(),
-		xcm::v3::prelude::XCM_VERSION,
+		newer_xcm_version,
 	);
 
 	// send XCM from AssetHubRococo - ok
@@ -164,20 +134,4 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 			]
 		);
 	});
-
-	// TODO: remove this block, when removing `xcm:v2`
-	{
-		// set `2` version for remote BridgeHub on BridgeHubRococo, which does not have
-		// `UniversalOrigin` and `DescendOrigin`
-		BridgeHubRococo::force_xcm_version(
-			bridge_hub_westend_location(),
-			xcm::v2::prelude::XCM_VERSION,
-		);
-
-		// send XCM from AssetHubRococo - ok
-		assert_ok!(send_asset_from_asset_hub_rococo(destination, (native_token, amount)));
-		// message is not accepted on the local BridgeHub (`DestinationUnsupported`) because we
-		// cannot add `UniversalOrigin` and `DescendOrigin`
-		assert_bridge_hub_rococo_message_accepted(false);
-	}
 }
diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs
index b01be5e8dc8..8539df97be9 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs
@@ -61,10 +61,13 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable
 #[test]
 fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 	// Initially set only default version on all runtimes
-	AssetHubRococo::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
-	BridgeHubRococo::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
-	BridgeHubWestend::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
-	AssetHubWestend::force_default_xcm_version(Some(xcm::v2::prelude::XCM_VERSION));
+	let newer_xcm_version = xcm::prelude::XCM_VERSION;
+	let older_xcm_version = newer_xcm_version - 1;
+
+	AssetHubRococo::force_default_xcm_version(Some(older_xcm_version));
+	BridgeHubRococo::force_default_xcm_version(Some(older_xcm_version));
+	BridgeHubWestend::force_default_xcm_version(Some(older_xcm_version));
+	AssetHubWestend::force_default_xcm_version(Some(older_xcm_version));
 
 	// prepare data
 	let destination = asset_hub_rococo_location();
@@ -87,42 +90,12 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 	);
 
 	// set destination version
-	AssetHubWestend::force_xcm_version(destination.clone(), xcm::v3::prelude::XCM_VERSION);
-
-	// TODO: remove this block, when removing `xcm:v2`
-	{
-		// send XCM from AssetHubRococo - fails - AssetHubRococo is set to the default/safe `2`
-		// version, which does not have the `ExportMessage` instruction. If the default `2` is
-		// changed to `3`, then this assert can go away"
-		assert_err!(
-			send_asset_from_asset_hub_westend(destination.clone(), (native_token.clone(), amount)),
-			DispatchError::Module(sp_runtime::ModuleError {
-				index: 31,
-				error: [1, 0, 0, 0],
-				message: Some("SendFailure")
-			})
-		);
-
-		// set exact version for BridgeHubWestend to `2` without `ExportMessage` instruction
-		AssetHubWestend::force_xcm_version(
-			ParentThen(Parachain(BridgeHubWestend::para_id().into()).into()).into(),
-			xcm::v2::prelude::XCM_VERSION,
-		);
-		// send XCM from AssetHubWestend - fails - `ExportMessage` is not in `2`
-		assert_err!(
-			send_asset_from_asset_hub_westend(destination.clone(), (native_token.clone(), amount)),
-			DispatchError::Module(sp_runtime::ModuleError {
-				index: 31,
-				error: [1, 0, 0, 0],
-				message: Some("SendFailure")
-			})
-		);
-	}
+	AssetHubWestend::force_xcm_version(destination.clone(), newer_xcm_version);
 
 	// set version with `ExportMessage` for BridgeHubWestend
 	AssetHubWestend::force_xcm_version(
 		ParentThen(Parachain(BridgeHubWestend::para_id().into()).into()).into(),
-		xcm::v3::prelude::XCM_VERSION,
+		newer_xcm_version,
 	);
 	// send XCM from AssetHubWestend - ok
 	assert_ok!(send_asset_from_asset_hub_westend(
@@ -134,14 +107,11 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 	assert_bridge_hub_westend_message_accepted(false);
 
 	// set version for remote BridgeHub on BridgeHubWestend
-	BridgeHubWestend::force_xcm_version(
-		bridge_hub_rococo_location(),
-		xcm::v3::prelude::XCM_VERSION,
-	);
+	BridgeHubWestend::force_xcm_version(bridge_hub_rococo_location(), newer_xcm_version);
 	// set version for AssetHubRococo on BridgeHubRococo
 	BridgeHubRococo::force_xcm_version(
 		ParentThen(Parachain(AssetHubRococo::para_id().into()).into()).into(),
-		xcm::v3::prelude::XCM_VERSION,
+		newer_xcm_version,
 	);
 
 	// send XCM from AssetHubWestend - ok
@@ -164,20 +134,4 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
 			]
 		);
 	});
-
-	// TODO: remove this block, when removing `xcm:v2`
-	{
-		// set `2` version for remote BridgeHub on BridgeHubRococo, which does not have
-		// `UniversalOrigin` and `DescendOrigin`
-		BridgeHubWestend::force_xcm_version(
-			bridge_hub_rococo_location(),
-			xcm::v2::prelude::XCM_VERSION,
-		);
-
-		// send XCM from AssetHubWestend - ok
-		assert_ok!(send_asset_from_asset_hub_westend(destination, (native_token, amount)));
-		// message is not accepted on the local BridgeHub (`DestinationUnsupported`) because we
-		// cannot add `UniversalOrigin` and `DescendOrigin`
-		assert_bridge_hub_westend_message_accepted(false);
-	}
 }
diff --git a/polkadot/xcm/pallet-xcm/src/benchmarking.rs b/polkadot/xcm/pallet-xcm/src/benchmarking.rs
index 081a4235b77..da46a6a37c0 100644
--- a/polkadot/xcm/pallet-xcm/src/benchmarking.rs
+++ b/polkadot/xcm/pallet-xcm/src/benchmarking.rs
@@ -15,12 +15,11 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use super::*;
-use bounded_collections::{ConstU32, WeakBoundedVec};
 use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError, BenchmarkResult};
 use frame_support::{assert_ok, weights::Weight};
 use frame_system::RawOrigin;
 use sp_std::prelude::*;
-use xcm::{latest::prelude::*, v2};
+use xcm::latest::prelude::*;
 use xcm_builder::EnsureDelivery;
 use xcm_executor::traits::FeeReason;
 
@@ -313,15 +312,17 @@ benchmarks! {
 	}
 
 	notify_target_migration_fail {
-		let bad_loc: v2::MultiLocation = v2::Junction::Plurality {
-			id: v2::BodyId::Named(WeakBoundedVec::<u8, ConstU32<32>>::try_from(vec![0; 32])
-				.expect("vec has a length of 32 bits; qed")),
-			part: v2::BodyPart::Voice,
-		}
-		.into();
-		let bad_loc = VersionedLocation::from(bad_loc);
+		let newer_xcm_version = xcm::prelude::XCM_VERSION;
+		let older_xcm_version = newer_xcm_version - 1;
+		let bad_location: Location = Plurality {
+			id: BodyId::Unit,
+			part: BodyPart::Voice,
+		}.into();
+		let bad_location = VersionedLocation::from(bad_location)
+			.into_version(older_xcm_version)
+			.expect("Version convertion should work");
 		let current_version = T::AdvertisedXcmVersion::get();
-		VersionNotifyTargets::<T>::insert(current_version, bad_loc, (0, Weight::zero(), current_version));
+		VersionNotifyTargets::<T>::insert(current_version, bad_location, (0, Weight::zero(), current_version));
 	}: {
 		crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
 	}
diff --git a/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs b/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs
index f42e220d693..af81ac9cf43 100644
--- a/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs
+++ b/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs
@@ -76,7 +76,7 @@ fn limited_teleport_assets_works() {
 			)]
 		);
 		let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
-		let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
+		let _check_v3_ok: xcm::v3::Xcm<()> = versioned_sent.try_into().unwrap();
 
 		let mut last_events = last_events(3).into_iter();
 		assert_eq!(
diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs
index 02aeafd68e8..c16c1a1ba98 100644
--- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs
+++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs
@@ -602,11 +602,11 @@ fn basic_subscription_works() {
 
 		let weight = BaseXcmWeight::get();
 		let mut message = Xcm::<()>(vec![
-			// Remote supports XCM v2
+			// Remote supports XCM v3
 			QueryResponse {
 				query_id: 0,
 				max_weight: Weight::zero(),
-				response: Response::Version(1),
+				response: Response::Version(3),
 				querier: None,
 			},
 		]);
@@ -764,14 +764,14 @@ fn subscription_side_upgrades_work_with_notify() {
 	new_test_ext_with_balances(vec![]).execute_with(|| {
 		AdvertisedXcmVersion::set(1);
 
-		// An entry from a previous runtime with v2 XCM.
-		let v2_location = VersionedLocation::V2(xcm::v2::Junction::Parachain(1001).into());
-		VersionNotifyTargets::<Test>::insert(1, v2_location, (70, Weight::zero(), 2));
-		let v3_location = Parachain(1003).into_versioned();
-		VersionNotifyTargets::<Test>::insert(3, v3_location, (72, Weight::zero(), 2));
+		// An entry from a previous runtime with v3 XCM.
+		let v3_location = VersionedLocation::V3(xcm::v3::Junction::Parachain(1001).into());
+		VersionNotifyTargets::<Test>::insert(3, v3_location, (70, Weight::zero(), 3));
+		let v4_location = Parachain(1003).into_versioned();
+		VersionNotifyTargets::<Test>::insert(4, v4_location, (72, Weight::zero(), 3));
 
 		// New version.
-		AdvertisedXcmVersion::set(3);
+		AdvertisedXcmVersion::set(4);
 
 		// A runtime upgrade which alters the version does send notifications.
 		CurrentMigration::<Test>::put(VersionMigrationStage::default());
@@ -780,13 +780,13 @@ fn subscription_side_upgrades_work_with_notify() {
 		let instr1 = QueryResponse {
 			query_id: 70,
 			max_weight: Weight::zero(),
-			response: Response::Version(3),
+			response: Response::Version(4),
 			querier: None,
 		};
 		let instr3 = QueryResponse {
 			query_id: 72,
 			max_weight: Weight::zero(),
-			response: Response::Version(3),
+			response: Response::Version(4),
 			querier: None,
 		};
 		let mut sent = take_sent_xcm();
@@ -807,8 +807,8 @@ fn subscription_side_upgrades_work_with_notify() {
 		assert_eq!(
 			contents,
 			vec![
-				(XCM_VERSION, Parachain(1001).into_versioned(), (70, Weight::zero(), 3)),
-				(XCM_VERSION, Parachain(1003).into_versioned(), (72, Weight::zero(), 3)),
+				(XCM_VERSION, Parachain(1001).into_versioned(), (70, Weight::zero(), 4)),
+				(XCM_VERSION, Parachain(1003).into_versioned(), (72, Weight::zero(), 4)),
 			]
 		);
 	});
@@ -817,11 +817,11 @@ fn subscription_side_upgrades_work_with_notify() {
 #[test]
 fn subscription_side_upgrades_work_without_notify() {
 	new_test_ext_with_balances(vec![]).execute_with(|| {
-		// An entry from a previous runtime with v2 XCM.
-		let v2_location = VersionedLocation::V2(xcm::v2::Junction::Parachain(1001).into());
-		VersionNotifyTargets::<Test>::insert(1, v2_location, (70, Weight::zero(), 2));
-		let v3_location = Parachain(1003).into_versioned();
-		VersionNotifyTargets::<Test>::insert(3, v3_location, (72, Weight::zero(), 2));
+		// An entry from a previous runtime with v3 XCM.
+		let v3_location = VersionedLocation::V3(xcm::v3::Junction::Parachain(1001).into());
+		VersionNotifyTargets::<Test>::insert(3, v3_location, (70, Weight::zero(), 3));
+		let v4_location = Parachain(1003).into_versioned();
+		VersionNotifyTargets::<Test>::insert(4, v4_location, (72, Weight::zero(), 3));
 
 		// A runtime upgrade which alters the version does send notifications.
 		CurrentMigration::<Test>::put(VersionMigrationStage::default());
@@ -854,11 +854,11 @@ fn subscriber_side_subscription_works() {
 
 		let weight = BaseXcmWeight::get();
 		let message = Xcm(vec![
-			// Remote supports XCM v2
+			// Remote supports XCM v3
 			QueryResponse {
 				query_id: 0,
 				max_weight: Weight::zero(),
-				response: Response::Version(1),
+				response: Response::Version(3),
 				querier: None,
 			},
 		]);
@@ -872,18 +872,21 @@ fn subscriber_side_subscription_works() {
 		);
 		assert_eq!(r, Outcome::Complete { used: weight });
 		assert_eq!(take_sent_xcm(), vec![]);
-		assert_eq!(XcmPallet::get_version_for(&remote), Some(1));
+		assert_eq!(XcmPallet::get_version_for(&remote), Some(3));
 
-		// This message cannot be sent to a v2 remote.
-		let v2_msg = xcm::v2::Xcm::<()>(vec![xcm::v2::Instruction::Trap(0)]);
-		assert_eq!(XcmPallet::wrap_version(&remote, v2_msg.clone()), Err(()));
+		// This message will be sent as v3.
+		let v4_msg = xcm::v4::Xcm::<()>(vec![xcm::v4::Instruction::Trap(0)]);
+		assert_eq!(
+			XcmPallet::wrap_version(&remote, v4_msg.clone()),
+			Ok(VersionedXcm::V3(xcm::v3::Xcm(vec![xcm::v3::Instruction::Trap(0)])))
+		);
 
 		let message = Xcm(vec![
-			// Remote upgraded to XCM v2
+			// Remote upgraded to XCM v4
 			QueryResponse {
 				query_id: 0,
 				max_weight: Weight::zero(),
-				response: Response::Version(2),
+				response: Response::Version(4),
 				querier: None,
 			},
 		]);
@@ -897,12 +900,12 @@ fn subscriber_side_subscription_works() {
 		);
 		assert_eq!(r, Outcome::Complete { used: weight });
 		assert_eq!(take_sent_xcm(), vec![]);
-		assert_eq!(XcmPallet::get_version_for(&remote), Some(2));
+		assert_eq!(XcmPallet::get_version_for(&remote), Some(4));
 
-		// This message can now be sent to remote as it's v2.
+		// This message is now sent as v4.
 		assert_eq!(
-			XcmPallet::wrap_version(&remote, v2_msg.clone()),
-			Ok(VersionedXcm::from(v2_msg))
+			XcmPallet::wrap_version(&remote, v4_msg.clone()),
+			Ok(VersionedXcm::from(v4_msg))
 		);
 	});
 }
@@ -911,30 +914,36 @@ fn subscriber_side_subscription_works() {
 #[test]
 fn auto_subscription_works() {
 	new_test_ext_with_balances_and_xcm_version(vec![], None).execute_with(|| {
-		let remote_v2: Location = Parachain(1000).into();
+		let remote_v3: Location = Parachain(1000).into();
 		let remote_v4: Location = Parachain(1001).into();
 
-		assert_ok!(XcmPallet::force_default_xcm_version(RuntimeOrigin::root(), Some(2)));
+		assert_ok!(XcmPallet::force_default_xcm_version(RuntimeOrigin::root(), Some(3)));
 
 		// Wrapping a version for a destination we don't know elicits a subscription.
-		let msg_v2 = xcm::v2::Xcm::<()>(vec![xcm::v2::Instruction::Trap(0)]);
+		let msg_v3 = xcm::v3::Xcm::<()>(vec![xcm::v3::Instruction::Trap(0)]);
 		let msg_v4 = xcm::v4::Xcm::<()>(vec![xcm::v4::Instruction::ClearTopic]);
 		assert_eq!(
-			XcmPallet::wrap_version(&remote_v2, msg_v2.clone()),
-			Ok(VersionedXcm::from(msg_v2.clone())),
+			XcmPallet::wrap_version(&remote_v3, msg_v3.clone()),
+			Ok(VersionedXcm::from(msg_v3.clone())),
+		);
+		assert_eq!(
+			XcmPallet::wrap_version(&remote_v3, msg_v4.clone()),
+			Ok(VersionedXcm::V3(xcm::v3::Xcm(vec![xcm::v3::Instruction::ClearTopic])))
 		);
-		assert_eq!(XcmPallet::wrap_version(&remote_v2, msg_v4.clone()), Err(()));
 
-		let expected = vec![(remote_v2.clone().into(), 2)];
+		let expected = vec![(remote_v3.clone().into(), 2)];
 		assert_eq!(VersionDiscoveryQueue::<Test>::get().into_inner(), expected);
 
 		assert_eq!(
-			XcmPallet::wrap_version(&remote_v4, msg_v2.clone()),
-			Ok(VersionedXcm::from(msg_v2.clone())),
+			XcmPallet::wrap_version(&remote_v4, msg_v3.clone()),
+			Ok(VersionedXcm::from(msg_v3.clone())),
+		);
+		assert_eq!(
+			XcmPallet::wrap_version(&remote_v4, msg_v4.clone()),
+			Ok(VersionedXcm::V3(xcm::v3::Xcm(vec![xcm::v3::Instruction::ClearTopic])))
 		);
-		assert_eq!(XcmPallet::wrap_version(&remote_v4, msg_v4.clone()), Err(()));
 
-		let expected = vec![(remote_v2.clone().into(), 2), (remote_v4.clone().into(), 2)];
+		let expected = vec![(remote_v3.clone().into(), 2), (remote_v4.clone().into(), 2)];
 		assert_eq!(VersionDiscoveryQueue::<Test>::get().into_inner(), expected);
 
 		XcmPallet::on_initialize(1);
@@ -968,10 +977,10 @@ fn auto_subscription_works() {
 		);
 		assert_eq!(r, Outcome::Complete { used: weight });
 
-		// V2 messages can be sent to remote_v4 under XCM v4.
+		// V3 messages can be sent to remote_v4 under XCM v4.
 		assert_eq!(
-			XcmPallet::wrap_version(&remote_v4, msg_v2.clone()),
-			Ok(VersionedXcm::from(msg_v2.clone()).into_version(4).unwrap()),
+			XcmPallet::wrap_version(&remote_v4, msg_v3.clone()),
+			Ok(VersionedXcm::from(msg_v3.clone()).into_version(4).unwrap()),
 		);
 		// This message can now be sent to remote_v4 as it's v4.
 		assert_eq!(
@@ -983,26 +992,26 @@ fn auto_subscription_works() {
 		assert_eq!(
 			take_sent_xcm(),
 			vec![(
-				remote_v2.clone(),
+				remote_v3.clone(),
 				Xcm(vec![SubscribeVersion { query_id: 1, max_response_weight: Weight::zero() }]),
 			)]
 		);
 
-		// Assume remote_v2 is working ok and XCM version 2.
+		// Assume remote_v3 is working ok and XCM version 3.
 
 		let weight = BaseXcmWeight::get();
 		let message = Xcm(vec![
-			// Remote supports XCM v2
+			// Remote supports XCM v3
 			QueryResponse {
 				query_id: 1,
 				max_weight: Weight::zero(),
-				response: Response::Version(2),
+				response: Response::Version(3),
 				querier: None,
 			},
 		]);
 		let mut hash = fake_message_hash(&message);
 		let r = XcmExecutor::<XcmConfig>::prepare_and_execute(
-			remote_v2.clone(),
+			remote_v3.clone(),
 			message,
 			&mut hash,
 			weight,
@@ -1010,12 +1019,15 @@ fn auto_subscription_works() {
 		);
 		assert_eq!(r, Outcome::Complete { used: weight });
 
-		// v4 messages cannot be sent to remote_v2...
+		// v4 messages cannot be sent to remote_v3...
+		assert_eq!(
+			XcmPallet::wrap_version(&remote_v3, msg_v3.clone()),
+			Ok(VersionedXcm::V3(msg_v3))
+		);
 		assert_eq!(
-			XcmPallet::wrap_version(&remote_v2, msg_v2.clone()),
-			Ok(VersionedXcm::V2(msg_v2))
+			XcmPallet::wrap_version(&remote_v3, msg_v4.clone()),
+			Ok(VersionedXcm::V3(xcm::v3::Xcm(vec![xcm::v3::Instruction::ClearTopic])))
 		);
-		assert_eq!(XcmPallet::wrap_version(&remote_v2, msg_v4.clone()), Err(()));
 	})
 }
 
@@ -1025,15 +1037,15 @@ fn subscription_side_upgrades_work_with_multistage_notify() {
 		AdvertisedXcmVersion::set(1);
 
 		// An entry from a previous runtime with v0 XCM.
-		let v2_location = VersionedLocation::V2(xcm::v2::Junction::Parachain(1001).into());
-		VersionNotifyTargets::<Test>::insert(1, v2_location, (70, Weight::zero(), 1));
-		let v2_location = VersionedLocation::V2(xcm::v2::Junction::Parachain(1002).into());
-		VersionNotifyTargets::<Test>::insert(2, v2_location, (71, Weight::zero(), 1));
-		let v3_location = Parachain(1003).into_versioned();
-		VersionNotifyTargets::<Test>::insert(3, v3_location, (72, Weight::zero(), 1));
+		let v3_location = VersionedLocation::V3(xcm::v3::Junction::Parachain(1001).into());
+		VersionNotifyTargets::<Test>::insert(3, v3_location, (70, Weight::zero(), 3));
+		let v3_location = VersionedLocation::V3(xcm::v3::Junction::Parachain(1002).into());
+		VersionNotifyTargets::<Test>::insert(3, v3_location, (71, Weight::zero(), 3));
+		let v4_location = Parachain(1003).into_versioned();
+		VersionNotifyTargets::<Test>::insert(4, v4_location, (72, Weight::zero(), 3));
 
 		// New version.
-		AdvertisedXcmVersion::set(3);
+		AdvertisedXcmVersion::set(4);
 
 		// A runtime upgrade which alters the version does send notifications.
 		CurrentMigration::<Test>::put(VersionMigrationStage::default());
@@ -1049,19 +1061,19 @@ fn subscription_side_upgrades_work_with_multistage_notify() {
 		let instr1 = QueryResponse {
 			query_id: 70,
 			max_weight: Weight::zero(),
-			response: Response::Version(3),
+			response: Response::Version(4),
 			querier: None,
 		};
 		let instr2 = QueryResponse {
 			query_id: 71,
 			max_weight: Weight::zero(),
-			response: Response::Version(3),
+			response: Response::Version(4),
 			querier: None,
 		};
 		let instr3 = QueryResponse {
 			query_id: 72,
 			max_weight: Weight::zero(),
-			response: Response::Version(3),
+			response: Response::Version(4),
 			querier: None,
 		};
 		let mut sent = take_sent_xcm();
@@ -1083,9 +1095,9 @@ fn subscription_side_upgrades_work_with_multistage_notify() {
 		assert_eq!(
 			contents,
 			vec![
-				(XCM_VERSION, Parachain(1001).into_versioned(), (70, Weight::zero(), 3)),
-				(XCM_VERSION, Parachain(1002).into_versioned(), (71, Weight::zero(), 3)),
-				(XCM_VERSION, Parachain(1003).into_versioned(), (72, Weight::zero(), 3)),
+				(XCM_VERSION, Parachain(1001).into_versioned(), (70, Weight::zero(), 4)),
+				(XCM_VERSION, Parachain(1002).into_versioned(), (71, Weight::zero(), 4)),
+				(XCM_VERSION, Parachain(1003).into_versioned(), (72, Weight::zero(), 4)),
 			]
 		);
 	});
diff --git a/polkadot/xcm/procedural/tests/conversion_functions.rs b/polkadot/xcm/procedural/tests/conversion_functions.rs
index 5b6965167fc..7d2698d2cd7 100644
--- a/polkadot/xcm/procedural/tests/conversion_functions.rs
+++ b/polkadot/xcm/procedural/tests/conversion_functions.rs
@@ -14,10 +14,10 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-use xcm::v2::prelude::*;
+use xcm::v3::prelude::*;
 
 #[test]
-fn slice_syntax_in_v2_works() {
+fn slice_syntax_in_v3_works() {
 	let old_junctions = Junctions::X2(Parachain(1), PalletInstance(1));
 	let new_junctions = Junctions::from([Parachain(1), PalletInstance(1)]);
 	assert_eq!(old_junctions, new_junctions);
diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs
index 513dfe5501b..8b0030e59b5 100644
--- a/polkadot/xcm/src/lib.rs
+++ b/polkadot/xcm/src/lib.rs
@@ -21,6 +21,8 @@
 //
 // Hence, `no_std` rather than sp-runtime.
 #![cfg_attr(not(feature = "std"), no_std)]
+// Because of XCMv2.
+#![allow(deprecated)]
 
 extern crate alloc;
 
@@ -28,6 +30,9 @@ use derivative::Derivative;
 use parity_scale_codec::{Decode, DecodeLimit, Encode, Error as CodecError, Input, MaxEncodedLen};
 use scale_info::TypeInfo;
 
+#[deprecated(
+	note = "XCMv2 will be removed once XCMv5 is released. Please use XCMv3 or XCMv4 instead."
+)]
 pub mod v2;
 pub mod v3;
 pub mod v4;
@@ -425,6 +430,7 @@ pub type VersionedMultiAssets = VersionedAssets;
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum VersionedXcm<RuntimeCall> {
 	#[codec(index = 2)]
+	#[deprecated]
 	V2(v2::Xcm<RuntimeCall>),
 	#[codec(index = 3)]
 	V3(v3::Xcm<RuntimeCall>),
diff --git a/polkadot/xcm/src/tests.rs b/polkadot/xcm/src/tests.rs
index 1aabbcef281..4c666063f3f 100644
--- a/polkadot/xcm/src/tests.rs
+++ b/polkadot/xcm/src/tests.rs
@@ -158,18 +158,6 @@ fn encode_decode_versioned_multi_assets_v3() {
 	assert_eq!(assets, decoded);
 }
 
-#[test]
-fn encode_decode_versioned_xcm_v2() {
-	let xcm = VersionedXcm::V2(v2::Xcm::<()>::new());
-	let encoded = xcm.encode();
-
-	assert_eq!(encoded, hex_literal::hex!("0200"), "encode format changed");
-	assert_eq!(encoded[0], 2, "bad version number");
-
-	let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap();
-	assert_eq!(xcm, decoded);
-}
-
 #[test]
 fn encode_decode_versioned_xcm_v3() {
 	let xcm = VersionedXcm::V3(v3::Xcm::<()>::new());
diff --git a/polkadot/xcm/src/v2/mod.rs b/polkadot/xcm/src/v2/mod.rs
index 347f3f2c292..7b6858e6a5c 100644
--- a/polkadot/xcm/src/v2/mod.rs
+++ b/polkadot/xcm/src/v2/mod.rs
@@ -15,6 +15,9 @@
 // along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
 
 //! # XCM Version 2
+//!
+//! WARNING: DEPRECATED, please use version 3 or 4.
+//!
 //! Version 2 of the Cross-Consensus Message format data structures. The comprehensive list of
 //! changes can be found in
 //! [this PR description](https://github.com/paritytech/polkadot/pull/3629#issue-968428279).
@@ -52,8 +55,8 @@
 use super::{
 	v3::{
 		BodyId as NewBodyId, BodyPart as NewBodyPart, Instruction as NewInstruction,
-		NetworkId as NewNetworkId, Response as NewResponse, WeightLimit as NewWeightLimit,
-		Xcm as NewXcm,
+		NetworkId as NewNetworkId, OriginKind as NewOriginKind, Response as NewResponse,
+		WeightLimit as NewWeightLimit, Xcm as NewXcm,
 	},
 	DoubleEncoded,
 };
@@ -104,6 +107,18 @@ pub enum OriginKind {
 	Xcm,
 }
 
+impl From<NewOriginKind> for OriginKind {
+	fn from(new: NewOriginKind) -> Self {
+		use NewOriginKind::*;
+		match new {
+			Native => Self::Native,
+			SovereignAccount => Self::SovereignAccount,
+			Superuser => Self::Superuser,
+			Xcm => Self::Xcm,
+		}
+	}
+}
+
 /// A global identifier of an account-bearing consensus system.
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
@@ -262,6 +277,7 @@ pub const VERSION: super::Version = 2;
 /// An identifier for a query.
 pub type QueryId = u64;
 
+/// DEPRECATED. Please use XCMv3 or XCMv4 instead.
 #[derive(Derivative, Default, Encode, Decode, TypeInfo)]
 #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
 #[codec(encode_bound())]
@@ -1065,7 +1081,7 @@ impl<RuntimeCall> TryFrom<NewInstruction<RuntimeCall>> for Instruction<RuntimeCa
 			HrmpChannelClosing { initiator, sender, recipient } =>
 				Self::HrmpChannelClosing { initiator, sender, recipient },
 			Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
-				origin_type: origin_kind,
+				origin_type: origin_kind.into(),
 				require_weight_at_most: require_weight_at_most.ref_time(),
 				call: call.into(),
 			},
diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs
index e7c57f414eb..8ff661a9bba 100644
--- a/polkadot/xcm/src/v3/mod.rs
+++ b/polkadot/xcm/src/v3/mod.rs
@@ -16,15 +16,14 @@
 
 //! Version 3 of the Cross-Consensus Message format data structures.
 
-use super::{
-	v2::{
-		Instruction as OldInstruction, Response as OldResponse, WeightLimit as OldWeightLimit,
-		Xcm as OldXcm,
-	},
-	v4::{
-		Instruction as NewInstruction, PalletInfo as NewPalletInfo,
-		QueryResponseInfo as NewQueryResponseInfo, Response as NewResponse, Xcm as NewXcm,
-	},
+#[allow(deprecated)]
+use super::v2::{
+	Instruction as OldInstruction, OriginKind as OldOriginKind, Response as OldResponse,
+	WeightLimit as OldWeightLimit, Xcm as OldXcm,
+};
+use super::v4::{
+	Instruction as NewInstruction, PalletInfo as NewPalletInfo,
+	QueryResponseInfo as NewQueryResponseInfo, Response as NewResponse, Xcm as NewXcm,
 };
 use crate::DoubleEncoded;
 use alloc::{vec, vec::Vec};
@@ -53,11 +52,46 @@ pub use multilocation::{
 	Ancestor, AncestorThen, InteriorMultiLocation, Location, MultiLocation, Parent, ParentThen,
 };
 pub use traits::{
-	send_xcm, validate_send, Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError,
-	SendResult, SendXcm, Weight, XcmHash,
+	send_xcm, validate_send, Error, ExecuteXcm, GetWeight, Outcome, PreparedMessage, Result,
+	SendError, SendResult, SendXcm, Weight, XcmHash,
 };
-// These parts of XCM v2 are unchanged in XCM v3, and are re-imported here.
-pub use super::v2::{GetWeight, OriginKind};
+
+/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
+#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
+#[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum OriginKind {
+	/// Origin should just be the native dispatch origin representation for the sender in the
+	/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
+	/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
+	/// primary/native dispatch origin form.
+	Native,
+
+	/// Origin should just be the standard account-based origin with the sovereign account of
+	/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
+	SovereignAccount,
+
+	/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
+	/// This will not usually be an available option.
+	Superuser,
+
+	/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
+	/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
+	/// the `pallet_xcm::Origin::Xcm` type.
+	Xcm,
+}
+
+impl From<OldOriginKind> for OriginKind {
+	fn from(old: OldOriginKind) -> Self {
+		use OldOriginKind::*;
+		match old {
+			Native => Self::Native,
+			SovereignAccount => Self::SovereignAccount,
+			Superuser => Self::Superuser,
+			Xcm => Self::Xcm,
+		}
+	}
+}
 
 /// This module's XCM version.
 pub const VERSION: super::Version = 3;
@@ -1310,6 +1344,7 @@ impl TryFrom<OldResponse> for Response {
 }
 
 // Convert from a v2 XCM to a v3 XCM.
+#[allow(deprecated)]
 impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
 	type Error = ();
 	fn try_from(old_xcm: OldXcm<Call>) -> result::Result<Self, ()> {
@@ -1500,7 +1535,7 @@ impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
 			HrmpChannelClosing { initiator, sender, recipient } =>
 				Self::HrmpChannelClosing { initiator, sender, recipient },
 			Transact { origin_type, require_weight_at_most, call } => Self::Transact {
-				origin_kind: origin_type,
+				origin_kind: origin_type.into(),
 				require_weight_at_most: Weight::from_parts(
 					require_weight_at_most,
 					DEFAULT_PROOF_SIZE,
@@ -1572,118 +1607,6 @@ impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
 #[cfg(test)]
 mod tests {
 	use super::{prelude::*, *};
-	use crate::v2::{
-		Junctions::Here as OldHere, MultiAssetFilter as OldMultiAssetFilter,
-		WildMultiAsset as OldWildMultiAsset,
-	};
-
-	#[test]
-	fn basic_roundtrip_works() {
-		let xcm = Xcm::<()>(vec![TransferAsset {
-			assets: (Here, 1u128).into(),
-			beneficiary: Here.into(),
-		}]);
-		let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset {
-			assets: (OldHere, 1).into(),
-			beneficiary: OldHere.into(),
-		}]);
-		assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
-		let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
-		assert_eq!(new_xcm, xcm);
-	}
-
-	#[test]
-	fn teleport_roundtrip_works() {
-		let xcm = Xcm::<()>(vec![
-			ReceiveTeleportedAsset((Here, 1u128).into()),
-			ClearOrigin,
-			DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
-		]);
-		let old_xcm: OldXcm<()> = OldXcm::<()>(vec![
-			OldInstruction::ReceiveTeleportedAsset((OldHere, 1).into()),
-			OldInstruction::ClearOrigin,
-			OldInstruction::DepositAsset {
-				assets: crate::v2::MultiAssetFilter::Wild(crate::v2::WildMultiAsset::All),
-				max_assets: 1,
-				beneficiary: OldHere.into(),
-			},
-		]);
-		assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
-		let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
-		assert_eq!(new_xcm, xcm);
-	}
-
-	#[test]
-	fn reserve_deposit_roundtrip_works() {
-		let xcm = Xcm::<()>(vec![
-			ReserveAssetDeposited((Here, 1u128).into()),
-			ClearOrigin,
-			BuyExecution {
-				fees: (Here, 1u128).into(),
-				weight_limit: Some(Weight::from_parts(1, DEFAULT_PROOF_SIZE)).into(),
-			},
-			DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
-		]);
-		let old_xcm = OldXcm::<()>(vec![
-			OldInstruction::ReserveAssetDeposited((OldHere, 1).into()),
-			OldInstruction::ClearOrigin,
-			OldInstruction::BuyExecution {
-				fees: (OldHere, 1).into(),
-				weight_limit: Some(1).into(),
-			},
-			OldInstruction::DepositAsset {
-				assets: crate::v2::MultiAssetFilter::Wild(crate::v2::WildMultiAsset::All),
-				max_assets: 1,
-				beneficiary: OldHere.into(),
-			},
-		]);
-		assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
-		let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
-		assert_eq!(new_xcm, xcm);
-	}
-
-	#[test]
-	fn deposit_asset_roundtrip_works() {
-		let xcm = Xcm::<()>(vec![
-			WithdrawAsset((Here, 1u128).into()),
-			DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
-		]);
-		let old_xcm = OldXcm::<()>(vec![
-			OldInstruction::WithdrawAsset((OldHere, 1).into()),
-			OldInstruction::DepositAsset {
-				assets: OldMultiAssetFilter::Wild(OldWildMultiAsset::All),
-				max_assets: 1,
-				beneficiary: OldHere.into(),
-			},
-		]);
-		assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
-		let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
-		assert_eq!(new_xcm, xcm);
-	}
-
-	#[test]
-	fn deposit_reserve_asset_roundtrip_works() {
-		let xcm = Xcm::<()>(vec![
-			WithdrawAsset((Here, 1u128).into()),
-			DepositReserveAsset {
-				assets: Wild(AllCounted(1)),
-				dest: Here.into(),
-				xcm: Xcm::<()>(vec![]),
-			},
-		]);
-		let old_xcm = OldXcm::<()>(vec![
-			OldInstruction::WithdrawAsset((OldHere, 1).into()),
-			OldInstruction::DepositReserveAsset {
-				assets: OldMultiAssetFilter::Wild(OldWildMultiAsset::All),
-				max_assets: 1,
-				dest: OldHere.into(),
-				xcm: OldXcm::<()>(vec![]),
-			},
-		]);
-		assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
-		let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
-		assert_eq!(new_xcm, xcm);
-	}
 
 	#[test]
 	fn decoding_respects_limit() {
diff --git a/polkadot/xcm/src/v3/traits.rs b/polkadot/xcm/src/v3/traits.rs
index cfe387df1a8..680e0bacd0c 100644
--- a/polkadot/xcm/src/v3/traits.rs
+++ b/polkadot/xcm/src/v3/traits.rs
@@ -25,6 +25,11 @@ pub use sp_weights::Weight;
 
 use super::*;
 
+// A simple trait to get the weight of some object.
+pub trait GetWeight<W> {
+	fn weight(&self) -> sp_weights::Weight;
+}
+
 /// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM
 /// format. Those trailing are merely part of the XCM implementation; there is no expectation that
 /// they will retain the same index over time.
diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs
index 77b6d915fcb..e1ca60087b1 100644
--- a/polkadot/xcm/src/v4/mod.rs
+++ b/polkadot/xcm/src/v4/mod.rs
@@ -16,7 +16,7 @@
 
 //! Version 4 of the Cross-Consensus Message format data structures.
 
-pub use super::v2::GetWeight;
+pub use super::v3::GetWeight;
 use super::v3::{
 	Instruction as OldInstruction, PalletInfo as OldPalletInfo,
 	QueryResponseInfo as OldQueryResponseInfo, Response as OldResponse, Xcm as OldXcm,
diff --git a/polkadot/xcm/xcm-builder/src/process_xcm_message.rs b/polkadot/xcm/xcm-builder/src/process_xcm_message.rs
index 7760274f6e2..449cda3d232 100644
--- a/polkadot/xcm/xcm-builder/src/process_xcm_message.rs
+++ b/polkadot/xcm/xcm-builder/src/process_xcm_message.rs
@@ -124,7 +124,7 @@ mod tests {
 	};
 	use parity_scale_codec::Encode;
 	use polkadot_test_runtime::*;
-	use xcm::{v2, v3, VersionedXcm};
+	use xcm::{v3, v4, VersionedXcm};
 
 	const ORIGIN: Junction = Junction::OnlyChild;
 	/// The processor to use for tests.
@@ -134,8 +134,8 @@ mod tests {
 	#[test]
 	fn process_message_trivial_works() {
 		// ClearOrigin works.
-		assert!(process(v2_xcm(true)).unwrap());
 		assert!(process(v3_xcm(true)).unwrap());
+		assert!(process(v4_xcm(true)).unwrap());
 	}
 
 	#[test]
@@ -194,7 +194,7 @@ mod tests {
 
 	#[test]
 	fn process_message_overweight_fails() {
-		for msg in [v3_xcm(true), v3_xcm(false), v3_xcm(false), v2_xcm(false)] {
+		for msg in [v4_xcm(true), v4_xcm(false), v4_xcm(false), v3_xcm(false)] {
 			let msg = &msg.encode()[..];
 
 			// Errors if we stay below a weight limit of 1000.
@@ -216,7 +216,7 @@ mod tests {
 		}
 	}
 
-	fn v2_xcm(success: bool) -> VersionedXcm<RuntimeCall> {
+	fn v3_xcm(success: bool) -> VersionedXcm<RuntimeCall> {
 		let instr = if success {
 			v3::Instruction::<RuntimeCall>::ClearOrigin
 		} else {
@@ -225,13 +225,13 @@ mod tests {
 		VersionedXcm::V3(v3::Xcm::<RuntimeCall>(vec![instr]))
 	}
 
-	fn v3_xcm(success: bool) -> VersionedXcm<RuntimeCall> {
+	fn v4_xcm(success: bool) -> VersionedXcm<RuntimeCall> {
 		let instr = if success {
-			v2::Instruction::<RuntimeCall>::ClearOrigin
+			v4::Instruction::<RuntimeCall>::ClearOrigin
 		} else {
-			v2::Instruction::<RuntimeCall>::Trap(1)
+			v4::Instruction::<RuntimeCall>::Trap(1)
 		};
-		VersionedXcm::V2(v2::Xcm::<RuntimeCall>(vec![instr]))
+		VersionedXcm::V4(v4::Xcm::<RuntimeCall>(vec![instr]))
 	}
 
 	fn process(msg: VersionedXcm<RuntimeCall>) -> Result<bool, ProcessMessageError> {
diff --git a/prdoc/pr_4131.prdoc b/prdoc/pr_4131.prdoc
new file mode 100644
index 00000000000..b0619eabe13
--- /dev/null
+++ b/prdoc/pr_4131.prdoc
@@ -0,0 +1,26 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Deprecate XCMv2
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      XCMv2 has been deprecated. It will be removed when XCMv5 is released.
+      Use version 3 or 4 instead.
+  - audience: Runtime User
+    description: |
+      XCMv2 has been deprecated. It will be removed when XCMv5 is released.
+      Use version 3 or 4 instead.
+
+crates:
+- name: staging-xcm
+  bump: minor
+- name: xcm-procedural
+  bump: minor
+- name: staging-xcm-builder
+  bump: minor
+- name: pallet-xcm
+  bump: minor
+- name: cumulus-pallet-xcmp-queue
+  bump: minor
-- 
GitLab