Repository: paritytech/polkadot-sdk (language: rust)

Issue:
DryRunApi.dryRunCall always produces the latest XCM version report
The [`DryRunApi.dryRunCall`](https://github.com/paritytech/polkadot-sdk/blob/698d9ae5b32785d3a5a55b770e973bbdb59ad271/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs#L67) doesn't know what version to use when reporting the XCM objects back to the client.

The [common implementation in pallet-xcm](https://github.com/paritytech/polkadot-sdk/blob/698d9ae5b32785d3a5a55b770e973bbdb59ad271/polkadot/xcm/pallet-xcm/src/lib.rs#L2509-L2544) uses the latest one. If a client is not ready to interpret the newest version, it won't be able to read the output. I ran into this when testing locally.

All client-facing APIs should use versions that a client can understand (similar to the `xcmPaymentApi.queryAcceptablePaymentAssets`) so that clients won't break immediately if a chain upgrades.

The same might go for the `dryRunXcm`.




Discussion / hints:
CC @franciscoaguirre 

Fix patch:
diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs
index 91f71558b54a2..a970a0b785ce5 100644
--- a/cumulus/pallets/xcmp-queue/src/lib.rs
+++ b/cumulus/pallets/xcmp-queue/src/lib.rs
@@ -1011,6 +1011,12 @@ impl<T: Config> InspectMessageQueues for Pallet<T> {
 	fn clear_messages() {
 		// Best effort.
 		let _ = OutboundXcmpMessages::<T>::clear(u32::MAX, None);
+		OutboundXcmpStatus::<T>::mutate(|details_vec| {
+			for details in details_vec {
+				details.first_index = 0;
+				details.last_index = 0;
+			}
+		});
 	}
 
 	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
index f9629e126a405..6835267e93680 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
@@ -104,7 +104,8 @@ use xcm::latest::prelude::{
 };
 use xcm::{
 	latest::prelude::{AssetId, BodyId},
-	VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm,
+	Version as XcmVersion, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation,
+	VersionedXcm,
 };
 use xcm_runtime_apis::{
 	dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
@@ -1470,8 +1471,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
index fcd7c4696c95b..514187e12ceb6 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
@@ -99,6 +99,7 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
 use xcm::{
 	latest::prelude::AssetId,
 	prelude::{VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm},
+	Version as XcmVersion,
 };
 
 #[cfg(feature = "runtime-benchmarks")]
@@ -1587,8 +1588,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
index 3d03d204f7a48..961ecf1c6edae 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
@@ -95,7 +95,7 @@ use snowbridge_core::{
 	outbound::{Command, Fee},
 	AgentId, PricingParameters,
 };
-use xcm::{latest::prelude::*, prelude::*};
+use xcm::{latest::prelude::*, prelude::*, Version as XcmVersion};
 use xcm_runtime_apis::{
 	dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
 	fees::Error as XcmPaymentApiError,
@@ -872,8 +872,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
index 5d0069393a63f..90cf2e85aa40e 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
@@ -103,7 +103,7 @@ use snowbridge_core::{
 	AgentId, PricingParameters,
 };
 use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*};
-use xcm::VersionedLocation;
+use xcm::{Version as XcmVersion, VersionedLocation};
 
 use westend_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID};
 
@@ -804,8 +804,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
index d4365713072c4..f1763445d470c 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
@@ -107,7 +107,7 @@ use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
 use polkadot_runtime_common::{
 	impls::VersionedLocatableAsset, BlockHashCount, SlowAdjustingFeeUpdate,
 };
-use xcm::prelude::*;
+use xcm::{prelude::*, Version as XcmVersion};
 use xcm_runtime_apis::{
 	dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
 	fees::Error as XcmPaymentApiError,
@@ -987,8 +987,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
index f84f04edf5a1b..6d6c57d554510 100644
--- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
@@ -64,7 +64,7 @@ use parachains_common::{
 };
 pub use parachains_common::{AuraId, Balance};
 use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*};
-use xcm::prelude::*;
+use xcm::{prelude::*, Version as XcmVersion};
 use xcm_config::CollatorSelectionUpdateOrigin;
 use xcm_runtime_apis::{
 	dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
@@ -630,8 +630,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs
index 8f71336495c51..d9e73d0694ad1 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs
@@ -77,7 +77,7 @@ use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
 use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*};
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
-use xcm::prelude::*;
+use xcm::{prelude::*, Version as XcmVersion};
 use xcm_config::{
 	FellowshipLocation, GovernanceLocation, RocRelayLocation, XcmOriginToTransactDispatchOrigin,
 };
@@ -839,8 +839,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs
index dfa72379f657a..47dd9ba5627b9 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs
@@ -77,7 +77,7 @@ use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
 use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*};
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
-use xcm::prelude::*;
+use xcm::{prelude::*, Version as XcmVersion};
 use xcm_config::{
 	FellowshipLocation, GovernanceLocation, TokenRelayLocation, XcmOriginToTransactDispatchOrigin,
 };
@@ -831,8 +831,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs
index 6c8b9e514bdfb..7358402a9f781 100644
--- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs
@@ -69,7 +69,7 @@ use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
 use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*};
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
-use xcm::prelude::*;
+use xcm::{prelude::*, Version as XcmVersion};
 use xcm_config::{
 	FellowshipLocation, GovernanceLocation, PriceForSiblingParachainDelivery, XcmConfig,
 	XcmOriginToTransactDispatchOrigin,
@@ -807,8 +807,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs
index 1c0ad10c581a6..7e1aceb8bcc07 100644
--- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs
@@ -69,7 +69,7 @@ use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
 use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*};
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
-use xcm::prelude::*;
+use xcm::{prelude::*, Version as XcmVersion};
 use xcm_config::{
 	FellowshipLocation, GovernanceLocation, PriceForSiblingParachainDelivery, XcmConfig,
 	XcmOriginToTransactDispatchOrigin,
@@ -805,8 +805,8 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs
index d8f147a9cf7b5..4e31c72d334f7 100644
--- a/polkadot/node/service/src/fake_runtime_api.rs
+++ b/polkadot/node/service/src/fake_runtime_api.rs
@@ -44,7 +44,9 @@ use sp_runtime::{
 use sp_version::RuntimeVersion;
 use sp_weights::Weight;
 use std::collections::BTreeMap;
-use xcm::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm};
+use xcm::{
+	Version as XcmVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm,
+};
 sp_api::decl_runtime_apis! {
 	/// This runtime API is only implemented for the test runtime!
 	pub trait GetLastTimestamp {
@@ -447,7 +449,7 @@ sp_api::impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, (), (), ()> for Runtime {
-		fn dry_run_call(_: (), _: ()) -> Result<xcm_runtime_apis::dry_run::CallDryRunEffects<()>, xcm_runtime_apis::dry_run::Error> {
+		fn dry_run_call(_: (), _: (), _: XcmVersion) -> Result<xcm_runtime_apis::dry_run::CallDryRunEffects<()>, xcm_runtime_apis::dry_run::Error> {
 			unimplemented!()
 		}
 
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index 4a9d429a01ccb..a16d993cda546 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -113,8 +113,8 @@ use sp_staking::SessionIndex;
 use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
 use xcm::{
-	latest::prelude::*, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation,
-	VersionedXcm,
+	latest::prelude::*, Version as XcmVersion, VersionedAsset, VersionedAssetId, VersionedAssets,
+	VersionedLocation, VersionedXcm,
 };
 use xcm_builder::PayOverXcm;
 
@@ -1895,8 +1895,8 @@ sp_api::impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			XcmPallet::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			XcmPallet::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index 25e7e653d220a..800886fe6a0c3 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -109,8 +109,8 @@ use sp_staking::SessionIndex;
 use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
 use xcm::{
-	latest::prelude::*, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation,
-	VersionedXcm,
+	latest::prelude::*, Version as XcmVersion, VersionedAsset, VersionedAssetId, VersionedAssets,
+	VersionedLocation, VersionedXcm,
 };
 use xcm_builder::PayOverXcm;
 
@@ -2465,8 +2465,8 @@ sp_api::impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			XcmPallet::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call)
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			XcmPallet::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs
index 5a75e4a1d0d3b..4f7ec0daf3ef7 100644
--- a/polkadot/xcm/pallet-xcm/src/lib.rs
+++ b/polkadot/xcm/pallet-xcm/src/lib.rs
@@ -2513,6 +2513,7 @@ impl<T: Config> Pallet<T> {
 	pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
 		origin: OriginCaller,
 		call: RuntimeCall,
+		result_xcms_version: XcmVersion,
 	) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
 	where
 		Runtime: crate::Config,
@@ -2527,9 +2528,28 @@ impl<T: Config> Pallet<T> {
 		frame_system::Pallet::<Runtime>::reset_events();
 		let result = call.dispatch(origin.into());
 		crate::Pallet::<Runtime>::set_record_xcm(false);
-		let local_xcm = crate::Pallet::<Runtime>::recorded_xcm();
+		let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
+			.map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
+			.transpose()
+			.map_err(|()| {
+				tracing::error!(
+					target: "xcm::DryRunApi::dry_run_call",
+					"Local xcm version conversion failed"
+				);
+
+				XcmDryRunApiError::VersionedConversionFailed
+			})?;
+
 		// Should only get messages from this call since we cleared previous ones.
-		let forwarded_xcms = Router::get_messages();
+		let forwarded_xcms =
+			Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
+				|error| {
+					tracing::error!(
+						target: "xcm::DryRunApi::dry_run_call",
+						?error, "Forwarded xcms version conversion failed with error"
+					);
+				},
+			)?;
 		let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
 			frame_system::Pallet::<Runtime>::read_events_no_consensus()
 				.map(|record| record.event.clone())
@@ -2562,6 +2582,7 @@ impl<T: Config> Pallet<T> {
 			);
 			XcmDryRunApiError::VersionedConversionFailed
 		})?;
+		let xcm_version = xcm.identify_version();
 		let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
 			tracing::error!(
 				target: "xcm::DryRunApi::dry_run_xcm",
@@ -2570,7 +2591,11 @@ impl<T: Config> Pallet<T> {
 			XcmDryRunApiError::VersionedConversionFailed
 		})?;
 		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
-		frame_system::Pallet::<Runtime>::reset_events(); // To make sure we only record events from current call.
+
+		// To make sure we only record events from current call.
+		Router::clear_messages();
+		frame_system::Pallet::<Runtime>::reset_events();
+
 		let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
 			origin_location,
 			xcm,
@@ -2578,7 +2603,13 @@ impl<T: Config> Pallet<T> {
 			Weight::MAX, // Max limit available for execution.
 			Weight::zero(),
 		);
-		let forwarded_xcms = Router::get_messages();
+		let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
+			.inspect_err(|error| {
+				tracing::error!(
+					target: "xcm::DryRunApi::dry_run_xcm",
+					?error, "Forwarded xcms version conversion failed with error"
+				);
+			})?;
 		let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
 			frame_system::Pallet::<Runtime>::read_events_no_consensus()
 				.map(|record| record.event.clone())
@@ -2586,6 +2617,31 @@ impl<T: Config> Pallet<T> {
 		Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
 	}
 
+	fn convert_xcms(
+		xcm_version: XcmVersion,
+		xcms: Vec<VersionedXcm<()>>,
+	) -> Result<Vec<VersionedXcm<()>>, ()> {
+		xcms.into_iter()
+			.map(|xcm| xcm.into_version(xcm_version))
+			.collect::<Result<Vec<_>, ()>>()
+	}
+
+	fn convert_forwarded_xcms(
+		xcm_version: XcmVersion,
+		forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
+	) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
+		forwarded_xcms
+			.into_iter()
+			.map(|(dest, forwarded_xcms)| {
+				let dest = dest.into_version(xcm_version)?;
+				let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
+
+				Ok((dest, forwarded_xcms))
+			})
+			.collect::<Result<Vec<_>, ()>>()
+			.map_err(|()| XcmDryRunApiError::VersionedConversionFailed)
+	}
+
 	/// Given a list of asset ids, returns the correct API response for
 	/// `XcmPaymentApi::query_acceptable_payment_assets`.
 	///
diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs
index a41a8e797b0f7..b88b65ad6c768 100644
--- a/polkadot/xcm/src/lib.rs
+++ b/polkadot/xcm/src/lib.rs
@@ -522,6 +522,9 @@ pub mod prelude {
 		VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation,
 		VersionedResponse, VersionedXcm, WrapVersion,
 	};
+
+	/// The minimal supported XCM version
+	pub const MIN_XCM_VERSION: XcmVersion = 3;
 }
 
 pub mod opaque {
diff --git a/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs b/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs
index f0a70b0dacfe1..87ec94db54e93 100644
--- a/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs
+++ b/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs
@@ -57,13 +57,18 @@ sp_api::decl_runtime_apis! {
 	/// Calls or XCMs might fail when executed, this doesn't mean the result of these calls will be an `Err`.
 	/// In those cases, there might still be a valid result, with the execution error inside it.
 	/// The only reasons why these calls might return an error are listed in the [`Error`] enum.
+	#[api_version(2)]
 	pub trait DryRunApi<Call, Event, OriginCaller>
 	where
 		Call: Encode,
 		Event: Decode,
 		OriginCaller: Encode
 	{
-		/// Dry run call.
+		/// Dry run call V2.
+		fn dry_run_call(origin: OriginCaller, call: Call, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<Event>, Error>;
+
+		/// Dry run call V1.
+		#[changed_in(2)]
 		fn dry_run_call(origin: OriginCaller, call: Call) -> Result<CallDryRunEffects<Event>, Error>;
 
 		/// Dry run XCM program
diff --git a/prdoc/pr_7689.prdoc b/prdoc/pr_7689.prdoc
new file mode 100644
index 0000000000000..abeb36eca4d76
--- /dev/null
+++ b/prdoc/pr_7689.prdoc
@@ -0,0 +1,50 @@
+title: Fix DryRunApi client-facing XCM versions
+
+doc:
+  - audience: Runtime Dev
+    description: |-
+      Fixes https://github.com/paritytech/polkadot-sdk/issues/7413
+
+      This PR updates the DryRunApi. The signature of the dry_run_call is changed, and the XCM version of the return values of dry_run_xcm now follows the version of the input XCM program.
+
+      It also fixes xcmp-queue's Router's `clear_messages`: the channel details `first_index` and `last_index` are reset when clearing.
+
+crates:
+- name: xcm-runtime-apis
+  bump: patch
+- name: staging-xcm
+  bump: patch
+- name: pallet-xcm
+  bump: minor
+- name: polkadot-service
+  bump: patch
+- name: cumulus-pallet-xcmp-queue
+  bump: patch
+- name: westend-runtime
+  bump: patch
+- name: rococo-runtime
+  bump: patch
+- name: emulated-integration-tests-common
+  bump: patch
+- name: asset-hub-rococo-runtime
+  bump: patch
+- name: asset-hub-westend-runtime
+  bump: patch
+- name: bridge-hub-rococo-runtime
+  bump: patch
+- name: bridge-hub-westend-runtime
+  bump: patch
+- name: collectives-westend-runtime
+  bump: patch
+- name: coretime-rococo-runtime
+  bump: patch
+- name: coretime-westend-runtime
+  bump: patch
+- name: people-rococo-runtime
+  bump: patch
+- name: people-westend-runtime
+  bump: patch
+- name: penpal-runtime
+  bump: patch
+- name: contracts-rococo-runtime
+  bump: patch


Test patch:
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs
index 5b40f681616b2..9b28ce418648e 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs
@@ -490,7 +490,7 @@ macro_rules! test_can_estimate_and_pay_exact_fees {
 					(Parent, 100_000_000_000u128),
 				);
 				let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
-				let result = Runtime::dry_run_call(origin, call).unwrap();
+				let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap();
 				let local_xcm = result.local_xcm.unwrap().clone();
 				let local_xcm_weight = Runtime::query_xcm_weight(local_xcm).unwrap();
 				local_execution_fees = Runtime::query_weight_to_asset_fee(
@@ -635,7 +635,7 @@ macro_rules! test_dry_run_transfer_across_pk_bridge {
 			use frame_support::{dispatch::RawOrigin, traits::fungible};
 			use sp_runtime::AccountId32;
 			use xcm::prelude::*;
-			use xcm_runtime_apis::dry_run::runtime_decl_for_dry_run_api::DryRunApiV1;
+			use xcm_runtime_apis::dry_run::runtime_decl_for_dry_run_api::DryRunApiV2;
 
 			let who = AccountId32::new([1u8; 32]);
 			let transfer_amount = 10_000_000_000_000u128;
@@ -665,7 +665,7 @@ macro_rules! test_dry_run_transfer_across_pk_bridge {
 					fee_asset_item: 0,
 					weight_limit: Unlimited,
 				});
-				let result = Runtime::dry_run_call(OriginCaller::system(RawOrigin::Signed(who)), call).unwrap();
+				let result = Runtime::dry_run_call(OriginCaller::system(RawOrigin::Signed(who)), call, XCM_VERSION).unwrap();
 				// We assert the dry run succeeds and sends only one message to the local bridge hub.
 				assert!(result.execution_result.is_ok());
 				assert_eq!(result.forwarded_xcms.len(), 1);
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs
index ea210d4f3b65e..36a3786cd364a 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs
@@ -19,7 +19,7 @@ use crate::imports::*;
 use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees;
 use frame_support::dispatch::RawOrigin;
 use xcm_runtime_apis::{
-	dry_run::runtime_decl_for_dry_run_api::DryRunApiV1,
+	dry_run::runtime_decl_for_dry_run_api::DryRunApiV2,
 	fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1,
 };
 
@@ -146,7 +146,7 @@ fn multi_hop_works() {
 
 		let call = transfer_assets_para_to_para_through_ah_call(test.clone());
 		let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
-		let result = Runtime::dry_run_call(origin, call).unwrap();
+		let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap();
 		// We filter the result to get only the messages we are interested in.
 		let (destination_to_query, messages_to_query) = &result
 			.forwarded_xcms
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs
index ec05a074c5acf..822de21fa4f64 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs
@@ -20,7 +20,7 @@ use crate::imports::*;
 use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees;
 use frame_support::dispatch::RawOrigin;
 use xcm_runtime_apis::{
-	dry_run::runtime_decl_for_dry_run_api::DryRunApiV1,
+	dry_run::runtime_decl_for_dry_run_api::DryRunApiV2,
 	fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1,
 };
 
@@ -148,7 +148,7 @@ fn multi_hop_works() {
 
 		let call = transfer_assets_para_to_para_through_ah_call(test.clone());
 		let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
-		let result = Runtime::dry_run_call(origin, call).unwrap();
+		let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap();
 		// We filter the result to get only the messages we are interested in.
 		let (destination_to_query, messages_to_query) = &result
 			.forwarded_xcms
diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs
index b51670c792d6b..2e51eb8c03669 100644
--- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs
+++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs
@@ -40,7 +40,6 @@ use assets_common::{
 	local_and_foreign_assets::{LocalFromLeft, TargetFromLeft},
 	AssetIdForTrustBackedAssetsConvert,
 };
-use codec::Encode;
 use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
 use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId};
 use frame_support::{
@@ -75,7 +74,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
 use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
 use sp_runtime::{
 	generic, impl_opaque_keys,
-	traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable},
+	traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT},
 	transaction_validity::{TransactionSource, TransactionValidity},
 	ApplyExtrinsicResult,
 };
@@ -93,7 +92,8 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
 use xcm::{
 	latest::prelude::{AssetId as AssetLocationId, BodyId},
-	VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm,
+	Version as XcmVersion, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation,
+	VersionedXcm,
 };
 use xcm_runtime_apis::{
 	dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
@@ -1023,61 +1023,12 @@ impl_runtime_apis! {
 	}
 
 	impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			use xcm_builder::InspectMessageQueues;
-			use xcm_executor::RecordXcm;
-			use xcm::prelude::*;
-			pallet_xcm::Pallet::<Runtime>::set_record_xcm(true);
-			frame_system::Pallet::<Runtime>::reset_events(); // To make sure we only record events from current call.
-			let result = call.dispatch(origin.into());
-			pallet_xcm::Pallet::<Runtime>::set_record_xcm(false);
-			let local_xcm = pallet_xcm::Pallet::<Runtime>::recorded_xcm();
-			let forwarded_xcms = xcm_config::XcmRouter::get_messages();
-			let events: Vec<RuntimeEvent> = System::read_events_no_consensus().map(|record| record.event.clone()).collect();
-			Ok(CallDryRunEffects {
-				local_xcm: local_xcm.map(VersionedXcm::<()>::from),
-				forwarded_xcms,
-				emitted_events: events,
-				execution_result: result,
-			})
+		fn dry_run_call(origin: OriginCaller, call: RuntimeCall, result_xcms_version: XcmVersion) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
 		}
 
-		fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			use xcm_builder::InspectMessageQueues;
-			use xcm::prelude::*;
-
-			let origin_location: Location = origin_location.try_into().map_err(|error| {
-				log::error!(
-					target: "xcm::DryRunApi::dry_run_xcm",
-					"Location version conversion failed with error: {:?}",
-					error,
-				);
-				XcmDryRunApiError::VersionedConversionFailed
-			})?;
-			let program: Xcm<RuntimeCall> = program.try_into().map_err(|error| {
-				log::error!(
-					target: "xcm::DryRunApi::dry_run_xcm",
-					"Xcm version conversion failed with error {:?}",
-					error,
-				);
-				XcmDryRunApiError::VersionedConversionFailed
-			})?;
-			let mut hash = program.using_encoded(sp_core::hashing::blake2_256);
-			frame_system::Pallet::<Runtime>::reset_events(); // To make sure we only record events from current call.
-			let result = xcm_executor::XcmExecutor::<xcm_config::XcmConfig>::prepare_and_execute(
-				origin_location,
-				program,
-				&mut hash,
-				Weight::MAX, // Max limit.
-				Weight::zero(),
-			);
-			let forwarded_xcms = xcm_config::XcmRouter::get_messages();
-			let events: Vec<RuntimeEvent> = System::read_events_no_consensus().map(|record| record.event.clone()).collect();
-			Ok(XcmDryRunEffects {
-				forwarded_xcms,
-				emitted_events: events,
-				execution_result: result,
-			})
+		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			PolkadotXcm::dry_run_xcm::<Runtime, xcm_config::XcmRouter, RuntimeCall, xcm_config::XcmConfig>(origin_location, xcm)
 		}
 	}
 
diff --git a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs
index 2d14b4e571c67..5c0ada410dff0 100644
--- a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs
+++ b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs
@@ -20,7 +20,10 @@ use frame_support::sp_runtime::testing::H256;
 use frame_system::RawOrigin;
 use sp_api::ProvideRuntimeApi;
 use xcm::prelude::*;
-use xcm_runtime_apis::{dry_run::DryRunApi, fees::XcmPaymentApi};
+use xcm_runtime_apis::{
+	dry_run::{CallDryRunEffects, DryRunApi},
+	fees::XcmPaymentApi,
+};
 
 mod mock;
 use mock::{
@@ -62,8 +65,10 @@ fn fee_estimation_for_teleport() {
 			weight_limit: Unlimited,
 		});
 		let origin = OriginCaller::system(RawOrigin::Signed(who));
-		let dry_run_effects =
-			runtime_api.dry_run_call(H256::zero(), origin, call).unwrap().unwrap();
+		let dry_run_effects = runtime_api
+			.dry_run_call(H256::zero(), origin, call, XCM_VERSION)
+			.unwrap()
+			.unwrap();
 
 		assert_eq!(
 			dry_run_effects.local_xcm,
@@ -193,8 +198,11 @@ fn fee_estimation_for_teleport() {
 //                 Reserve Asset Transfer Relay Token
 //                 Reserve Asset Transfer Relay Token for fees
 // Parachain(2000) -------------------------------------------> Parachain(1000)
-#[test]
-fn dry_run_reserve_asset_transfer() {
+fn dry_run_reserve_asset_transfer_common(
+	input_xcm_version: XcmVersion,
+	expected_result_xcms_version: XcmVersion,
+	dry_run_call: impl FnOnce(&TestClient, OriginCaller, RuntimeCall) -> CallDryRunEffects<RuntimeEvent>,
+) {
 	sp_tracing::init_for_tests();
 	let who = 1; // AccountId = u64.
 			  // Native token used for fees.
@@ -202,30 +210,40 @@ fn dry_run_reserve_asset_transfer() {
 	// Relay token is the one we want to transfer.
 	let assets = vec![(1, who, 100)]; // id, account_id, balance.
 	new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| {
-		let client = TestClient;
-		let runtime_api = client.runtime_api();
 		let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets {
-			dest: Box::new(VersionedLocation::from((Parent, Parachain(1000)))),
-			beneficiary: Box::new(VersionedLocation::from(AccountId32 {
-				id: [0u8; 32],
-				network: None,
-			})),
-			assets: Box::new(VersionedAssets::from((Parent, 100u128))),
+			dest: Box::new(
+				VersionedLocation::from((Parent, Parachain(1000)))
+					.into_version(input_xcm_version)
+					.unwrap(),
+			),
+			beneficiary: Box::new(
+				VersionedLocation::from(AccountId32 { id: [0u8; 32], network: None })
+					.into_version(input_xcm_version)
+					.unwrap(),
+			),
+			assets: Box::new(
+				VersionedAssets::from((Parent, 100u128))
+					.into_version(input_xcm_version)
+					.unwrap(),
+			),
 			fee_asset_item: 0,
 			weight_limit: Unlimited,
 		});
 		let origin = OriginCaller::system(RawOrigin::Signed(who));
-		let dry_run_effects =
-			runtime_api.dry_run_call(H256::zero(), origin, call).unwrap().unwrap();
+		let dry_run_effects = dry_run_call(&TestClient, origin, call);
 
 		assert_eq!(
 			dry_run_effects.local_xcm,
-			Some(VersionedXcm::from(
-				Xcm::builder_unsafe()
-					.withdraw_asset((Parent, 100u128))
-					.burn_asset((Parent, 100u128))
-					.build()
-			)),
+			Some(
+				VersionedXcm::from(
+					Xcm::builder_unsafe()
+						.withdraw_asset((Parent, 100u128))
+						.burn_asset((Parent, 100u128))
+						.build()
+				)
+				.into_version(expected_result_xcms_version)
+				.unwrap()
+			),
 		);
 
 		// In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws
@@ -240,8 +258,12 @@ fn dry_run_reserve_asset_transfer() {
 		assert_eq!(
 			dry_run_effects.forwarded_xcms,
 			vec![(
-				VersionedLocation::from(send_destination.clone()),
-				vec![VersionedXcm::from(send_message.clone())],
+				VersionedLocation::from(send_destination.clone())
+					.into_version(expected_result_xcms_version)
+					.unwrap(),
+				vec![VersionedXcm::from(send_message.clone())
+					.into_version(expected_result_xcms_version)
+					.unwrap()],
 			),],
 		);
 
@@ -273,7 +295,49 @@ fn dry_run_reserve_asset_transfer() {
 }
 
 #[test]
-fn dry_run_xcm() {
+fn dry_run_reserve_asset_transfer_xcm_versions() {
+	let tested_versions = MIN_XCM_VERSION..=XCM_VERSION;
+
+	for version in tested_versions {
+		let input_version = version;
+		let expected_result_xcms_version = version;
+		dry_run_reserve_asset_transfer_common(
+			input_version,
+			expected_result_xcms_version,
+			|client, origin, call| {
+				client
+					.runtime_api()
+					.dry_run_call(H256::zero(), origin, call, expected_result_xcms_version)
+					.unwrap()
+					.unwrap()
+			},
+		);
+	}
+}
+
+#[test]
+fn dry_run_before_api_v2_reserve_asset_transfer() {
+	let tested_versions = MIN_XCM_VERSION..=XCM_VERSION;
+
+	for version in tested_versions {
+		let input_version = version;
+		let expected_result_xcms_version = XCM_VERSION;
+		dry_run_reserve_asset_transfer_common(
+			input_version,
+			expected_result_xcms_version,
+			|client, origin, call| {
+				#[allow(deprecated)]
+				client
+					.runtime_api()
+					.dry_run_call_before_version_2(H256::zero(), origin, call)
+					.unwrap()
+					.unwrap()
+			},
+		);
+	}
+}
+
+fn dry_run_xcm_common(xcm_version: XcmVersion) {
 	sp_tracing::init_for_tests();
 	let who = 1; // AccountId = u64.
 	let transfer_amount = 100u128;
@@ -291,14 +355,19 @@ fn dry_run_xcm() {
 	let client = TestClient;
 	let runtime_api = client.runtime_api();
 	let xcm_weight = runtime_api
-		.query_xcm_weight(H256::zero(), VersionedXcm::from(xcm_to_weigh.clone().into()))
+		.query_xcm_weight(
+			H256::zero(),
+			VersionedXcm::from(xcm_to_weigh.clone().into())
+				.into_version(xcm_version)
+				.unwrap(),
+		)
 		.unwrap()
 		.unwrap();
 	let execution_fees = runtime_api
 		.query_weight_to_asset_fee(
 			H256::zero(),
 			xcm_weight,
-			VersionedAssetId::from(AssetId(Here.into())),
+			VersionedAssetId::from(AssetId(Here.into())).into_version(xcm_version).unwrap(),
 		)
 		.unwrap()
 		.unwrap();
@@ -316,15 +385,19 @@ fn dry_run_xcm() {
 		let dry_run_effects = runtime_api
 			.dry_run_xcm(
 				H256::zero(),
-				VersionedLocation::from([AccountIndex64 { index: 1, network: None }]),
-				VersionedXcm::from(xcm),
+				VersionedLocation::from([AccountIndex64 { index: 1, network: None }])
+					.into_version(xcm_version)
+					.unwrap(),
+				VersionedXcm::from(xcm).into_version(xcm_version).unwrap(),
 			)
 			.unwrap()
 			.unwrap();
 		assert_eq!(
 			dry_run_effects.forwarded_xcms,
 			vec![(
-				VersionedLocation::from((Parent, Parachain(2100))),
+				VersionedLocation::from((Parent, Parachain(2100)))
+					.into_version(xcm_version)
+					.unwrap(),
 				vec![VersionedXcm::from(
 					Xcm::<()>::builder_unsafe()
 						.reserve_asset_deposited((
@@ -335,7 +408,9 @@ fn dry_run_xcm() {
 						.buy_execution((Here, 1u128), Unlimited)
 						.deposit_asset(AllCounted(1), [0u8; 32])
 						.build()
-				)],
+				)
+				.into_version(xcm_version)
+				.unwrap()],
 			),]
 		);
 
@@ -353,3 +428,35 @@ fn dry_run_xcm() {
 		);
 	});
 }
+
+#[test]
+fn dry_run_xcm_versions() {
+	let tested_versions = [XCM_VERSION, 5, 4, 3];
+
+	for version in tested_versions {
+		dry_run_xcm_common(version);
+	}
+}
+
+#[test]
+fn calling_payment_api_with_a_lower_version_works() {
+	let transfer_amount = 100u128;
+	let xcm_to_weigh = Xcm::<RuntimeCall>::builder_unsafe()
+		.withdraw_asset((Here, transfer_amount))
+		.buy_execution((Here, transfer_amount), Unlimited)
+		.deposit_asset(AllCounted(1), [1u8; 32])
+		.build();
+	let versioned_xcm_to_weigh = VersionedXcm::from(xcm_to_weigh.clone().into());
+	let lower_version_xcm_to_weigh = versioned_xcm_to_weigh.into_version(XCM_VERSION - 1).unwrap();
+	let client = TestClient;
+	let runtime_api = client.runtime_api();
+	let xcm_weight =
+		runtime_api.query_xcm_weight(H256::zero(), lower_version_xcm_to_weigh).unwrap();
+	assert!(xcm_weight.is_ok());
+	let native_token = VersionedAssetId::from(AssetId(Here.into()));
+	let lower_version_native_token = native_token.into_version(XCM_VERSION - 1).unwrap();
+	let execution_fees = runtime_api
+		.query_weight_to_asset_fee(H256::zero(), xcm_weight.unwrap(), lower_version_native_token)
+		.unwrap();
+	assert!(execution_fees.is_ok());
+}
diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs
index f0a5be908f693..75d9f09c80417 100644
--- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs
+++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs
@@ -22,7 +22,7 @@ use core::{cell::RefCell, marker::PhantomData};
 use frame_support::{
 	construct_runtime, derive_impl, parameter_types, sp_runtime,
 	sp_runtime::{
-		traits::{Dispatchable, Get, IdentityLookup, MaybeEquivalence, TryConvert},
+		traits::{Get, IdentityLookup, MaybeEquivalence, TryConvert},
 		BuildStorage, SaturatedConversion,
 	},
 	traits::{
@@ -36,8 +36,8 @@ use pallet_xcm::TestWeightInfo;
 use xcm::{prelude::*, Version as XcmVersion};
 use xcm_builder::{
 	AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible,
-	FixedWeightBounds, FungibleAdapter, FungiblesAdapter, IsConcrete, MintLocation, NoChecking,
-	TakeWeightCredit,
+	FixedWeightBounds, FungibleAdapter, FungiblesAdapter, InspectMessageQueues, IsConcrete,
+	MintLocation, NoChecking, TakeWeightCredit,
 };
 use xcm_executor::{
 	traits::{ConvertLocation, JustTry},
@@ -110,10 +110,6 @@ thread_local! {
 	pub static SENT_XCM: RefCell<Vec<(Location, Xcm<()>)>> = const { RefCell::new(Vec::new()) };
 }
 
-pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> {
-	SENT_XCM.with(|q| (*q.borrow()).clone())
-}
-
 pub struct TestXcmSender;
 impl SendXcm for TestXcmSender {
 	type Ticket = (Location, Xcm<()>);
@@ -131,6 +127,26 @@ impl SendXcm for TestXcmSender {
 		Ok(hash)
 	}
 }
+impl InspectMessageQueues for TestXcmSender {
+	fn clear_messages() {
+		SENT_XCM.with(|q| q.borrow_mut().clear());
+	}
+
+	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
+		SENT_XCM.with(|q| {
+			(*q.borrow())
+				.clone()
+				.iter()
+				.map(|(location, message)| {
+					(
+						VersionedLocation::from(location.clone()),
+						vec![VersionedXcm::from(message.clone())],
+					)
+				})
+				.collect()
+		})
+	}
+}
 
 pub(crate) fn fake_message_hash<Call>(message: &Xcm<Call>) -> XcmHash {
 	message.using_encoded(sp_io::hashing::blake2_256)
@@ -480,64 +496,23 @@ sp_api::mock_impl_runtime_apis! {
 	}
 
 	impl DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for RuntimeApi {
-		fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			use xcm_executor::RecordXcm;
-			pallet_xcm::Pallet::<TestRuntime>::set_record_xcm(true);
-			let result = call.dispatch(origin.into());
-			pallet_xcm::Pallet::<TestRuntime>::set_record_xcm(false);
-			let local_xcm = pallet_xcm::Pallet::<TestRuntime>::recorded_xcm();
-			let forwarded_xcms = sent_xcm()
-							   .into_iter()
-							   .map(|(location, message)| (
-									   VersionedLocation::from(location),
-									   vec![VersionedXcm::from(message)],
-							   )).collect();
-			let events: Vec<RuntimeEvent> = System::read_events_no_consensus().map(|record| record.event.clone()).collect();
-			Ok(CallDryRunEffects {
-				local_xcm: local_xcm.map(VersionedXcm::<()>::from),
-				forwarded_xcms,
-				emitted_events: events,
-				execution_result: result,
-			})
+		fn dry_run_call(
+			origin: OriginCaller,
+			call: RuntimeCall,
+			result_xcms_version: XcmVersion,
+		) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			pallet_xcm::Pallet::<TestRuntime>::dry_run_call::<TestRuntime, XcmRouter, OriginCaller, RuntimeCall>(origin, call, result_xcms_version)
+		}
+
+		fn dry_run_call_before_version_2(
+			origin: OriginCaller,
+			call: RuntimeCall,
+		) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
+			pallet_xcm::Pallet::<TestRuntime>::dry_run_call::<TestRuntime, XcmRouter, OriginCaller, RuntimeCall>(origin, call, xcm::latest::VERSION)
 		}
 
 		fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
-			let origin_location: Location = origin_location.try_into().map_err(|error| {
-				log::error!(
-					target: "xcm::DryRunApi::dry_run_xcm",
-					"Location version conversion failed with error: {:?}",
-					error,
-				);
-				XcmDryRunApiError::VersionedConversionFailed
-			})?;
-			let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
-				log::error!(
-					target: "xcm::DryRunApi::dry_run_xcm",
-					"Xcm version conversion failed with error {:?}",
-					error,
-				);
-				XcmDryRunApiError::VersionedConversionFailed
-			})?;
-			let mut hash = fake_message_hash(&xcm);
-			let result = XcmExecutor::<XcmConfig>::prepare_and_execute(
-				origin_location,
-				xcm,
-				&mut hash,
-				Weight::MAX, // Max limit available for execution.
-				Weight::zero(),
-			);
-			let forwarded_xcms = sent_xcm()
-				.into_iter()
-				.map(|(location, message)| (
-					VersionedLocation::from(location),
-					vec![VersionedXcm::from(message)],
-				)).collect();
-			let events: Vec<RuntimeEvent> = System::events().iter().map(|record| record.event.clone()).collect();
-			Ok(XcmDryRunEffects {
-				forwarded_xcms,
-				emitted_events: events,
-				execution_result: result,
-			})
+			pallet_xcm::Pallet::<TestRuntime>::dry_run_xcm::<TestRuntime, XcmRouter, RuntimeCall, XcmConfig>(origin_location, xcm)
 		}
 	}
 }