From eadfbca388176f46e63d20edbaf546d06afad1bc Mon Sep 17 00:00:00 2001
From: Ignacio Palacios <ignacio.palacios.santos@gmail.com>
Date: Thu, 10 Aug 2023 19:38:18 +0200
Subject: [PATCH] Replicate `e2e` integration test as `emulated` (#2958)

* Allow functions to work over both parachains and relay chains

* additional references

* import

* backup

* refactoring para and relay traits

* use runtime crates to build types

* decouple ProcessMessage

* decouple ProcessMessage 2

* dmp and xcmp handlers decouple

* backup

* refactor done

* common int values working

* added global ext with mutex

* works for two mutex

* single mutex and remove condvar

* global test ext done

* failing moving test_ext because relay block num

* relay_block_number issue fixed

* backup

* Test working with assertions

* assertions get Test as arg

* DispatchArgs as generic

* clean up

* backup

* teleports for asset-hub-kusama done

* improve assert_expected_events macro

* rename Test generics

* check assertions for tuples

* test assertions redone

* reserve_transfer_assets done

* send transact done

* hrmp test for paras

* hrmp channels test done

* hrmp channels test done 2

* before modifying test dispatch

* reserve tests done & Test dispatch fixed

* reserve transfer local asset

* force_create_and_mint_asset

* force create and mint done

* tests done

* fix imports in common

* common events refactored

* add option to events attributes

* asset-hub-polkadot tests done

* asset-hub-westend half done

* relay chain events move to common

* remove failing send tests for asset-hub-westend

* added events to bridge-hub-rococo

* added events to collectives-polkadot

* cargo clean up

* fix asset-hub-westend tests

* ".git/.scripts/commands/fmt/fmt.sh"

* fix clippy

* ".git/.scripts/commands/fmt/fmt.sh"

* Removed unnecessary deps

* Extracted some commonality for Kusama/Polkadot (which will be reused also for BridgeHubs) (#2971)

* Extracted some commonality for Kusama/Polkadot (which will be reused also for BridgeHubs)

* AssetHubRococo should better use AssetHubKusama runtime

* add fund_account

---------

Co-authored-by: NachoPal <ignacio.palacios.santos@gmail.com>

* address comments

* rename event assertion helpers

* clean comments

* address comments 2

* ".git/.scripts/commands/fmt/fmt.sh"

---------

Co-authored-by: Giles Cope <gilescope@gmail.com>
Co-authored-by: command-bot <>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
---
 cumulus/Cargo.lock                            |   38 +-
 .../assets/asset-hub-kusama/Cargo.toml        |    5 +-
 .../assets/asset-hub-kusama/src/lib.rs        |   74 +-
 .../src/tests/hrmp_channels.rs                |  201 ++++
 .../assets/asset-hub-kusama/src/tests/mod.rs  |    4 +-
 .../src/tests/reserve_transfer.rs             |  451 ++++++-
 .../assets/asset-hub-kusama/src/tests/send.rs |  196 +++
 .../src/tests/set_xcm_versions.rs             |   94 ++
 .../asset-hub-kusama/src/tests/teleport.rs    |  402 ++++++-
 .../asset-hub-kusama/src/tests/transact.rs    |   74 --
 .../assets/asset-hub-polkadot/Cargo.toml      |    5 +-
 .../assets/asset-hub-polkadot/src/lib.rs      |   78 +-
 .../src/tests/hrmp_channels.rs                |  201 ++++
 .../asset-hub-polkadot/src/tests/mod.rs       |    4 +-
 .../src/tests/reserve_transfer.rs             |  451 ++++++-
 .../asset-hub-polkadot/src/tests/send.rs      |  202 ++++
 .../src/tests/set_xcm_versions.rs             |   97 ++
 .../asset-hub-polkadot/src/tests/teleport.rs  |  400 ++++++-
 .../asset-hub-polkadot/src/tests/transact.rs  |   74 --
 .../assets/asset-hub-westend/Cargo.toml       |    5 +-
 .../assets/asset-hub-westend/src/lib.rs       |   75 +-
 .../assets/asset-hub-westend/src/tests/mod.rs |    3 +-
 .../src/tests/reserve_transfer.rs             |  451 ++++++-
 .../asset-hub-westend/src/tests/send.rs       |  141 +++
 .../src/tests/set_xcm_versions.rs             |   97 ++
 .../asset-hub-westend/src/tests/swap.rs       |   78 +-
 .../asset-hub-westend/src/tests/teleport.rs   |  400 ++++++-
 .../asset-hub-westend/src/tests/transact.rs   |   74 --
 .../bridges/bridge-hub-rococo/Cargo.toml      |    2 +-
 .../bridges/bridge-hub-rococo/src/lib.rs      |   62 +-
 .../bridge-hub-rococo/src/tests/example.rs    |   10 +-
 .../collectives-polkadot/Cargo.toml           |    3 +-
 .../collectives-polkadot/src/lib.rs           |   91 +-
 .../src/tests/fellowship.rs                   |   12 +-
 .../emulated/common/Cargo.toml                |    6 +
 .../emulated/common/src/constants.rs          |  108 +-
 .../emulated/common/src/impls.rs              |  481 +++++++-
 .../emulated/common/src/lib.rs                |  446 ++++---
 cumulus/xcm/xcm-emulator/Cargo.toml           |    7 +-
 cumulus/xcm/xcm-emulator/src/lib.rs           | 1051 +++++++++++------
 40 files changed, 5349 insertions(+), 1305 deletions(-)
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/hrmp_channels.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/send.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/set_xcm_versions.rs
 delete mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/transact.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/hrmp_channels.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/send.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/set_xcm_versions.rs
 delete mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/transact.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/set_xcm_versions.rs
 delete mode 100644 cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/transact.rs

diff --git a/cumulus/Cargo.lock b/cumulus/Cargo.lock
index 7164f6b25b3..fdaecced8fc 100644
--- a/cumulus/Cargo.lock
+++ b/cumulus/Cargo.lock
@@ -447,7 +447,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
 name = "asset-hub-kusama-integration-tests"
 version = "1.0.0"
 dependencies = [
- "asset-hub-kusama-runtime",
+ "cumulus-pallet-dmp-queue",
+ "cumulus-pallet-parachain-system",
+ "cumulus-pallet-xcmp-queue",
  "frame-support",
  "frame-system",
  "integration-tests-common",
@@ -456,7 +458,6 @@ dependencies = [
  "pallet-xcm",
  "parachains-common",
  "parity-scale-codec",
- "penpal-runtime",
  "polkadot-core-primitives",
  "polkadot-parachain",
  "polkadot-runtime",
@@ -544,7 +545,9 @@ dependencies = [
 name = "asset-hub-polkadot-integration-tests"
 version = "1.0.0"
 dependencies = [
- "asset-hub-polkadot-runtime",
+ "cumulus-pallet-dmp-queue",
+ "cumulus-pallet-parachain-system",
+ "cumulus-pallet-xcmp-queue",
  "frame-support",
  "frame-system",
  "integration-tests-common",
@@ -553,7 +556,6 @@ dependencies = [
  "pallet-xcm",
  "parachains-common",
  "parity-scale-codec",
- "penpal-runtime",
  "polkadot-core-primitives",
  "polkadot-parachain",
  "polkadot-runtime",
@@ -641,6 +643,9 @@ version = "1.0.0"
 dependencies = [
  "assert_matches",
  "asset-hub-westend-runtime",
+ "cumulus-pallet-dmp-queue",
+ "cumulus-pallet-parachain-system",
+ "cumulus-pallet-xcmp-queue",
  "frame-support",
  "frame-system",
  "integration-tests-common",
@@ -650,7 +655,6 @@ dependencies = [
  "pallet-xcm",
  "parachains-common",
  "parity-scale-codec",
- "penpal-runtime",
  "polkadot-core-primitives",
  "polkadot-parachain",
  "polkadot-runtime",
@@ -658,7 +662,6 @@ dependencies = [
  "sp-core",
  "sp-runtime",
  "sp-weights",
- "westend-runtime",
  "xcm",
  "xcm-emulator",
  "xcm-executor",
@@ -1445,7 +1448,7 @@ name = "bridge-hub-rococo-integration-tests"
 version = "1.0.0"
 dependencies = [
  "bp-messages",
- "bridge-hub-rococo-runtime",
+ "cumulus-pallet-parachain-system",
  "cumulus-pallet-xcmp-queue",
  "frame-support",
  "frame-system",
@@ -1759,15 +1762,6 @@ dependencies = [
  "thiserror",
 ]
 
-[[package]]
-name = "casey"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614586263949597dcc18675da12ef9b429135e13628d92eb8b8c6fa50ca5656b"
-dependencies = [
- "syn 1.0.109",
-]
-
 [[package]]
 name = "cast"
 version = "0.3.0"
@@ -1999,6 +1993,7 @@ version = "0.1.0"
 dependencies = [
  "asset-hub-polkadot-runtime",
  "collectives-polkadot-runtime",
+ "cumulus-pallet-parachain-system",
  "cumulus-pallet-xcmp-queue",
  "frame-support",
  "frame-system",
@@ -5263,20 +5258,26 @@ dependencies = [
  "bridge-hub-rococo-runtime",
  "bridge-runtime-common",
  "collectives-polkadot-runtime",
+ "cumulus-pallet-dmp-queue",
+ "cumulus-pallet-parachain-system",
+ "cumulus-pallet-xcmp-queue",
  "cumulus-primitives-core",
  "frame-support",
  "frame-system",
  "kusama-runtime",
  "kusama-runtime-constants",
+ "lazy_static",
  "pallet-assets",
  "pallet-balances",
  "pallet-bridge-messages",
  "pallet-im-online",
+ "pallet-message-queue",
  "pallet-staking",
  "pallet-xcm",
  "parachain-info",
  "parachains-common",
  "parity-scale-codec",
+ "paste",
  "penpal-runtime",
  "polkadot-core-primitives",
  "polkadot-parachain",
@@ -15908,7 +15909,6 @@ dependencies = [
 name = "xcm-emulator"
 version = "0.1.0"
 dependencies = [
- "casey",
  "cumulus-pallet-dmp-queue",
  "cumulus-pallet-parachain-system",
  "cumulus-pallet-xcmp-queue",
@@ -15917,6 +15917,8 @@ dependencies = [
  "cumulus-test-relay-sproof-builder",
  "frame-support",
  "frame-system",
+ "impl-trait-for-tuples",
+ "lazy_static",
  "log",
  "pallet-balances",
  "pallet-message-queue",
@@ -15926,7 +15928,6 @@ dependencies = [
  "paste",
  "polkadot-primitives",
  "polkadot-runtime-parachains",
- "quote",
  "sp-arithmetic",
  "sp-core",
  "sp-io",
@@ -15934,6 +15935,7 @@ dependencies = [
  "sp-std",
  "sp-trie",
  "xcm",
+ "xcm-executor",
 ]
 
 [[package]]
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml
index a25e2116873..d3ecbde4291 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml
@@ -28,8 +28,9 @@ pallet-xcm = { default-features = false, git = "https://github.com/paritytech/po
 
 # Cumulus
 parachains-common = { path = "../../../../common" }
-penpal-runtime = { path = "../../../../runtimes/testing/penpal" }
-asset-hub-kusama-runtime = { path = "../../../../runtimes/assets/asset-hub-kusama" }
+cumulus-pallet-dmp-queue = { path = "../../../../../pallets/dmp-queue" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
+cumulus-pallet-parachain-system = { path = "../../../../../pallets/parachain-system" }
 
 # Local
 xcm-emulator = { default-features = false, path = "../../../../../xcm/xcm-emulator" }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/lib.rs
index b090432fa7c..4282e91c499 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/lib.rs
@@ -16,31 +16,89 @@
 
 pub use codec::Encode;
 pub use frame_support::{
-	assert_ok, instances::Instance1, pallet_prelude::Weight, traits::fungibles::Inspect,
+	assert_err, assert_ok,
+	instances::Instance1,
+	pallet_prelude::Weight,
+	sp_runtime::{AccountId32, DispatchError, DispatchResult, MultiAddress},
+	traits::{fungibles::Inspect, OriginTrait},
 };
 pub use integration_tests_common::{
 	constants::{
 		accounts::{ALICE, BOB},
+		asset_hub_kusama::ED as ASSET_HUB_KUSAMA_ED,
 		kusama::ED as KUSAMA_ED,
 		PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
 	},
-	AccountId, AssetHubKusama, AssetHubKusamaPallet, AssetHubKusamaReceiver, AssetHubKusamaSender,
-	BridgeHubKusama, BridgeHubKusamaPallet, BridgeHubKusamaReceiver, BridgeHubKusamaSender,
-	BridgeHubPolkadot, BridgeHubPolkadotPallet, BridgeHubPolkadotReceiver, BridgeHubPolkadotSender,
-	Collectives, CollectivesPallet, CollectivesReceiver, CollectivesSender, Kusama, KusamaMockNet,
-	KusamaPallet, KusamaReceiver, KusamaSender, PenpalKusama, PenpalKusamaReceiver,
-	PenpalKusamaSender, PenpalPolkadot, PenpalPolkadotReceiver, PenpalPolkadotSender, Polkadot,
+	lazy_static::lazy_static,
+	xcm_transact_paid_execution, xcm_transact_unpaid_execution, AssetHubKusama,
+	AssetHubKusamaPallet, AssetHubKusamaReceiver, AssetHubKusamaSender, BridgeHubKusama,
+	BridgeHubKusamaPallet, BridgeHubKusamaReceiver, BridgeHubKusamaSender, BridgeHubPolkadot,
+	BridgeHubPolkadotPallet, BridgeHubPolkadotReceiver, BridgeHubPolkadotSender, Collectives,
+	CollectivesPallet, CollectivesReceiver, CollectivesSender, Kusama, KusamaMockNet, KusamaPallet,
+	KusamaReceiver, KusamaSender, PenpalKusamaA, PenpalKusamaAPallet, PenpalKusamaAReceiver,
+	PenpalKusamaASender, PenpalKusamaB, PenpalKusamaBPallet, PenpalKusamaBReceiver,
+	PenpalKusamaBSender, PenpalPolkadotA, PenpalPolkadotAReceiver, PenpalPolkadotASender, Polkadot,
 	PolkadotMockNet, PolkadotPallet, PolkadotReceiver, PolkadotSender,
 };
+pub use parachains_common::{AccountId, Balance};
 pub use polkadot_core_primitives::InboundDownwardMessage;
+pub use polkadot_parachain::primitives::{HrmpChannelId, Id};
+pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
 pub use xcm::{
 	prelude::*,
 	v3::{Error, NetworkId::Kusama as KusamaId},
+	DoubleEncoded,
 };
 pub use xcm_emulator::{
 	assert_expected_events, bx, cumulus_pallet_dmp_queue, helpers::weight_within_threshold,
-	Parachain as Para, RelayChain as Relay, TestExt,
+	AccountId32Junction, Chain, ParaId, Parachain as Para, RelayChain as Relay, Test, TestArgs,
+	TestContext, TestExt, TestExternalities,
 };
 
+pub const ASSET_ID: u32 = 1;
+pub const ASSET_MIN_BALANCE: u128 = 1000;
+// `Assets` pallet index
+pub const ASSETS_PALLET_ID: u8 = 50;
+
+pub type RelayToSystemParaTest = Test<Kusama, AssetHubKusama>;
+pub type SystemParaToRelayTest = Test<AssetHubKusama, Kusama>;
+pub type SystemParaToParaTest = Test<AssetHubKusama, PenpalKusamaA>;
+
+/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
+pub fn relay_test_args(amount: Balance) -> TestArgs {
+	TestArgs {
+		dest: Kusama::child_location_of(AssetHubKusama::para_id()),
+		beneficiary: AccountId32Junction {
+			network: None,
+			id: AssetHubKusamaReceiver::get().into(),
+		}
+		.into(),
+		amount,
+		assets: (Here, amount).into(),
+		asset_id: None,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
+/// Returns a `TestArgs` instance to de used for the System Parachain accross integraton tests
+pub fn system_para_test_args(
+	dest: MultiLocation,
+	beneficiary_id: AccountId32,
+	amount: Balance,
+	assets: MultiAssets,
+	asset_id: Option<u32>,
+) -> TestArgs {
+	TestArgs {
+		dest,
+		beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
+		amount,
+		assets,
+		asset_id,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/hrmp_channels.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/hrmp_channels.rs
new file mode 100644
index 00000000000..9d0bd50502e
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/hrmp_channels.rs
@@ -0,0 +1,201 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+const MAX_CAPACITY: u32 = 8;
+const MAX_MESSAGE_SIZE: u32 = 8192;
+
+/// Opening HRMP channels between Parachains should work
+#[test]
+fn open_hrmp_channel_between_paras_works() {
+	// Parchain A init values
+	let para_a_id = PenpalKusamaA::para_id();
+	let para_a_root_origin = <PenpalKusamaA as Chain>::RuntimeOrigin::root();
+
+	// Parachain B init values
+	let para_b_id = PenpalKusamaB::para_id();
+	let para_b_root_origin = <PenpalKusamaB as Chain>::RuntimeOrigin::root();
+
+	let fee_amount = KUSAMA_ED * 1000;
+	let fund_amount = KUSAMA_ED * 1000_000_000;
+
+	// Fund Parachain's Sovereign accounts to be able to reserve the deposit
+	let para_a_sovereign_account = Kusama::fund_para_sovereign(fund_amount, para_a_id);
+	let para_b_sovereign_account = Kusama::fund_para_sovereign(fund_amount, para_b_id);
+
+	let relay_destination: VersionedMultiLocation = PenpalKusamaA::parent_location().into();
+
+	// ---- Init Open channel from Parachain to System Parachain
+	let mut call = Kusama::init_open_channel_call(para_b_id, MAX_CAPACITY, MAX_MESSAGE_SIZE);
+	let origin_kind = OriginKind::Native;
+	let native_asset: MultiAsset = (Here, fee_amount).into();
+	let beneficiary = Kusama::sovereign_account_id_of_child_para(para_a_id);
+
+	let mut xcm = xcm_transact_paid_execution(call, origin_kind, native_asset.clone(), beneficiary);
+
+	PenpalKusamaA::execute_with(|| {
+		assert_ok!(<PenpalKusamaA as PenpalKusamaAPallet>::PolkadotXcm::send(
+			para_a_root_origin,
+			bx!(relay_destination.clone()),
+			bx!(xcm),
+		));
+	});
+
+	Kusama::execute_with(|| {
+		type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+		Kusama::assert_ump_queue_processed(
+			true,
+			Some(para_a_id),
+			Some(Weight::from_parts(1_312_558_000, 200000)),
+		);
+
+		assert_expected_events!(
+			Kusama,
+			vec![
+				// Parachain's Sovereign account balance is withdrawn to pay XCM fees
+				RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+					who: *who == para_a_sovereign_account.clone(),
+					amount: *amount == fee_amount,
+				},
+				// Sender deposit is reserved for Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == para_a_sovereign_account,
+				},
+				// Open channel requested from Para A to Para B
+				RuntimeEvent::Hrmp(
+					polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested(
+						sender, recipient, max_capacity, max_message_size
+					)
+				) => {
+					sender: *sender == para_a_id.into(),
+					recipient: *recipient == para_b_id.into(),
+					max_capacity: *max_capacity == MAX_CAPACITY,
+					max_message_size: *max_message_size == MAX_MESSAGE_SIZE,
+				},
+			]
+		);
+	});
+
+	// ---- Accept Open channel from Parachain to System Parachain
+	call = Kusama::accept_open_channel_call(para_a_id);
+	let beneficiary = Kusama::sovereign_account_id_of_child_para(para_b_id);
+
+	xcm = xcm_transact_paid_execution(call, origin_kind, native_asset, beneficiary);
+
+	PenpalKusamaB::execute_with(|| {
+		assert_ok!(<PenpalKusamaB as PenpalKusamaBPallet>::PolkadotXcm::send(
+			para_b_root_origin,
+			bx!(relay_destination),
+			bx!(xcm),
+		));
+	});
+
+	Kusama::execute_with(|| {
+		type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+		Kusama::assert_ump_queue_processed(
+			true,
+			Some(para_b_id),
+			Some(Weight::from_parts(1_312_558_000, 200_000)),
+		);
+
+		assert_expected_events!(
+			Kusama,
+			vec![
+				// Parachain's Sovereign account balance is withdrawn to pay XCM fees
+				RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+					who: *who == para_b_sovereign_account.clone(),
+					amount: *amount == fee_amount,
+				},
+				// Sender deposit is reserved for Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == para_b_sovereign_account,
+				},
+				// Open channel accepted for Para A to Para B
+				RuntimeEvent::Hrmp(
+					polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted(
+						sender, recipient
+					)
+				) => {
+					sender: *sender == para_a_id.into(),
+					recipient: *recipient == para_b_id.into(),
+				},
+			]
+		);
+	});
+
+	Kusama::force_process_hrmp_open(para_a_id, para_b_id);
+}
+
+/// Opening HRMP channels between System Parachains and Parachains should work
+#[test]
+fn force_open_hrmp_channel_for_system_para_works() {
+	// Relay Chain init values
+	let relay_root_origin = <Kusama as Chain>::RuntimeOrigin::root();
+
+	// System Para init values
+	let system_para_id = AssetHubKusama::para_id();
+
+	// Parachain A init values
+	let para_a_id = PenpalKusamaA::para_id();
+
+	let fund_amount = KUSAMA_ED * 1000_000_000;
+
+	// Fund Parachain's Sovereign accounts to be able to reserve the deposit
+	let para_a_sovereign_account = Kusama::fund_para_sovereign(fund_amount, para_a_id);
+	let system_para_sovereign_account = Kusama::fund_para_sovereign(fund_amount, system_para_id);
+
+	Kusama::execute_with(|| {
+		assert_ok!(<Kusama as KusamaPallet>::Hrmp::force_open_hrmp_channel(
+			relay_root_origin,
+			system_para_id,
+			para_a_id,
+			MAX_CAPACITY,
+			MAX_MESSAGE_SIZE
+		));
+
+		type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+		assert_expected_events!(
+			Kusama,
+			vec![
+				// Sender deposit is reserved for System Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == system_para_sovereign_account,
+				},
+				// Recipient deposit is reserved for Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == para_a_sovereign_account,
+				},
+				// HRMP channel forced opened
+				RuntimeEvent::Hrmp(
+					polkadot_runtime_parachains::hrmp::Event::HrmpChannelForceOpened(
+						sender, recipient, max_capacity, max_message_size
+					)
+				) => {
+					sender: *sender == system_para_id.into(),
+					recipient: *recipient == para_a_id.into(),
+					max_capacity: *max_capacity == MAX_CAPACITY,
+					max_message_size: *max_message_size == MAX_MESSAGE_SIZE,
+				},
+			]
+		);
+	});
+
+	Kusama::force_process_hrmp_open(system_para_id, para_a_id);
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs
index 44861d2a872..00e0a663e47 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs
@@ -14,6 +14,8 @@
 // You should have received a copy of the GNU General Public License
 // along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
 
+mod hrmp_channels;
 mod reserve_transfer;
+mod send;
+mod set_xcm_versions;
 mod teleport;
-mod transact;
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/reserve_transfer.rs
index b1c5cbbd3bf..d26f48215c1 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/reserve_transfer.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/reserve_transfer.rs
@@ -16,66 +16,395 @@
 
 use crate::*;
 
+fn relay_origin_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+	Kusama::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(630_092_000, 6_196)));
+
+	assert_expected_events!(
+		Kusama,
+		vec![
+			// Amount to reserve transfer is transferred to System Parachain's Sovereign account
+			RuntimeEvent::Balances(pallet_balances::Event::Transfer { from, to, amount }) => {
+				from: *from == t.sender.account_id,
+				to: *to == Kusama::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount:  *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn system_para_dest_assertions_incomplete(_t: RelayToSystemParaTest) {
+	AssetHubKusama::assert_dmp_queue_incomplete(
+		Some(Weight::from_parts(1_000_000_000, 0)),
+		Some(Error::UntrustedReserveLocation),
+	);
+}
+
+fn system_para_to_relay_assertions(_t: SystemParaToRelayTest) {
+	AssetHubKusama::assert_xcm_pallet_attempted_error(Some(XcmError::Barrier))
+}
+
+fn system_para_to_para_assertions(t: SystemParaToParaTest) {
+	type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+	AssetHubKusama::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		630_092_000,
+		6_196,
+	)));
+
+	assert_expected_events!(
+		AssetHubKusama,
+		vec![
+			// Amount to reserve transfer is transferred to Parachain's Sovereing account
+			RuntimeEvent::Balances(
+				pallet_balances::Event::Transfer { from, to, amount }
+			) => {
+				from: *from == t.sender.account_id,
+				to: *to == AssetHubKusama::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
+	type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+	AssetHubKusama::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		676_119_000,
+		6196,
+	)));
+
+	assert_expected_events!(
+		AssetHubKusama,
+		vec![
+			// Amount to reserve transfer is transferred to Parachain's Sovereing account
+			RuntimeEvent::Assets(
+				pallet_assets::Event::Transferred { asset_id, from, to, amount }
+			) => {
+				asset_id: *asset_id == ASSET_ID,
+				from: *from == t.sender.account_id,
+				to: *to == AssetHubKusama::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn relay_limited_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Kusama as KusamaPallet>::XcmPallet::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn relay_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Kusama as KusamaPallet>::XcmPallet::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_limited_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn system_para_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
+	<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
+	<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+/// Limited Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
 #[test]
-fn reserve_transfer_native_asset_from_relay_to_assets() {
-	// Init tests variables
-	let amount = KUSAMA_ED * 1000;
-	let relay_sender_balance_before = Kusama::account_data_of(KusamaSender::get()).free;
-	let para_receiver_balance_before =
-		AssetHubKusama::account_data_of(AssetHubKusamaReceiver::get()).free;
-
-	let origin = <Kusama as Relay>::RuntimeOrigin::signed(KusamaSender::get());
-	let assets_para_destination: VersionedMultiLocation =
-		Kusama::child_location_of(AssetHubKusama::para_id()).into();
-	let beneficiary: VersionedMultiLocation =
-		AccountId32 { network: None, id: AssetHubKusamaReceiver::get().into() }.into();
-	let native_assets: VersionedMultiAssets = (Here, amount).into();
-	let fee_asset_item = 0;
-	let weight_limit = WeightLimit::Unlimited;
-
-	// Send XCM message from Relay Chain
-	Kusama::execute_with(|| {
-		assert_ok!(<Kusama as KusamaPallet>::XcmPallet::limited_reserve_transfer_assets(
-			origin,
-			bx!(assets_para_destination),
-			bx!(beneficiary),
-			bx!(native_assets),
-			fee_asset_item,
-			weight_limit,
-		));
-
-		type RuntimeEvent = <Kusama as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Kusama,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => {
-					weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(630_092_000, 6196), *weight),
-				},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubKusama::execute_with(|| {
-		type RuntimeEvent = <AssetHubKusama as Para>::RuntimeEvent;
-
-		assert_expected_events!(
-			AssetHubKusama,
-			vec![
-				RuntimeEvent::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward {
-					outcome: Outcome::Incomplete(_, Error::UntrustedReserveLocation),
-					..
-				}) => {},
-			]
-		);
-	});
-
-	// Check if balances are updated accordingly in Relay Chain and Assets Parachain
-	let relay_sender_balance_after = Kusama::account_data_of(KusamaSender::get()).free;
-	let para_sender_balance_after =
-		AssetHubKusama::account_data_of(AssetHubKusamaReceiver::get()).free;
-
-	assert_eq!(relay_sender_balance_before - amount, relay_sender_balance_after);
-	assert_eq!(para_sender_balance_after, para_receiver_balance_before);
+fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = KUSAMA_ED * 1000;
+	let test_args = TestContext {
+		sender: KusamaSender::get(),
+		receiver: AssetHubKusamaReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Kusama>(relay_origin_assertions);
+	test.set_assertion::<AssetHubKusama>(system_para_dest_assertions_incomplete);
+	test.set_dispatchable::<Kusama>(relay_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Limited Reserve Transfers of native asset from System Parachain to Relay Chain shoudln't work
+#[test]
+fn limited_reserve_transfer_native_asset_from_system_para_to_relay_fails() {
+	// Init values for System Parachain
+	let destination = AssetHubKusama::parent_location();
+	let beneficiary_id = KusamaReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: KusamaReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubKusama>(system_para_to_relay_assertions);
+	test.set_dispatchable::<AssetHubKusama>(system_para_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
+#[test]
+fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = KUSAMA_ED * 1000;
+	let test_args = TestContext {
+		sender: KusamaSender::get(),
+		receiver: AssetHubKusamaReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Kusama>(relay_origin_assertions);
+	test.set_assertion::<AssetHubKusama>(system_para_dest_assertions_incomplete);
+	test.set_dispatchable::<Kusama>(relay_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work
+#[test]
+fn reserve_transfer_native_asset_from_system_para_to_relay_fails() {
+	// Init values for System Parachain
+	let destination = AssetHubKusama::parent_location();
+	let beneficiary_id = KusamaReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: KusamaReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubKusama>(system_para_to_relay_assertions);
+	test.set_dispatchable::<AssetHubKusama>(system_para_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Limited Reserve Transfers of native asset from System Parachain to Parachain should work
+#[test]
+fn limited_reserve_transfer_native_asset_from_system_para_to_para() {
+	// Init values for System Parachain
+	let destination = AssetHubKusama::sibling_location_of(PenpalKusamaA::para_id());
+	let beneficiary_id = PenpalKusamaAReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: PenpalKusamaAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+
+	test.set_assertion::<AssetHubKusama>(system_para_to_para_assertions);
+	// TODO: Add assertion for Penpal runtime. Right now message is failing with `UntrustedReserveLocation`
+	test.set_dispatchable::<AssetHubKusama>(system_para_to_para_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve transfers
+}
+
+/// Reserve Transfers of native asset from System Parachain to Parachain should work
+#[test]
+fn reserve_transfer_native_asset_from_system_para_to_para() {
+	// Init values for System Parachain
+	let destination = AssetHubKusama::sibling_location_of(PenpalKusamaA::para_id());
+	let beneficiary_id = PenpalKusamaAReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: PenpalKusamaAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+
+	test.set_assertion::<AssetHubKusama>(system_para_to_para_assertions);
+	// TODO: Add assertion for Penpal runtime. Right now message is failing with `UntrustedReserveLocation`
+	test.set_dispatchable::<AssetHubKusama>(system_para_to_para_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve transfers
+}
+
+/// Limited Reserve Transfers of a local asset from System Parachain to Parachain should work
+#[test]
+fn limited_reserve_transfer_asset_from_system_para_to_para() {
+	// Force create asset from Relay Chain and mint assets for System Parachain's sender account
+	AssetHubKusama::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		AssetHubKusamaSender::get(),
+		ASSET_MIN_BALANCE * 1000000,
+	);
+
+	// Init values for System Parachain
+	let destination = AssetHubKusama::sibling_location_of(PenpalKusamaA::para_id());
+	let beneficiary_id = PenpalKusamaAReceiver::get();
+	let amount_to_send = ASSET_MIN_BALANCE * 1000;
+	let assets =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
+			.into();
+
+	let system_para_test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: PenpalKusamaAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
+
+	system_para_test.set_assertion::<AssetHubKusama>(system_para_to_para_assets_assertions);
+	// TODO: Add assertions when Penpal is able to manage assets
+	system_para_test
+		.set_dispatchable::<AssetHubKusama>(system_para_to_para_limited_reserve_transfer_assets);
+	system_para_test.assert();
+}
+
+/// Reserve Transfers of a local asset from System Parachain to Parachain should work
+#[test]
+fn reserve_transfer_asset_from_system_para_to_para() {
+	// Force create asset from Relay Chain and mint assets for System Parachain's sender account
+	AssetHubKusama::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		AssetHubKusamaSender::get(),
+		ASSET_MIN_BALANCE * 1000000,
+	);
+
+	// Init values for System Parachain
+	let destination = AssetHubKusama::sibling_location_of(PenpalKusamaA::para_id());
+	let beneficiary_id = PenpalKusamaAReceiver::get();
+	let amount_to_send = ASSET_MIN_BALANCE * 1000;
+	let assets =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
+			.into();
+
+	let system_para_test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: PenpalKusamaAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
+
+	system_para_test.set_assertion::<AssetHubKusama>(system_para_to_para_assets_assertions);
+	// TODO: Add assertions when Penpal is able to manage assets
+	system_para_test
+		.set_dispatchable::<AssetHubKusama>(system_para_to_para_reserve_transfer_assets);
+	system_para_test.assert();
 }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/send.rs
new file mode 100644
index 00000000000..8b92447f11f
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/send.rs
@@ -0,0 +1,196 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+/// Relay Chain should be able to execute `Transact` instructions in System Parachain
+/// when `OriginKind::Superuser` and signer is `sudo`
+#[test]
+fn send_transact_sudo_from_relay_to_system_para_works() {
+	// Init tests variables
+	let root_origin = <Kusama as Chain>::RuntimeOrigin::root();
+	let system_para_destination = Kusama::child_location_of(AssetHubKusama::para_id()).into();
+	let asset_owner: AccountId = AssetHubKusamaSender::get().into();
+	let xcm = AssetHubKusama::force_create_asset_xcm(
+		OriginKind::Superuser,
+		ASSET_ID,
+		asset_owner.clone(),
+		true,
+		1000,
+	);
+	// Send XCM message from Relay Chain
+	Kusama::execute_with(|| {
+		assert_ok!(<Kusama as KusamaPallet>::XcmPallet::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		Kusama::assert_xcm_pallet_sent();
+	});
+
+	// Receive XCM message in Assets Parachain
+	AssetHubKusama::execute_with(|| {
+		type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+		AssetHubKusama::assert_dmp_queue_complete(Some(Weight::from_parts(1_019_445_000, 200_000)));
+
+		assert_expected_events!(
+			AssetHubKusama,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::ForceCreated { asset_id, owner }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == asset_owner,
+				},
+			]
+		);
+
+		assert!(<AssetHubKusama as AssetHubKusamaPallet>::Assets::asset_exists(ASSET_ID));
+	});
+}
+
+/// Relay Chain shouldn't be able to execute `Transact` instructions in System Parachain
+/// when `OriginKind::Native`
+#[test]
+fn send_transact_native_from_relay_to_system_para_fails() {
+	// Init tests variables
+	let signed_origin = <Kusama as Chain>::RuntimeOrigin::signed(KusamaSender::get().into());
+	let system_para_destination = Kusama::child_location_of(AssetHubKusama::para_id()).into();
+	let asset_owner = AssetHubKusamaSender::get().into();
+	let xcm = AssetHubKusama::force_create_asset_xcm(
+		OriginKind::Native,
+		ASSET_ID,
+		asset_owner,
+		true,
+		1000,
+	);
+
+	// Send XCM message from Relay Chain
+	Kusama::execute_with(|| {
+		assert_err!(
+			<Kusama as KusamaPallet>::XcmPallet::send(
+				signed_origin,
+				bx!(system_para_destination),
+				bx!(xcm)
+			),
+			DispatchError::BadOrigin
+		);
+	});
+}
+
+/// System Parachain shouldn't be able to execute `Transact` instructions in Relay Chain
+/// when `OriginKind::Native`
+#[test]
+fn send_transact_native_from_system_para_to_relay_fails() {
+	// Init tests variables
+	let signed_origin =
+		<AssetHubKusama as Chain>::RuntimeOrigin::signed(AssetHubKusamaSender::get().into());
+	let relay_destination = AssetHubKusama::parent_location().into();
+	let call = <Kusama as Chain>::RuntimeCall::System(frame_system::Call::<
+		<Kusama as Chain>::Runtime,
+	>::remark_with_event {
+		remark: vec![0, 1, 2, 3],
+	})
+	.encode()
+	.into();
+	let origin_kind = OriginKind::Native;
+
+	let xcm = xcm_transact_unpaid_execution(call, origin_kind);
+
+	// Send XCM message from Relay Chain
+	AssetHubKusama::execute_with(|| {
+		assert_err!(
+			<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::send(
+				signed_origin,
+				bx!(relay_destination),
+				bx!(xcm)
+			),
+			DispatchError::BadOrigin
+		);
+	});
+}
+
+/// Parachain should be able to send XCM paying its fee with sufficient asset
+/// in the System Parachain
+#[test]
+fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
+	let para_sovereign_account = AssetHubKusama::sovereign_account_id_of(
+		AssetHubKusama::sibling_location_of(PenpalKusamaA::para_id()),
+	);
+
+	// Force create and mint assets for Parachain's sovereign account
+	AssetHubKusama::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		para_sovereign_account.clone(),
+		ASSET_MIN_BALANCE * 1000000000,
+	);
+
+	// We just need a call that can pass the `SafeCallFilter`
+	// Call values are not relevant
+	let call = AssetHubKusama::force_create_asset_call(
+		ASSET_ID,
+		para_sovereign_account.clone(),
+		true,
+		ASSET_MIN_BALANCE,
+	);
+
+	let origin_kind = OriginKind::SovereignAccount;
+	let fee_amount = ASSET_MIN_BALANCE * 1000000;
+	let native_asset =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into();
+
+	let root_origin = <PenpalKusamaA as Chain>::RuntimeOrigin::root();
+	let system_para_destination =
+		PenpalKusamaA::sibling_location_of(AssetHubKusama::para_id()).into();
+	let xcm = xcm_transact_paid_execution(
+		call,
+		origin_kind,
+		native_asset,
+		para_sovereign_account.clone(),
+	);
+
+	PenpalKusamaA::execute_with(|| {
+		assert_ok!(<PenpalKusamaA as PenpalKusamaAPallet>::PolkadotXcm::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		AssetHubKusama::assert_xcm_pallet_sent();
+	});
+
+	AssetHubKusama::execute_with(|| {
+		type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+		AssetHubKusama::assert_xcmp_queue_success(Some(Weight::from_parts(2_176_414_000, 203_593)));
+
+		assert_expected_events!(
+			AssetHubKusama,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == para_sovereign_account,
+					balance: *balance == fee_amount,
+				},
+				RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
+					asset_id: *asset_id == ASSET_ID,
+				},
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/set_xcm_versions.rs
new file mode 100644
index 00000000000..0ab53b45122
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/set_xcm_versions.rs
@@ -0,0 +1,94 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+#[test]
+fn relay_sets_system_para_xcm_supported_version() {
+	// Init tests variables
+	let sudo_origin = <Kusama as Chain>::RuntimeOrigin::root();
+	let system_para_destination: MultiLocation =
+		Kusama::child_location_of(AssetHubKusama::para_id());
+
+	// Relay Chain sets supported version for Asset Parachain
+	Kusama::execute_with(|| {
+		assert_ok!(<Kusama as KusamaPallet>::XcmPallet::force_xcm_version(
+			sudo_origin,
+			bx!(system_para_destination),
+			XCM_V3
+		));
+
+		type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+		assert_expected_events!(
+			Kusama,
+			vec![
+				RuntimeEvent::XcmPallet(pallet_xcm::Event::SupportedVersionChanged {
+					location,
+					version: XCM_V3
+				}) => { location: *location == system_para_destination, },
+			]
+		);
+	});
+}
+
+#[test]
+fn system_para_sets_relay_xcm_supported_version() {
+	// Init test variables
+	let sudo_origin = <Kusama as Chain>::RuntimeOrigin::root();
+	let parent_location = AssetHubKusama::parent_location();
+	let system_para_destination: VersionedMultiLocation =
+		Kusama::child_location_of(AssetHubKusama::para_id()).into();
+	let call = <AssetHubKusama as Chain>::RuntimeCall::PolkadotXcm(pallet_xcm::Call::<
+		<AssetHubKusama as Chain>::Runtime,
+	>::force_xcm_version {
+		location: bx!(parent_location),
+		version: XCM_V3,
+	})
+	.encode()
+	.into();
+	let origin_kind = OriginKind::Superuser;
+
+	let xcm = xcm_transact_unpaid_execution(call, origin_kind);
+
+	// System Parachain sets supported version for Relay Chain throught it
+	Kusama::execute_with(|| {
+		assert_ok!(<Kusama as KusamaPallet>::XcmPallet::send(
+			sudo_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		Kusama::assert_xcm_pallet_sent();
+	});
+
+	// System Parachain receive the XCM message
+	AssetHubKusama::execute_with(|| {
+		type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+		AssetHubKusama::assert_dmp_queue_complete(Some(Weight::from_parts(1_019_210_000, 200_000)));
+
+		assert_expected_events!(
+			AssetHubKusama,
+			vec![
+				RuntimeEvent::PolkadotXcm(pallet_xcm::Event::SupportedVersionChanged {
+					location,
+					version: XCM_V3
+				}) => { location: *location == parent_location, },
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/teleport.rs
index 16c0db907c3..baaca92e051 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/teleport.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/teleport.rs
@@ -16,65 +16,347 @@
 
 use crate::*;
 
+fn relay_origin_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+	Kusama::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(631_531_000, 7_186)));
+
+	assert_expected_events!(
+		Kusama,
+		vec![
+			// Amount to teleport is withdrawn from Sender
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == t.sender.account_id,
+				amount: *amount == t.args.amount,
+			},
+			// Amount to teleport is deposited in Relay's `CheckAccount`
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, amount }) => {
+				who: *who == <Kusama as KusamaPallet>::XcmPallet::check_account(),
+				amount:  *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn relay_dest_assertions(t: SystemParaToRelayTest) {
+	type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
+
+	Kusama::assert_ump_queue_processed(
+		true,
+		Some(AssetHubKusama::para_id()),
+		Some(Weight::from_parts(307_225_000, 7_186)),
+	);
+
+	assert_expected_events!(
+		Kusama,
+		vec![
+			// Amount is witdrawn from Relay Chain's `CheckAccount`
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == <Kusama as KusamaPallet>::XcmPallet::check_account(),
+				amount: *amount == t.args.amount,
+			},
+			// Amount minus fees are deposited in Receiver's account
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
+				who: *who == t.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) {
+	Kusama::assert_ump_queue_processed(
+		false,
+		Some(AssetHubKusama::para_id()),
+		Some(Weight::from_parts(148_433_000, 3_593)),
+	);
+}
+
+fn para_origin_assertions(t: SystemParaToRelayTest) {
+	type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+	AssetHubKusama::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		534_872_000,
+		7_133,
+	)));
+
+	AssetHubKusama::assert_parachain_system_ump_sent();
+
+	assert_expected_events!(
+		AssetHubKusama,
+		vec![
+			// Amount is withdrawn from Sender's account
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == t.sender.account_id,
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn para_dest_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
+
+	AssetHubKusama::assert_dmp_queue_complete(Some(Weight::from_parts(165_592_000, 0)));
+
+	assert_expected_events!(
+		AssetHubKusama,
+		vec![
+			// Amount minus fees are deposited in Receiver's account
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
+				who: *who == t.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Kusama as KusamaPallet>::XcmPallet::limited_teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn relay_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Kusama as KusamaPallet>::XcmPallet::teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::limited_teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+// TODO: Uncomment when https://github.com/paritytech/polkadot/pull/7424 is merged
+// fn system_para_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
+// 	<AssetHubKusama as AssetHubKusamaPallet>::PolkadotXcm::teleport_assets(
+// 		t.signed_origin,
+// 		bx!(t.args.dest),
+// 		bx!(t.args.beneficiary),
+// 		bx!(t.args.assets),
+// 		t.args.fee_asset_item,
+// 	)
+// }
+
+/// Limited Teleport of native asset from Relay Chain to the System Parachain should work
 #[test]
-fn teleport_native_assets_from_relay_to_assets_para() {
-	// Init tests variables
-	let amount = KUSAMA_ED * 1000;
-	let relay_sender_balance_before = Kusama::account_data_of(KusamaSender::get()).free;
-	let para_receiver_balance_before =
-		AssetHubKusama::account_data_of(AssetHubKusamaReceiver::get()).free;
-
-	let origin = <Kusama as Relay>::RuntimeOrigin::signed(KusamaSender::get());
-	let assets_para_destination: VersionedMultiLocation =
-		Kusama::child_location_of(AssetHubKusama::para_id()).into();
-	let beneficiary: VersionedMultiLocation =
-		AccountId32 { network: None, id: AssetHubKusamaReceiver::get().into() }.into();
-	let native_assets: VersionedMultiAssets = (Here, amount).into();
-	let fee_asset_item = 0;
-	let weight_limit = WeightLimit::Unlimited;
-
-	// Send XCM message from Relay Chain
-	Kusama::execute_with(|| {
-		assert_ok!(<Kusama as KusamaPallet>::XcmPallet::limited_teleport_assets(
-			origin,
-			bx!(assets_para_destination),
-			bx!(beneficiary),
-			bx!(native_assets),
-			fee_asset_item,
-			weight_limit,
-		));
-
-		type RuntimeEvent = <Kusama as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Kusama,
-			vec![
-				RuntimeEvent::XcmPallet(
-					pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
-				) => {},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubKusama::execute_with(|| {
-		type RuntimeEvent = <AssetHubKusama as Para>::RuntimeEvent;
-
-		assert_expected_events!(
-			AssetHubKusama,
-			vec![
-				RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
-					who: *who == AssetHubKusamaReceiver::get().into(),
-				},
-			]
-		);
-	});
-
-	// Check if balances are updated accordingly in Relay Chain and Assets Parachain
-	let relay_sender_balance_after = Kusama::account_data_of(KusamaSender::get()).free;
-	let para_sender_balance_after =
-		AssetHubKusama::account_data_of(AssetHubKusamaReceiver::get()).free;
-
-	assert_eq!(relay_sender_balance_before - amount, relay_sender_balance_after);
-	assert!(para_sender_balance_after > para_receiver_balance_before);
+fn limited_teleport_native_assets_from_relay_to_system_para_works() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = KUSAMA_ED * 1000;
+	let test_args = TestContext {
+		sender: KusamaSender::get(),
+		receiver: AssetHubKusamaReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Kusama>(relay_origin_assertions);
+	test.set_assertion::<AssetHubKusama>(para_dest_assertions);
+	test.set_dispatchable::<Kusama>(relay_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
 }
+
+/// Limited Teleport of native asset from System Parachain to Relay Chain
+/// should work when there is enough balance in Relay Chain's `CheckAccount`
+#[test]
+fn limited_teleport_native_assets_back_from_system_para_to_relay_works() {
+	// Dependency - Relay Chain's `CheckAccount` should have enough balance
+	limited_teleport_native_assets_from_relay_to_system_para_works();
+
+	// Init values for Relay Chain
+	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+	let destination = AssetHubKusama::parent_location();
+	let beneficiary_id = KusamaReceiver::get();
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: KusamaReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubKusama>(para_origin_assertions);
+	test.set_assertion::<Kusama>(relay_dest_assertions);
+	test.set_dispatchable::<AssetHubKusama>(system_para_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
+}
+
+/// Limited Teleport of native asset from System Parachain to Relay Chain
+/// should't work when there is not enough balance in Relay Chain's `CheckAccount`
+#[test]
+fn limited_teleport_native_assets_from_system_para_to_relay_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+	let destination = AssetHubKusama::parent_location().into();
+	let beneficiary_id = KusamaReceiver::get().into();
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubKusamaSender::get(),
+		receiver: KusamaReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubKusama>(para_origin_assertions);
+	test.set_assertion::<Kusama>(relay_dest_assertions_fail);
+	test.set_dispatchable::<AssetHubKusama>(system_para_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance does not change
+	assert_eq!(receiver_balance_after, receiver_balance_before);
+}
+
+/// Teleport of native asset from Relay Chain to the System Parachain should work
+#[test]
+fn teleport_native_assets_from_relay_to_system_para_works() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = KUSAMA_ED * 1000;
+	let test_args = TestContext {
+		sender: KusamaSender::get(),
+		receiver: AssetHubKusamaReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Kusama>(relay_origin_assertions);
+	test.set_assertion::<AssetHubKusama>(para_dest_assertions);
+	test.set_dispatchable::<Kusama>(relay_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
+}
+
+// TODO: Uncomment when https://github.com/paritytech/polkadot/pull/7424 is merged
+
+// Right now it is failing in the Relay Chain with a
+// `messageQueue.ProcessingFailed` event `error: Unsupported`.
+// The reason is the `Weigher` in `pallet_xcm` is not properly calculating the `remote_weight`
+// and it cause an `Overweight` error in `AllowTopLevelPaidExecutionFrom` barrier
+
+// /// Teleport of native asset from System Parachains to the Relay Chain
+// /// should work when there is enough balance in Relay Chain's `CheckAccount`
+// #[test]
+// fn teleport_native_assets_back_from_system_para_to_relay_works() {
+// 	// Dependency - Relay Chain's `CheckAccount` should have enough balance
+// 	teleport_native_assets_from_relay_to_system_para_works();
+
+// 	// Init values for Relay Chain
+// 	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+// 	let test_args = TestContext {
+// 		sender: AssetHubKusamaSender::get(),
+// 		receiver: KusamaReceiver::get(),
+// 		args: get_para_dispatch_args(amount_to_send),
+// 	};
+
+// 	let mut test = SystemParaToRelayTest::new(test_args);
+
+// 	let sender_balance_before = test.sender.balance;
+// 	let receiver_balance_before = test.receiver.balance;
+
+// 	test.set_assertion::<AssetHubKusama>(para_origin_assertions);
+// 	test.set_assertion::<Kusama>(relay_dest_assertions);
+// 	test.set_dispatchable::<AssetHubKusama>(system_para_teleport_assets);
+// 	test.assert();
+
+// 	let sender_balance_after = test.sender.balance;
+// 	let receiver_balance_after = test.receiver.balance;
+
+// 	// Sender's balance is reduced
+// 	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+// 	// Receiver's balance is increased
+// 	assert!(receiver_balance_after > receiver_balance_before);
+// }
+
+// /// Teleport of native asset from System Parachain to Relay Chain
+// /// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount`
+// #[test]
+// fn teleport_native_assets_from_system_para_to_relay_fails() {
+// 	// Init values for Relay Chain
+// 	let amount_to_send: Balance = ASSET_HUB_KUSAMA_ED * 1000;
+//  let assets = (Parent, amount_to_send).into();
+//
+// 	let test_args = TestContext {
+// 		sender: AssetHubKusamaSender::get(),
+// 		receiver: KusamaReceiver::get(),
+// 		args: system_para_test_args(amount_to_send),
+//      assets,
+//      None
+// 	};
+
+// 	let mut test = SystemParaToRelayTest::new(test_args);
+
+// 	let sender_balance_before = test.sender.balance;
+// 	let receiver_balance_before = test.receiver.balance;
+
+// 	test.set_assertion::<AssetHubKusama>(para_origin_assertions);
+// 	test.set_assertion::<Kusama>(relay_dest_assertions);
+// 	test.set_dispatchable::<AssetHubKusama>(system_para_teleport_assets);
+// 	test.assert();
+
+// 	let sender_balance_after = test.sender.balance;
+// 	let receiver_balance_after = test.receiver.balance;
+
+// 	// Sender's balance is reduced
+// 	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+// 	// Receiver's balance does not change
+// 	assert_eq!(receiver_balance_after, receiver_balance_before);
+// }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/transact.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/transact.rs
deleted file mode 100644
index 65264c25203..00000000000
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/transact.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright Parity Technologies (UK) Ltd.
-// This file is part of Cumulus.
-
-// Cumulus is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Cumulus is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
-
-use crate::*;
-
-#[test]
-fn transact_sudo_from_relay_to_assets_para() {
-	// Init tests variables
-	// Call to be executed in Assets Parachain
-	const ASSET_ID: u32 = 1;
-
-	let call = <AssetHubKusama as Para>::RuntimeCall::Assets(pallet_assets::Call::<
-		<AssetHubKusama as Para>::Runtime,
-		Instance1,
-	>::force_create {
-		id: ASSET_ID.into(),
-		is_sufficient: true,
-		min_balance: 1000,
-		owner: AssetHubKusamaSender::get().into(),
-	})
-	.encode()
-	.into();
-
-	// XcmPallet send arguments
-	let sudo_origin = <Kusama as Relay>::RuntimeOrigin::root();
-	let assets_para_destination: VersionedMultiLocation =
-		Kusama::child_location_of(AssetHubKusama::para_id()).into();
-
-	let weight_limit = WeightLimit::Unlimited;
-	let require_weight_at_most = Weight::from_parts(1000000000, 200000);
-	let origin_kind = OriginKind::Superuser;
-	let check_origin = None;
-
-	let xcm = VersionedXcm::from(Xcm(vec![
-		UnpaidExecution { weight_limit, check_origin },
-		Transact { require_weight_at_most, origin_kind, call },
-	]));
-
-	// Send XCM message from Relay Chain
-	Kusama::execute_with(|| {
-		assert_ok!(<Kusama as KusamaPallet>::XcmPallet::send(
-			sudo_origin,
-			bx!(assets_para_destination),
-			bx!(xcm),
-		));
-
-		type RuntimeEvent = <Kusama as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Kusama,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubKusama::execute_with(|| {
-		assert!(<AssetHubKusama as AssetHubKusamaPallet>::Assets::asset_exists(ASSET_ID));
-	});
-}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml
index b518257b1a5..ceba1820d35 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml
@@ -28,8 +28,9 @@ pallet-xcm = { default-features = false, git = "https://github.com/paritytech/po
 
 # Cumulus
 parachains-common = { path = "../../../../common" }
-penpal-runtime = { path = "../../../../runtimes/testing/penpal" }
-asset-hub-polkadot-runtime = { path = "../../../../runtimes/assets/asset-hub-polkadot" }
+cumulus-pallet-dmp-queue = { path = "../../../../../pallets/dmp-queue" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
+cumulus-pallet-parachain-system = { path = "../../../../../pallets/parachain-system" }
 
 # Local
 xcm-emulator = { default-features = false, path = "../../../../../xcm/xcm-emulator" }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/lib.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/lib.rs
index 70506d98cbc..9d87458f876 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/lib.rs
@@ -16,35 +16,87 @@
 
 pub use codec::Encode;
 pub use frame_support::{
-	assert_ok, instances::Instance1, pallet_prelude::Weight, traits::fungibles::Inspect,
+	assert_err, assert_ok,
+	instances::Instance1,
+	pallet_prelude::Weight,
+	sp_runtime::{AccountId32, DispatchError, DispatchResult, MultiAddress},
+	traits::{fungibles::Inspect, OriginTrait},
 };
 pub use integration_tests_common::{
 	constants::{
 		accounts::{ALICE, BOB},
+		asset_hub_polkadot::ED as ASSET_HUB_POLKADOT_ED,
 		polkadot::ED as POLKADOT_ED,
 		PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
 	},
-	AccountId, AssetHubKusama, AssetHubKusamaPallet, AssetHubKusamaReceiver, AssetHubKusamaSender,
-	AssetHubPolkadot, AssetHubPolkadotPallet, AssetHubPolkadotReceiver, AssetHubPolkadotSender,
-	BridgeHubKusama, BridgeHubKusamaPallet, BridgeHubKusamaReceiver, BridgeHubKusamaSender,
-	BridgeHubPolkadot, BridgeHubPolkadotPallet, BridgeHubPolkadotReceiver, BridgeHubPolkadotSender,
-	Collectives, CollectivesPallet, CollectivesReceiver, CollectivesSender, Kusama, KusamaMockNet,
-	KusamaPallet, KusamaReceiver, KusamaSender, PenpalKusama, PenpalKusamaReceiver,
-	PenpalKusamaSender, PenpalPolkadot, PenpalPolkadotReceiver, PenpalPolkadotSender, Polkadot,
+	lazy_static::lazy_static,
+	xcm_transact_paid_execution, xcm_transact_unpaid_execution, AssetHubPolkadot,
+	AssetHubPolkadotPallet, AssetHubPolkadotReceiver, AssetHubPolkadotSender, BridgeHubPolkadot,
+	BridgeHubPolkadotPallet, BridgeHubPolkadotReceiver, BridgeHubPolkadotSender, Collectives,
+	CollectivesPallet, CollectivesReceiver, CollectivesSender, PenpalPolkadotA,
+	PenpalPolkadotAPallet, PenpalPolkadotAReceiver, PenpalPolkadotASender, PenpalPolkadotB,
+	PenpalPolkadotBPallet, PenpalPolkadotBReceiver, PenpalPolkadotBSender, Polkadot,
 	PolkadotMockNet, PolkadotPallet, PolkadotReceiver, PolkadotSender,
 };
+pub use parachains_common::{AccountId, Balance};
 pub use polkadot_core_primitives::InboundDownwardMessage;
+pub use polkadot_parachain::primitives::{HrmpChannelId, Id};
+pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
 pub use xcm::{
 	prelude::*,
-	v3::{
-		Error,
-		NetworkId::{Kusama as KusamaId, Polkadot as PolkadotId},
-	},
+	v3::{Error, NetworkId::Polkadot as PolkadotId},
+	DoubleEncoded,
 };
 pub use xcm_emulator::{
 	assert_expected_events, bx, cumulus_pallet_dmp_queue, helpers::weight_within_threshold,
-	Parachain as Para, RelayChain as Relay, TestExt,
+	AccountId32Junction, Chain, ParaId, Parachain as Para, RelayChain as Relay, Test, TestArgs,
+	TestContext, TestExt, TestExternalities,
 };
 
+pub const ASSET_ID: u32 = 1;
+pub const ASSET_MIN_BALANCE: u128 = 1000;
+// `Assets` pallet index
+pub const ASSETS_PALLET_ID: u8 = 50;
+
+pub type RelayToSystemParaTest = Test<Polkadot, AssetHubPolkadot>;
+pub type SystemParaToRelayTest = Test<AssetHubPolkadot, Polkadot>;
+pub type SystemParaToParaTest = Test<AssetHubPolkadot, PenpalPolkadotA>;
+
+/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
+pub fn relay_test_args(amount: Balance) -> TestArgs {
+	TestArgs {
+		dest: Polkadot::child_location_of(AssetHubPolkadot::para_id()),
+		beneficiary: AccountId32Junction {
+			network: None,
+			id: AssetHubPolkadotReceiver::get().into(),
+		}
+		.into(),
+		amount,
+		assets: (Here, amount).into(),
+		asset_id: None,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
+/// Returns a `TestArgs` instance to de used for the System Parachain accross integraton tests
+pub fn system_para_test_args(
+	dest: MultiLocation,
+	beneficiary_id: AccountId32,
+	amount: Balance,
+	assets: MultiAssets,
+	asset_id: Option<u32>,
+) -> TestArgs {
+	TestArgs {
+		dest,
+		beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
+		amount,
+		assets,
+		asset_id,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/hrmp_channels.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/hrmp_channels.rs
new file mode 100644
index 00000000000..e0cb340ddaa
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/hrmp_channels.rs
@@ -0,0 +1,201 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+const MAX_CAPACITY: u32 = 8;
+const MAX_MESSAGE_SIZE: u32 = 8192;
+
+/// Opening HRMP channels between Parachains should work
+#[test]
+fn open_hrmp_channel_between_paras_works() {
+	// Parchain A init values
+	let para_a_id = PenpalPolkadotA::para_id();
+	let para_a_root_origin = <PenpalPolkadotA as Chain>::RuntimeOrigin::root();
+
+	// Parachain B init values
+	let para_b_id = PenpalPolkadotB::para_id();
+	let para_b_root_origin = <PenpalPolkadotB as Chain>::RuntimeOrigin::root();
+
+	let fee_amount = POLKADOT_ED * 1000;
+	let fund_amount = POLKADOT_ED * 1000_000_000;
+
+	// Fund Parachain's Sovereign accounts to be able to reserve the deposit
+	let para_a_sovereign_account = Polkadot::fund_para_sovereign(fund_amount, para_a_id);
+	let para_b_sovereign_account = Polkadot::fund_para_sovereign(fund_amount, para_b_id);
+
+	let relay_destination: VersionedMultiLocation = PenpalPolkadotA::parent_location().into();
+
+	// ---- Init Open channel from Parachain to System Parachain
+	let mut call = Polkadot::init_open_channel_call(para_b_id, MAX_CAPACITY, MAX_MESSAGE_SIZE);
+	let origin_kind = OriginKind::Native;
+	let native_asset: MultiAsset = (Here, fee_amount).into();
+	let beneficiary = Polkadot::sovereign_account_id_of_child_para(para_a_id);
+
+	let mut xcm = xcm_transact_paid_execution(call, origin_kind, native_asset.clone(), beneficiary);
+
+	PenpalPolkadotA::execute_with(|| {
+		assert_ok!(<PenpalPolkadotA as PenpalPolkadotAPallet>::PolkadotXcm::send(
+			para_a_root_origin,
+			bx!(relay_destination.clone()),
+			bx!(xcm),
+		));
+	});
+
+	Polkadot::execute_with(|| {
+		type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+		Polkadot::assert_ump_queue_processed(
+			true,
+			Some(para_a_id),
+			Some(Weight::from_parts(1_282_426_000, 207_186)),
+		);
+
+		assert_expected_events!(
+			Polkadot,
+			vec![
+				// Parachain's Sovereign account balance is withdrawn to pay XCM fees
+				RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+					who: *who == para_a_sovereign_account.clone(),
+					amount: *amount == fee_amount,
+				},
+				// Sender deposit is reserved for Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == para_a_sovereign_account,
+				},
+				// Open channel requested from Para A to Para B
+				RuntimeEvent::Hrmp(
+					polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested(
+						sender, recipient, max_capacity, max_message_size
+					)
+				) => {
+					sender: *sender == para_a_id.into(),
+					recipient: *recipient == para_b_id.into(),
+					max_capacity: *max_capacity == MAX_CAPACITY,
+					max_message_size: *max_message_size == MAX_MESSAGE_SIZE,
+				},
+			]
+		);
+	});
+
+	// ---- Accept Open channel from Parachain to System Parachain
+	call = Polkadot::accept_open_channel_call(para_a_id);
+	let beneficiary = Polkadot::sovereign_account_id_of_child_para(para_b_id);
+
+	xcm = xcm_transact_paid_execution(call, origin_kind, native_asset, beneficiary);
+
+	PenpalPolkadotB::execute_with(|| {
+		assert_ok!(<PenpalPolkadotB as PenpalPolkadotBPallet>::PolkadotXcm::send(
+			para_b_root_origin,
+			bx!(relay_destination),
+			bx!(xcm),
+		));
+	});
+
+	Polkadot::execute_with(|| {
+		type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+		Polkadot::assert_ump_queue_processed(
+			true,
+			Some(para_b_id),
+			Some(Weight::from_parts(1_282_426_000, 207_186)),
+		);
+
+		assert_expected_events!(
+			Polkadot,
+			vec![
+				// Parachain's Sovereign account balance is withdrawn to pay XCM fees
+				RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+					who: *who == para_b_sovereign_account.clone(),
+					amount: *amount == fee_amount,
+				},
+				// Sender deposit is reserved for Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == para_b_sovereign_account,
+				},
+				// Open channel accepted for Para A to Para B
+				RuntimeEvent::Hrmp(
+					polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted(
+						sender, recipient
+					)
+				) => {
+					sender: *sender == para_a_id.into(),
+					recipient: *recipient == para_b_id.into(),
+				},
+			]
+		);
+	});
+
+	Polkadot::force_process_hrmp_open(para_a_id, para_b_id);
+}
+
+/// Opening HRMP channels between System Parachains and Parachains should work
+#[test]
+fn force_open_hrmp_channel_for_system_para_works() {
+	// Relay Chain init values
+	let relay_root_origin = <Polkadot as Chain>::RuntimeOrigin::root();
+
+	// System Para init values
+	let system_para_id = AssetHubPolkadot::para_id();
+
+	// Parachain A init values
+	let para_a_id = PenpalPolkadotA::para_id();
+
+	let fund_amount = POLKADOT_ED * 1000_000_000;
+
+	// Fund Parachain's Sovereign accounts to be able to reserve the deposit
+	let system_para_sovereign_account = Polkadot::fund_para_sovereign(fund_amount, system_para_id);
+	let para_a_sovereign_account = Polkadot::fund_para_sovereign(fund_amount, para_a_id);
+
+	Polkadot::execute_with(|| {
+		assert_ok!(<Polkadot as PolkadotPallet>::Hrmp::force_open_hrmp_channel(
+			relay_root_origin,
+			system_para_id,
+			para_a_id,
+			MAX_CAPACITY,
+			MAX_MESSAGE_SIZE
+		));
+
+		type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+		assert_expected_events!(
+			Polkadot,
+			vec![
+				// Sender deposit is reserved for System Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == system_para_sovereign_account,
+				},
+				// Recipient deposit is reserved for Parachain's Sovereign account
+				RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
+					who: *who == para_a_sovereign_account,
+				},
+				// HRMP channel forced opened
+				RuntimeEvent::Hrmp(
+					polkadot_runtime_parachains::hrmp::Event::HrmpChannelForceOpened(
+						sender, recipient, max_capacity, max_message_size
+					)
+				) => {
+					sender: *sender == system_para_id.into(),
+					recipient: *recipient == para_a_id.into(),
+					max_capacity: *max_capacity == MAX_CAPACITY,
+					max_message_size: *max_message_size == MAX_MESSAGE_SIZE,
+				},
+			]
+		);
+	});
+
+	Polkadot::force_process_hrmp_open(system_para_id, para_a_id);
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs
index 44861d2a872..00e0a663e47 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs
@@ -14,6 +14,8 @@
 // You should have received a copy of the GNU General Public License
 // along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
 
+mod hrmp_channels;
 mod reserve_transfer;
+mod send;
+mod set_xcm_versions;
 mod teleport;
-mod transact;
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/reserve_transfer.rs
index ffe9bac86d5..6b1ecde6a14 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/reserve_transfer.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/reserve_transfer.rs
@@ -16,66 +16,395 @@
 
 use crate::*;
 
+fn relay_origin_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+	Polkadot::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(629_384_000, 6_196)));
+
+	assert_expected_events!(
+		Polkadot,
+		vec![
+			// Amount to reserve transfer is transferred to System Parachain's Sovereign account
+			RuntimeEvent::Balances(pallet_balances::Event::Transfer { from, to, amount }) => {
+				from: *from == t.sender.account_id,
+				to: *to == Polkadot::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount:  *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn system_para_dest_assertions_incomplete(_t: RelayToSystemParaTest) {
+	AssetHubPolkadot::assert_dmp_queue_incomplete(
+		Some(Weight::from_parts(1_000_000_000, 0)),
+		Some(Error::UntrustedReserveLocation),
+	);
+}
+
+fn system_para_to_relay_assertions(_t: SystemParaToRelayTest) {
+	AssetHubPolkadot::assert_xcm_pallet_attempted_error(Some(XcmError::Barrier))
+}
+
+fn system_para_to_para_assertions(t: SystemParaToParaTest) {
+	type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+	AssetHubPolkadot::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		676_119_000,
+		6196,
+	)));
+
+	assert_expected_events!(
+		AssetHubPolkadot,
+		vec![
+			// Amount to reserve transfer is transferred to Parachain's Sovereing account
+			RuntimeEvent::Balances(
+				pallet_balances::Event::Transfer { from, to, amount }
+			) => {
+				from: *from == t.sender.account_id,
+				to: *to == AssetHubPolkadot::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
+	type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+	AssetHubPolkadot::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		676_119_000,
+		6196,
+	)));
+
+	assert_expected_events!(
+		AssetHubPolkadot,
+		vec![
+			// Amount to reserve transfer is transferred to Parachain's Sovereing account
+			RuntimeEvent::Assets(
+				pallet_assets::Event::Transferred { asset_id, from, to, amount }
+			) => {
+				asset_id: *asset_id == ASSET_ID,
+				from: *from == t.sender.account_id,
+				to: *to == AssetHubPolkadot::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn relay_limited_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Polkadot as PolkadotPallet>::XcmPallet::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn relay_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Polkadot as PolkadotPallet>::XcmPallet::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_limited_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn system_para_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
+	<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
+	<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+/// Limited Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
 #[test]
-fn reserve_transfer_native_asset_from_relay_to_assets() {
-	// Init tests variables
-	let amount = POLKADOT_ED * 1000;
-	let relay_sender_balance_before = Polkadot::account_data_of(PolkadotSender::get()).free;
-	let para_receiver_balance_before =
-		AssetHubPolkadot::account_data_of(AssetHubPolkadotReceiver::get()).free;
-
-	let origin = <Polkadot as Relay>::RuntimeOrigin::signed(PolkadotSender::get());
-	let assets_para_destination: VersionedMultiLocation =
-		Polkadot::child_location_of(AssetHubPolkadot::para_id()).into();
-	let beneficiary: VersionedMultiLocation =
-		AccountId32 { network: None, id: AssetHubPolkadotReceiver::get().into() }.into();
-	let native_assets: VersionedMultiAssets = (Here, amount).into();
-	let fee_asset_item = 0;
-	let weight_limit = WeightLimit::Unlimited;
-
-	// Send XCM message from Relay Chain
-	Polkadot::execute_with(|| {
-		assert_ok!(<Polkadot as PolkadotPallet>::XcmPallet::limited_reserve_transfer_assets(
-			origin,
-			bx!(assets_para_destination),
-			bx!(beneficiary),
-			bx!(native_assets),
-			fee_asset_item,
-			weight_limit,
-		));
-
-		type RuntimeEvent = <Polkadot as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Polkadot,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => {
-					weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(686_043_000, 6196), *weight),
-				},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubPolkadot::execute_with(|| {
-		type RuntimeEvent = <AssetHubPolkadot as Para>::RuntimeEvent;
-
-		assert_expected_events!(
-			AssetHubPolkadot,
-			vec![
-				RuntimeEvent::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward {
-					outcome: Outcome::Incomplete(_, Error::UntrustedReserveLocation),
-					..
-				}) => {},
-			]
-		);
-	});
-
-	// Check if balances are updated accordingly in Relay Chain and Assets Parachain
-	let relay_sender_balance_after = Polkadot::account_data_of(PolkadotSender::get()).free;
-	let para_sender_balance_after =
-		AssetHubPolkadot::account_data_of(AssetHubPolkadotReceiver::get()).free;
-
-	assert_eq!(relay_sender_balance_before - amount, relay_sender_balance_after);
-	assert_eq!(para_sender_balance_after, para_receiver_balance_before);
+fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = POLKADOT_ED * 1000;
+	let test_args = TestContext {
+		sender: PolkadotSender::get(),
+		receiver: AssetHubPolkadotReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Polkadot>(relay_origin_assertions);
+	test.set_assertion::<AssetHubPolkadot>(system_para_dest_assertions_incomplete);
+	test.set_dispatchable::<Polkadot>(relay_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Limited Reserve Transfers of native asset from System Parachain to Relay Chain shoudln't work
+#[test]
+fn limited_reserve_transfer_native_asset_from_system_para_to_relay_fails() {
+	// Init values for System Parachain
+	let destination = AssetHubPolkadot::parent_location();
+	let beneficiary_id = PolkadotReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PolkadotReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubPolkadot>(system_para_to_relay_assertions);
+	test.set_dispatchable::<AssetHubPolkadot>(system_para_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
+#[test]
+fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = POLKADOT_ED * 1000;
+	let test_args = TestContext {
+		sender: PolkadotSender::get(),
+		receiver: AssetHubPolkadotReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Polkadot>(relay_origin_assertions);
+	test.set_assertion::<AssetHubPolkadot>(system_para_dest_assertions_incomplete);
+	test.set_dispatchable::<Polkadot>(relay_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work
+#[test]
+fn reserve_transfer_native_asset_from_system_para_to_relay_fails() {
+	// Init values for System Parachain
+	let destination = AssetHubPolkadot::parent_location();
+	let beneficiary_id = PolkadotReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PolkadotReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubPolkadot>(system_para_to_relay_assertions);
+	test.set_dispatchable::<AssetHubPolkadot>(system_para_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Limited Reserve Transfers of native asset from System Parachain to Parachain should work
+#[test]
+fn limited_reserve_transfer_native_asset_from_system_para_to_para() {
+	// Init values for System Parachain
+	let destination = AssetHubPolkadot::sibling_location_of(PenpalPolkadotA::para_id());
+	let beneficiary_id = PenpalPolkadotAReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PenpalPolkadotAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+
+	test.set_assertion::<AssetHubPolkadot>(system_para_to_para_assertions);
+	// TODO: Add assertion for Penpal runtime. Right now message is failing with `UntrustedReserveLocation`
+	test.set_dispatchable::<AssetHubPolkadot>(system_para_to_para_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve transfers
+}
+
+/// Reserve Transfers of native asset from System Parachain to Parachain should work
+#[test]
+fn reserve_transfer_native_asset_from_system_para_to_para() {
+	// Init values for System Parachain
+	let destination = AssetHubPolkadot::sibling_location_of(PenpalPolkadotA::para_id());
+	let beneficiary_id = PenpalPolkadotAReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PenpalPolkadotAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+
+	test.set_assertion::<AssetHubPolkadot>(system_para_to_para_assertions);
+	// TODO: Add assertion for Penpal runtime. Right now message is failing with `UntrustedReserveLocation`
+	test.set_dispatchable::<AssetHubPolkadot>(system_para_to_para_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve transfers
+}
+
+/// Limited Reserve Transfers of a local asset from System Parachain to Parachain should work
+#[test]
+fn limited_reserve_transfer_asset_from_system_para_to_para() {
+	// Force create asset from Relay Chain and mint assets for System Parachain's sender account
+	AssetHubPolkadot::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		AssetHubPolkadotSender::get(),
+		ASSET_MIN_BALANCE * 1000000,
+	);
+
+	// Init values for System Parachain
+	let destination = AssetHubPolkadot::sibling_location_of(PenpalPolkadotA::para_id());
+	let beneficiary_id = PenpalPolkadotAReceiver::get();
+	let amount_to_send = ASSET_MIN_BALANCE * 1000;
+	let assets =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
+			.into();
+
+	let system_para_test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PenpalPolkadotAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
+
+	system_para_test.set_assertion::<AssetHubPolkadot>(system_para_to_para_assets_assertions);
+	// TODO: Add assertions when Penpal is able to manage assets
+	system_para_test
+		.set_dispatchable::<AssetHubPolkadot>(system_para_to_para_limited_reserve_transfer_assets);
+	system_para_test.assert();
+}
+
+/// Reserve Transfers of a local asset from System Parachain to Parachain should work
+#[test]
+fn reserve_transfer_asset_from_system_para_to_para() {
+	// Force create asset from Relay Chain and mint assets for System Parachain's sender account
+	AssetHubPolkadot::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		AssetHubPolkadotSender::get(),
+		ASSET_MIN_BALANCE * 1000000,
+	);
+
+	// Init values for System Parachain
+	let destination = AssetHubPolkadot::sibling_location_of(PenpalPolkadotA::para_id());
+	let beneficiary_id = PenpalPolkadotAReceiver::get();
+	let amount_to_send = ASSET_MIN_BALANCE * 1000;
+	let assets =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
+			.into();
+
+	let system_para_test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PenpalPolkadotAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
+
+	system_para_test.set_assertion::<AssetHubPolkadot>(system_para_to_para_assets_assertions);
+	// TODO: Add assertions when Penpal is able to manage assets
+	system_para_test
+		.set_dispatchable::<AssetHubPolkadot>(system_para_to_para_reserve_transfer_assets);
+	system_para_test.assert();
 }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/send.rs
new file mode 100644
index 00000000000..ef34d1b4337
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/send.rs
@@ -0,0 +1,202 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+/// Relay Chain should be able to execute `Transact` instructions in System Parachain
+/// when `OriginKind::Superuser` and signer is `sudo`
+#[test]
+fn send_transact_sudo_from_relay_to_system_para_works() {
+	// Init tests variables
+	let root_origin = <Polkadot as Chain>::RuntimeOrigin::root();
+	let system_para_destination = Polkadot::child_location_of(AssetHubPolkadot::para_id()).into();
+	let asset_owner: AccountId = AssetHubPolkadotSender::get().into();
+	let xcm = AssetHubPolkadot::force_create_asset_xcm(
+		OriginKind::Superuser,
+		ASSET_ID,
+		asset_owner.clone(),
+		true,
+		1000,
+	);
+	// Send XCM message from Relay Chain
+	Polkadot::execute_with(|| {
+		assert_ok!(<Polkadot as PolkadotPallet>::XcmPallet::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		Polkadot::assert_xcm_pallet_sent();
+	});
+
+	// Receive XCM message in Assets Parachain
+	AssetHubPolkadot::execute_with(|| {
+		type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+		AssetHubPolkadot::assert_dmp_queue_complete(Some(Weight::from_parts(
+			1_019_445_000,
+			200_000,
+		)));
+
+		assert_expected_events!(
+			AssetHubPolkadot,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::ForceCreated { asset_id, owner }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == asset_owner,
+				},
+			]
+		);
+
+		assert!(<AssetHubPolkadot as AssetHubPolkadotPallet>::Assets::asset_exists(ASSET_ID));
+	});
+}
+
+/// Relay Chain shouldn't be able to execute `Transact` instructions in System Parachain
+/// when `OriginKind::Native`
+#[test]
+fn send_transact_native_from_relay_to_system_para_fails() {
+	// Init tests variables
+	let signed_origin = <Polkadot as Chain>::RuntimeOrigin::signed(PolkadotSender::get().into());
+	let system_para_destination = Polkadot::child_location_of(AssetHubPolkadot::para_id()).into();
+	let asset_owner = AssetHubPolkadotSender::get().into();
+	let xcm = AssetHubPolkadot::force_create_asset_xcm(
+		OriginKind::Native,
+		ASSET_ID,
+		asset_owner,
+		true,
+		1000,
+	);
+
+	// Send XCM message from Relay Chain
+	Polkadot::execute_with(|| {
+		assert_err!(
+			<Polkadot as PolkadotPallet>::XcmPallet::send(
+				signed_origin,
+				bx!(system_para_destination),
+				bx!(xcm)
+			),
+			DispatchError::BadOrigin
+		);
+	});
+}
+
+/// System Parachain shouldn't be able to execute `Transact` instructions in Relay Chain
+/// when `OriginKind::Native`
+#[test]
+fn send_transact_native_from_system_para_to_relay_fails() {
+	// Init tests variables
+	let signed_origin =
+		<AssetHubPolkadot as Chain>::RuntimeOrigin::signed(AssetHubPolkadotSender::get().into());
+	let relay_destination = AssetHubPolkadot::parent_location().into();
+	let call = <Polkadot as Chain>::RuntimeCall::System(frame_system::Call::<
+		<Polkadot as Chain>::Runtime,
+	>::remark_with_event {
+		remark: vec![0, 1, 2, 3],
+	})
+	.encode()
+	.into();
+	let origin_kind = OriginKind::Native;
+
+	let xcm = xcm_transact_unpaid_execution(call, origin_kind);
+
+	// Send XCM message from Relay Chain
+	AssetHubPolkadot::execute_with(|| {
+		assert_err!(
+			<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::send(
+				signed_origin,
+				bx!(relay_destination),
+				bx!(xcm)
+			),
+			DispatchError::BadOrigin
+		);
+	});
+}
+
+/// Parachain should be able to send XCM paying its fee with sufficient asset
+/// in the System Parachain
+#[test]
+fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
+	let para_sovereign_account = AssetHubPolkadot::sovereign_account_id_of(
+		AssetHubPolkadot::sibling_location_of(PenpalPolkadotA::para_id()),
+	);
+
+	// Force create and mint assets for Parachain's sovereign account
+	AssetHubPolkadot::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		para_sovereign_account.clone(),
+		ASSET_MIN_BALANCE * 1000000000,
+	);
+
+	// We just need a call that can pass the `SafeCallFilter`
+	// Call values are not relevant
+	let call = AssetHubPolkadot::force_create_asset_call(
+		ASSET_ID,
+		para_sovereign_account.clone(),
+		true,
+		ASSET_MIN_BALANCE,
+	);
+
+	let origin_kind = OriginKind::SovereignAccount;
+	let fee_amount = ASSET_MIN_BALANCE * 1000000;
+	let native_asset =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into();
+
+	let root_origin = <PenpalPolkadotA as Chain>::RuntimeOrigin::root();
+	let system_para_destination =
+		PenpalPolkadotA::sibling_location_of(AssetHubPolkadot::para_id()).into();
+	let xcm = xcm_transact_paid_execution(
+		call,
+		origin_kind,
+		native_asset,
+		para_sovereign_account.clone(),
+	);
+
+	PenpalPolkadotA::execute_with(|| {
+		assert_ok!(<PenpalPolkadotA as PenpalPolkadotAPallet>::PolkadotXcm::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		AssetHubPolkadot::assert_xcm_pallet_sent();
+	});
+
+	AssetHubPolkadot::execute_with(|| {
+		type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+		AssetHubPolkadot::assert_xcmp_queue_success(Some(Weight::from_parts(
+			2_176_414_000,
+			203_593,
+		)));
+
+		assert_expected_events!(
+			AssetHubPolkadot,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == para_sovereign_account,
+					balance: *balance == fee_amount,
+				},
+				RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
+					asset_id: *asset_id == ASSET_ID,
+				},
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/set_xcm_versions.rs
new file mode 100644
index 00000000000..84abf630e50
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/set_xcm_versions.rs
@@ -0,0 +1,97 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+#[test]
+fn relay_sets_system_para_xcm_supported_version() {
+	// Init tests variables
+	let sudo_origin = <Polkadot as Chain>::RuntimeOrigin::root();
+	let system_para_destination: MultiLocation =
+		Polkadot::child_location_of(AssetHubPolkadot::para_id());
+
+	// Relay Chain sets supported version for Asset Parachain
+	Polkadot::execute_with(|| {
+		assert_ok!(<Polkadot as PolkadotPallet>::XcmPallet::force_xcm_version(
+			sudo_origin,
+			bx!(system_para_destination),
+			XCM_V3
+		));
+
+		type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+		assert_expected_events!(
+			Polkadot,
+			vec![
+				RuntimeEvent::XcmPallet(pallet_xcm::Event::SupportedVersionChanged {
+					location,
+					version: XCM_V3
+				}) => { location: *location == system_para_destination, },
+			]
+		);
+	});
+}
+
+#[test]
+fn system_para_sets_relay_xcm_supported_version() {
+	// Init test variables
+	let sudo_origin = <Polkadot as Chain>::RuntimeOrigin::root();
+	let parent_location = AssetHubPolkadot::parent_location();
+	let system_para_destination: VersionedMultiLocation =
+		Polkadot::child_location_of(AssetHubPolkadot::para_id()).into();
+	let call = <AssetHubPolkadot as Chain>::RuntimeCall::PolkadotXcm(pallet_xcm::Call::<
+		<AssetHubPolkadot as Chain>::Runtime,
+	>::force_xcm_version {
+		location: bx!(parent_location),
+		version: XCM_V3,
+	})
+	.encode()
+	.into();
+	let origin_kind = OriginKind::Superuser;
+
+	let xcm = xcm_transact_unpaid_execution(call, origin_kind);
+
+	// System Parachain sets supported version for Relay Chain throught it
+	Polkadot::execute_with(|| {
+		assert_ok!(<Polkadot as PolkadotPallet>::XcmPallet::send(
+			sudo_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		Polkadot::assert_xcm_pallet_sent();
+	});
+
+	// System Parachain receive the XCM message
+	AssetHubPolkadot::execute_with(|| {
+		type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+		AssetHubPolkadot::assert_dmp_queue_complete(Some(Weight::from_parts(
+			1_019_210_000,
+			200_000,
+		)));
+
+		assert_expected_events!(
+			AssetHubPolkadot,
+			vec![
+				RuntimeEvent::PolkadotXcm(pallet_xcm::Event::SupportedVersionChanged {
+					location,
+					version: XCM_V3
+				}) => { location: *location == parent_location, },
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/teleport.rs
index 6b799096054..5d40e5d3538 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/teleport.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/teleport.rs
@@ -16,63 +16,347 @@
 
 use crate::*;
 
+fn relay_origin_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+	Polkadot::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(632_207_000, 7_186)));
+
+	assert_expected_events!(
+		Polkadot,
+		vec![
+			// Amount to teleport is withdrawn from Sender
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == t.sender.account_id,
+				amount: *amount == t.args.amount,
+			},
+			// Amount to teleport is deposited in Relay's `CheckAccount`
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, amount }) => {
+				who: *who == <Polkadot as PolkadotPallet>::XcmPallet::check_account(),
+				amount:  *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn relay_dest_assertions(t: SystemParaToRelayTest) {
+	type RuntimeEvent = <Polkadot as Chain>::RuntimeEvent;
+
+	Polkadot::assert_ump_queue_processed(
+		true,
+		Some(AssetHubPolkadot::para_id()),
+		Some(Weight::from_parts(368_931_000, 7_186)),
+	);
+
+	assert_expected_events!(
+		Polkadot,
+		vec![
+			// Amount is witdrawn from Relay Chain's `CheckAccount`
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == <Polkadot as PolkadotPallet>::XcmPallet::check_account(),
+				amount: *amount == t.args.amount,
+			},
+			// Amount minus fees are deposited in Receiver's account
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
+				who: *who == t.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) {
+	Polkadot::assert_ump_queue_processed(
+		false,
+		Some(AssetHubPolkadot::para_id()),
+		Some(Weight::from_parts(232_982_000, 3_593)),
+	);
+}
+
+fn para_origin_assertions(t: SystemParaToRelayTest) {
+	type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+	AssetHubPolkadot::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		632_207_000,
+		7_186,
+	)));
+
+	AssetHubPolkadot::assert_parachain_system_ump_sent();
+
+	assert_expected_events!(
+		AssetHubPolkadot,
+		vec![
+			// Amount is withdrawn from Sender's account
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == t.sender.account_id,
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn para_dest_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
+
+	AssetHubPolkadot::assert_dmp_queue_complete(Some(Weight::from_parts(161_196_000, 0)));
+
+	assert_expected_events!(
+		AssetHubPolkadot,
+		vec![
+			// Amount minus fees are deposited in Receiver's account
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
+				who: *who == t.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Polkadot as PolkadotPallet>::XcmPallet::limited_teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn relay_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Polkadot as PolkadotPallet>::XcmPallet::teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::limited_teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+// TODO: Uncomment when https://github.com/paritytech/polkadot/pull/7424 is merged
+// fn system_para_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
+// 	<AssetHubPolkadot as AssetHubPolkadotPallet>::PolkadotXcm::teleport_assets(
+// 		t.signed_origin,
+// 		bx!(t.args.dest),
+// 		bx!(t.args.beneficiary),
+// 		bx!(t.args.assets),
+// 		t.args.fee_asset_item,
+// 	)
+// }
+
+/// Limited Teleport of native asset from Relay Chain to the System Parachain should work
 #[test]
-fn teleport_native_assets_from_relay_to_assets_para() {
-	// Init tests variables
-	let amount = POLKADOT_ED * 1000;
-	let relay_sender_balance_before = Polkadot::account_data_of(PolkadotSender::get()).free;
-	let para_receiver_balance_before =
-		AssetHubPolkadot::account_data_of(AssetHubPolkadotReceiver::get()).free;
-
-	let origin = <Polkadot as Relay>::RuntimeOrigin::signed(PolkadotSender::get());
-	let assets_para_destination: VersionedMultiLocation =
-		Polkadot::child_location_of(AssetHubPolkadot::para_id()).into();
-	let beneficiary: VersionedMultiLocation =
-		AccountId32 { network: None, id: AssetHubPolkadotReceiver::get().into() }.into();
-	let native_assets: VersionedMultiAssets = (Here, amount).into();
-	let fee_asset_item = 0;
-	let weight_limit = WeightLimit::Unlimited;
-
-	// Send XCM message from Relay Chain
-	Polkadot::execute_with(|| {
-		assert_ok!(<Polkadot as PolkadotPallet>::XcmPallet::limited_teleport_assets(
-			origin,
-			bx!(assets_para_destination),
-			bx!(beneficiary),
-			bx!(native_assets),
-			fee_asset_item,
-			weight_limit,
-		));
-
-		type RuntimeEvent = <Polkadot as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Polkadot,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }) => {},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubPolkadot::execute_with(|| {
-		type RuntimeEvent = <AssetHubPolkadot as Para>::RuntimeEvent;
-
-		assert_expected_events!(
-			AssetHubPolkadot,
-			vec![
-				RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
-					who: *who == AssetHubPolkadotReceiver::get().into(),
-				},
-			]
-		);
-	});
-
-	// Check if balances are updated accordingly in Relay Chain and Assets Parachain
-	let relay_sender_balance_after = Polkadot::account_data_of(PolkadotSender::get()).free;
-	let para_sender_balance_after =
-		AssetHubPolkadot::account_data_of(AssetHubPolkadotReceiver::get()).free;
-
-	assert_eq!(relay_sender_balance_before - amount, relay_sender_balance_after);
-	assert!(para_sender_balance_after > para_receiver_balance_before);
+fn limited_teleport_native_assets_from_relay_to_system_para_works() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = POLKADOT_ED * 1000;
+	let test_args = TestContext {
+		sender: PolkadotSender::get(),
+		receiver: AssetHubPolkadotReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Polkadot>(relay_origin_assertions);
+	test.set_assertion::<AssetHubPolkadot>(para_dest_assertions);
+	test.set_dispatchable::<Polkadot>(relay_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
 }
+
+/// Limited Teleport of native asset from System Parachain to Relay Chain
+/// should work when there is enough balance in Relay Chain's `CheckAccount`
+#[test]
+fn limited_teleport_native_assets_back_from_system_para_to_relay_works() {
+	// Dependency - Relay Chain's `CheckAccount` should have enough balance
+	limited_teleport_native_assets_from_relay_to_system_para_works();
+
+	// Init values for Relay Chain
+	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+	let destination = AssetHubPolkadot::parent_location();
+	let beneficiary_id = PolkadotReceiver::get();
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PolkadotReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubPolkadot>(para_origin_assertions);
+	test.set_assertion::<Polkadot>(relay_dest_assertions);
+	test.set_dispatchable::<AssetHubPolkadot>(system_para_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
+}
+
+/// Limited Teleport of native asset from System Parachain to Relay Chain
+/// should't work when there is not enough balance in Relay Chain's `CheckAccount`
+#[test]
+fn limited_teleport_native_assets_from_system_para_to_relay_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+	let destination = AssetHubPolkadot::parent_location().into();
+	let beneficiary_id = PolkadotReceiver::get().into();
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubPolkadotSender::get(),
+		receiver: PolkadotReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubPolkadot>(para_origin_assertions);
+	test.set_assertion::<Polkadot>(relay_dest_assertions_fail);
+	test.set_dispatchable::<AssetHubPolkadot>(system_para_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance does not change
+	assert_eq!(receiver_balance_after, receiver_balance_before);
+}
+
+/// Teleport of native asset from Relay Chain to the System Parachain should work
+#[test]
+fn teleport_native_assets_from_relay_to_system_para_works() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = POLKADOT_ED * 1000;
+	let test_args = TestContext {
+		sender: PolkadotSender::get(),
+		receiver: AssetHubPolkadotReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Polkadot>(relay_origin_assertions);
+	test.set_assertion::<AssetHubPolkadot>(para_dest_assertions);
+	test.set_dispatchable::<Polkadot>(relay_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
+}
+
+// TODO: Uncomment when https://github.com/paritytech/polkadot/pull/7424 is merged
+
+// Right now it is failing in the Relay Chain with a
+// `messageQueue.ProcessingFailed` event `error: Unsupported`.
+// The reason is the `Weigher` in `pallet_xcm` is not properly calculating the `remote_weight`
+// and it cause an `Overweight` error in `AllowTopLevelPaidExecutionFrom` barrier
+
+// /// Teleport of native asset from System Parachains to the Relay Chain
+// /// should work when there is enough balance in Relay Chain's `CheckAccount`
+// #[test]
+// fn teleport_native_assets_back_from_system_para_to_relay_works() {
+// 	// Dependency - Relay Chain's `CheckAccount` should have enough balance
+// 	teleport_native_assets_from_relay_to_system_para_works();
+
+// 	// Init values for Relay Chain
+// 	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+// 	let test_args = TestContext {
+// 		sender: AssetHubPolkadotSender::get(),
+// 		receiver: PolkadotReceiver::get(),
+// 		args: get_para_dispatch_args(amount_to_send),
+// 	};
+
+// 	let mut test = SystemParaToRelayTest::new(test_args);
+
+// 	let sender_balance_before = test.sender.balance;
+// 	let receiver_balance_before = test.receiver.balance;
+
+// 	test.set_assertion::<AssetHubPolkadot>(para_origin_assertions);
+// 	test.set_assertion::<Polkadot>(relay_dest_assertions);
+// 	test.set_dispatchable::<AssetHubPolkadot>(system_para_teleport_assets);
+// 	test.assert();
+
+// 	let sender_balance_after = test.sender.balance;
+// 	let receiver_balance_after = test.receiver.balance;
+
+// 	// Sender's balance is reduced
+// 	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+// 	// Receiver's balance is increased
+// 	assert!(receiver_balance_after > receiver_balance_before);
+// }
+
+// /// Teleport of native asset from System Parachain to Relay Chain
+// /// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount`
+// #[test]
+// fn teleport_native_assets_from_system_para_to_relay_fails() {
+// 	// Init values for Relay Chain
+// 	let amount_to_send: Balance = ASSET_HUB_POLKADOT_ED * 1000;
+//  let assets = (Parent, amount_to_send).into();
+//
+// 	let test_args = TestContext {
+// 		sender: AssetHubPolkadotSender::get(),
+// 		receiver: PolkadotReceiver::get(),
+// 		args: system_para_test_args(amount_to_send),
+//      assets,
+//      None
+// 	};
+
+// 	let mut test = SystemParaToRelayTest::new(test_args);
+
+// 	let sender_balance_before = test.sender.balance;
+// 	let receiver_balance_before = test.receiver.balance;
+
+// 	test.set_assertion::<AssetHubPolkadot>(para_origin_assertions);
+// 	test.set_assertion::<Polkadot>(relay_dest_assertions);
+// 	test.set_dispatchable::<AssetHubPolkadot>(system_para_teleport_assets);
+// 	test.assert();
+
+// 	let sender_balance_after = test.sender.balance;
+// 	let receiver_balance_after = test.receiver.balance;
+
+// 	// Sender's balance is reduced
+// 	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+// 	// Receiver's balance does not change
+// 	assert_eq!(receiver_balance_after, receiver_balance_before);
+// }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/transact.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/transact.rs
deleted file mode 100644
index dc2f1e87d4b..00000000000
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/transact.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright Parity Technologies (UK) Ltd.
-// This file is part of Cumulus.
-
-// Cumulus is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Cumulus is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
-
-use crate::*;
-
-#[test]
-fn transact_sudo_from_relay_to_assets_para() {
-	// Init tests variables
-	// Call to be executed in Assets Parachain
-	const ASSET_ID: u32 = 1;
-
-	let call = <AssetHubPolkadot as Para>::RuntimeCall::Assets(pallet_assets::Call::<
-		<AssetHubPolkadot as Para>::Runtime,
-		Instance1,
-	>::force_create {
-		id: ASSET_ID.into(),
-		is_sufficient: true,
-		min_balance: 1000,
-		owner: AssetHubPolkadotSender::get().into(),
-	})
-	.encode()
-	.into();
-
-	// XcmPallet send arguments
-	let sudo_origin = <Polkadot as Relay>::RuntimeOrigin::root();
-	let assets_para_destination: VersionedMultiLocation =
-		Polkadot::child_location_of(AssetHubPolkadot::para_id()).into();
-
-	let weight_limit = WeightLimit::Unlimited;
-	let require_weight_at_most = Weight::from_parts(1000000000, 200000);
-	let origin_kind = OriginKind::Superuser;
-	let check_origin = None;
-
-	let xcm = VersionedXcm::from(Xcm(vec![
-		UnpaidExecution { weight_limit, check_origin },
-		Transact { require_weight_at_most, origin_kind, call },
-	]));
-
-	// Send XCM message from Relay Chain
-	Polkadot::execute_with(|| {
-		assert_ok!(<Polkadot as PolkadotPallet>::XcmPallet::send(
-			sudo_origin,
-			bx!(assets_para_destination),
-			bx!(xcm),
-		));
-
-		type RuntimeEvent = <Polkadot as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Polkadot,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubPolkadot::execute_with(|| {
-		assert!(<AssetHubPolkadot as AssetHubPolkadotPallet>::Assets::asset_exists(ASSET_ID));
-	});
-}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml
index 8e9b1dd6603..6b2bb18ae8b 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml
@@ -24,15 +24,16 @@ polkadot-core-primitives = { default-features = false, git = "https://github.com
 polkadot-parachain = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
 polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" }
 polkadot-runtime = { git = "https://github.com/paritytech/polkadot", branch = "master" }
-westend-runtime = { git = "https://github.com/paritytech/polkadot", branch = "master" }
 xcm = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
 xcm-executor = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
 pallet-xcm = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
 
 # Cumulus
 parachains-common = { path = "../../../../common" }
-penpal-runtime = { path = "../../../../runtimes/testing/penpal" }
 asset-hub-westend-runtime = { path = "../../../../runtimes/assets/asset-hub-westend" }
+cumulus-pallet-dmp-queue = { path = "../../../../../pallets/dmp-queue" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
+cumulus-pallet-parachain-system = { path = "../../../../../pallets/parachain-system" }
 
 # Local
 xcm-emulator = { default-features = false, path = "../../../../../xcm/xcm-emulator" }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs
index e3d33dabaa2..b7f064e7d6e 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs
@@ -16,31 +16,86 @@
 
 pub use codec::Encode;
 pub use frame_support::{
-	assert_ok, instances::Instance1, pallet_prelude::Weight, traits::fungibles::Inspect,
+	assert_err, assert_ok,
+	instances::{Instance1, Instance2},
+	pallet_prelude::Weight,
+	sp_runtime::{AccountId32, DispatchError, DispatchResult, ModuleError, MultiAddress},
+	traits::{fungibles::Inspect, OriginTrait},
+	BoundedVec,
 };
 pub use integration_tests_common::{
 	constants::{
 		accounts::{ALICE, BOB},
-		polkadot::ED as POLKADOT_ED,
+		asset_hub_westend::ED as ASSET_HUB_WESTEND_ED,
+		westend::ED as WESTEND_ED,
 		PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
 	},
-	AccountId, AssetHubWestend, AssetHubWestendPallet, AssetHubWestendReceiver,
-	AssetHubWestendSender, Collectives, CollectivesPallet, CollectivesReceiver, CollectivesSender,
-	PenpalWestend, PenpalWestendPallet, PenpalWestendReceiver, PenpalWestendSender, Westend,
+	lazy_static::lazy_static,
+	xcm_transact_paid_execution, xcm_transact_unpaid_execution, AssetHubWestend,
+	AssetHubWestendPallet, AssetHubWestendReceiver, AssetHubWestendSender, Collectives,
+	CollectivesPallet, CollectivesReceiver, CollectivesSender, PenpalWestendA,
+	PenpalWestendAPallet, PenpalWestendAReceiver, PenpalWestendASender, Westend, WestendMockNet,
 	WestendPallet, WestendReceiver, WestendSender,
 };
+pub use parachains_common::{AccountId, Balance};
 pub use polkadot_core_primitives::InboundDownwardMessage;
+pub use polkadot_parachain::primitives::{HrmpChannelId, Id};
+pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
 pub use xcm::{
 	prelude::*,
-	v3::{
-		Error,
-		NetworkId::{Kusama as KusamaId, Polkadot as PolkadotId},
-	},
+	v3::{Error, NetworkId::Westend as WestendId},
+	DoubleEncoded,
 };
 pub use xcm_emulator::{
 	assert_expected_events, bx, cumulus_pallet_dmp_queue, helpers::weight_within_threshold,
-	Parachain as Para, RelayChain as Relay, TestExt,
+	AccountId32Junction, Chain, ParaId, Parachain as Para, RelayChain as Relay, Test, TestArgs,
+	TestContext, TestExt, TestExternalities,
 };
 
+pub const ASSET_ID: u32 = 1;
+pub const ASSET_MIN_BALANCE: u128 = 1000;
+// `Assets` pallet index
+pub const ASSETS_PALLET_ID: u8 = 50;
+
+pub type RelayToSystemParaTest = Test<Westend, AssetHubWestend>;
+pub type SystemParaToRelayTest = Test<AssetHubWestend, Westend>;
+pub type SystemParaToParaTest = Test<AssetHubWestend, PenpalWestendA>;
+
+/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
+pub fn relay_test_args(amount: Balance) -> TestArgs {
+	TestArgs {
+		dest: Westend::child_location_of(AssetHubWestend::para_id()),
+		beneficiary: AccountId32Junction {
+			network: None,
+			id: AssetHubWestendReceiver::get().into(),
+		}
+		.into(),
+		amount,
+		assets: (Here, amount).into(),
+		asset_id: None,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
+/// Returns a `TestArgs` instance to de used for the System Parachain accross integraton tests
+pub fn system_para_test_args(
+	dest: MultiLocation,
+	beneficiary_id: AccountId32,
+	amount: Balance,
+	assets: MultiAssets,
+	asset_id: Option<u32>,
+) -> TestArgs {
+	TestArgs {
+		dest,
+		beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
+		amount,
+		assets,
+		asset_id,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/mod.rs
index d4e304eb0ff..e45b78da1c2 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/mod.rs
@@ -15,6 +15,7 @@
 // along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
 
 mod reserve_transfer;
+mod send;
+mod set_xcm_versions;
 mod swap;
 mod teleport;
-mod transact;
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs
index 7ef76efdfed..430c203edd8 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs
@@ -16,66 +16,395 @@
 
 use crate::*;
 
+fn relay_origin_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
+
+	Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(629_384_000, 6_196)));
+
+	assert_expected_events!(
+		Westend,
+		vec![
+			// Amount to reserve transfer is transferred to System Parachain's Sovereign account
+			RuntimeEvent::Balances(pallet_balances::Event::Transfer { from, to, amount }) => {
+				from: *from == t.sender.account_id,
+				to: *to == Westend::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount:  *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn system_para_dest_assertions_incomplete(_t: RelayToSystemParaTest) {
+	AssetHubWestend::assert_dmp_queue_incomplete(
+		Some(Weight::from_parts(1_000_000_000, 0)),
+		Some(Error::UntrustedReserveLocation),
+	);
+}
+
+fn system_para_to_relay_assertions(_t: SystemParaToRelayTest) {
+	AssetHubWestend::assert_xcm_pallet_attempted_error(Some(XcmError::Barrier))
+}
+
+fn system_para_to_para_assertions(t: SystemParaToParaTest) {
+	type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+	AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		676_119_000,
+		6196,
+	)));
+
+	assert_expected_events!(
+		AssetHubWestend,
+		vec![
+			// Amount to reserve transfer is transferred to Parachain's Sovereing account
+			RuntimeEvent::Balances(
+				pallet_balances::Event::Transfer { from, to, amount }
+			) => {
+				from: *from == t.sender.account_id,
+				to: *to == AssetHubWestend::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
+	type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+	AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		676_119_000,
+		6196,
+	)));
+
+	assert_expected_events!(
+		AssetHubWestend,
+		vec![
+			// Amount to reserve transfer is transferred to Parachain's Sovereing account
+			RuntimeEvent::Assets(
+				pallet_assets::Event::Transferred { asset_id, from, to, amount }
+			) => {
+				asset_id: *asset_id == ASSET_ID,
+				from: *from == t.sender.account_id,
+				to: *to == AssetHubWestend::sovereign_account_id_of(
+					t.args.dest
+				),
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn relay_limited_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Westend as WestendPallet>::XcmPallet::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn relay_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Westend as WestendPallet>::XcmPallet::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_limited_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn system_para_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
+	<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::limited_reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
+	<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::reserve_transfer_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+/// Limited Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
 #[test]
-fn reserve_transfer_native_asset_from_relay_to_assets() {
-	// Init tests variables
-	let amount = POLKADOT_ED * 1000;
-	let relay_sender_balance_before = Westend::account_data_of(WestendSender::get()).free;
-	let para_receiver_balance_before =
-		AssetHubWestend::account_data_of(AssetHubWestendReceiver::get()).free;
-
-	let origin = <Westend as Relay>::RuntimeOrigin::signed(WestendSender::get());
-	let assets_para_destination: VersionedMultiLocation =
-		Westend::child_location_of(AssetHubWestend::para_id()).into();
-	let beneficiary: VersionedMultiLocation =
-		AccountId32 { network: None, id: AssetHubWestendReceiver::get().into() }.into();
-	let native_assets: VersionedMultiAssets = (Here, amount).into();
-	let fee_asset_item = 0;
-	let weight_limit = WeightLimit::Unlimited;
-
-	// Send XCM message from Relay Chain
-	Westend::execute_with(|| {
-		assert_ok!(<Westend as WestendPallet>::XcmPallet::limited_reserve_transfer_assets(
-			origin,
-			bx!(assets_para_destination),
-			bx!(beneficiary),
-			bx!(native_assets),
-			fee_asset_item,
-			weight_limit,
-		));
-
-		type RuntimeEvent = <Westend as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Westend,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => {
-					weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(629_384_000, 6196), *weight),
-				},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubWestend::execute_with(|| {
-		type RuntimeEvent = <AssetHubWestend as Para>::RuntimeEvent;
-
-		assert_expected_events!(
-			AssetHubWestend,
-			vec![
-				RuntimeEvent::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward {
-					outcome: Outcome::Incomplete(_, Error::UntrustedReserveLocation),
-					..
-				}) => {},
-			]
-		);
-	});
-
-	// Check if balances are updated accordingly in Relay Chain and Assets Parachain
-	let relay_sender_balance_after = Westend::account_data_of(WestendSender::get()).free;
-	let para_sender_balance_after =
-		AssetHubWestend::account_data_of(AssetHubWestendReceiver::get()).free;
-
-	assert_eq!(relay_sender_balance_before - amount, relay_sender_balance_after);
-	assert_eq!(para_sender_balance_after, para_receiver_balance_before);
+fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = WESTEND_ED * 1000;
+	let test_args = TestContext {
+		sender: WestendSender::get(),
+		receiver: AssetHubWestendReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Westend>(relay_origin_assertions);
+	test.set_assertion::<AssetHubWestend>(system_para_dest_assertions_incomplete);
+	test.set_dispatchable::<Westend>(relay_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Limited Reserve Transfers of native asset from System Parachain to Relay Chain shoudln't work
+#[test]
+fn limited_reserve_transfer_native_asset_from_system_para_to_relay_fails() {
+	// Init values for System Parachain
+	let destination = AssetHubWestend::parent_location();
+	let beneficiary_id = WestendReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: WestendReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubWestend>(system_para_to_relay_assertions);
+	test.set_dispatchable::<AssetHubWestend>(system_para_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
+#[test]
+fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = WESTEND_ED * 1000;
+	let test_args = TestContext {
+		sender: WestendSender::get(),
+		receiver: AssetHubWestendReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Westend>(relay_origin_assertions);
+	test.set_assertion::<AssetHubWestend>(system_para_dest_assertions_incomplete);
+	test.set_dispatchable::<Westend>(relay_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work
+#[test]
+fn reserve_transfer_native_asset_from_system_para_to_relay_fails() {
+	// Init values for System Parachain
+	let destination = AssetHubWestend::parent_location();
+	let beneficiary_id = WestendReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: WestendReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubWestend>(system_para_to_relay_assertions);
+	test.set_dispatchable::<AssetHubWestend>(system_para_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	assert_eq!(sender_balance_before, sender_balance_after);
+	assert_eq!(receiver_balance_before, receiver_balance_after);
+}
+
+/// Limited Reserve Transfers of native asset from System Parachain to Parachain should work
+#[test]
+fn limited_reserve_transfer_native_asset_from_system_para_to_para() {
+	// Init values for System Parachain
+	let destination = AssetHubWestend::sibling_location_of(PenpalWestendA::para_id());
+	let beneficiary_id = PenpalWestendAReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: PenpalWestendAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+
+	test.set_assertion::<AssetHubWestend>(system_para_to_para_assertions);
+	// TODO: Add assertion for Penpal runtime. Right now message is failing with `UntrustedReserveLocation`
+	test.set_dispatchable::<AssetHubWestend>(system_para_to_para_limited_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve transfers
+}
+
+/// Reserve Transfers of native asset from System Parachain to Parachain should work
+#[test]
+fn reserve_transfer_native_asset_from_system_para_to_para() {
+	// Init values for System Parachain
+	let destination = AssetHubWestend::sibling_location_of(PenpalWestendA::para_id());
+	let beneficiary_id = PenpalWestendAReceiver::get();
+	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: PenpalWestendAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+
+	test.set_assertion::<AssetHubWestend>(system_para_to_para_assertions);
+	// TODO: Add assertion for Penpal runtime. Right now message is failing with `UntrustedReserveLocation`
+	test.set_dispatchable::<AssetHubWestend>(system_para_to_para_reserve_transfer_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve transfers
+}
+
+/// Limited Reserve Transfers of a local asset from System Parachain to Parachain should work
+#[test]
+fn limited_reserve_transfer_asset_from_system_para_to_para() {
+	// Force create asset from Relay Chain and mint assets for System Parachain's sender account
+	AssetHubWestend::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		AssetHubWestendSender::get(),
+		ASSET_MIN_BALANCE * 1000000,
+	);
+
+	// Init values for System Parachain
+	let destination = AssetHubWestend::sibling_location_of(PenpalWestendA::para_id());
+	let beneficiary_id = PenpalWestendAReceiver::get();
+	let amount_to_send = ASSET_MIN_BALANCE * 1000;
+	let assets =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
+			.into();
+
+	let system_para_test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: PenpalWestendAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
+
+	system_para_test.set_assertion::<AssetHubWestend>(system_para_to_para_assets_assertions);
+	// TODO: Add assertions when Penpal is able to manage assets
+	system_para_test
+		.set_dispatchable::<AssetHubWestend>(system_para_to_para_limited_reserve_transfer_assets);
+	system_para_test.assert();
+}
+
+/// Reserve Transfers of a local asset from System Parachain to Parachain should work
+#[test]
+fn reserve_transfer_asset_from_system_para_to_para() {
+	// Force create asset from Relay Chain and mint assets for System Parachain's sender account
+	AssetHubWestend::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		AssetHubWestendSender::get(),
+		ASSET_MIN_BALANCE * 1000000,
+	);
+
+	// Init values for System Parachain
+	let destination = AssetHubWestend::sibling_location_of(PenpalWestendA::para_id());
+	let beneficiary_id = PenpalWestendAReceiver::get();
+	let amount_to_send = ASSET_MIN_BALANCE * 1000;
+	let assets =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
+			.into();
+
+	let system_para_test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: PenpalWestendAReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
+
+	system_para_test.set_assertion::<AssetHubWestend>(system_para_to_para_assets_assertions);
+	// TODO: Add assertions when Penpal is able to manage assets
+	system_para_test
+		.set_dispatchable::<AssetHubWestend>(system_para_to_para_reserve_transfer_assets);
+	system_para_test.assert();
 }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs
new file mode 100644
index 00000000000..24301fe1c7d
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs
@@ -0,0 +1,141 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+/// Relay Chain should be able to execute `Transact` instructions in System Parachain
+/// when `OriginKind::Superuser` and signer is `sudo`
+#[test]
+fn send_transact_sudo_from_relay_to_system_para_works() {
+	// Init tests variables
+	let root_origin = <Westend as Chain>::RuntimeOrigin::root();
+	let system_para_destination = Westend::child_location_of(AssetHubWestend::para_id()).into();
+	let asset_owner: AccountId = AssetHubWestendSender::get().into();
+	let xcm = AssetHubWestend::force_create_asset_xcm(
+		OriginKind::Superuser,
+		ASSET_ID,
+		asset_owner.clone(),
+		true,
+		1000,
+	);
+	// Send XCM message from Relay Chain
+	Westend::execute_with(|| {
+		assert_ok!(<Westend as WestendPallet>::XcmPallet::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		Westend::assert_xcm_pallet_sent();
+	});
+
+	// Receive XCM message in Assets Parachain
+	AssetHubWestend::execute_with(|| {
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+		AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(
+			1_019_445_000,
+			200_000,
+		)));
+
+		assert_expected_events!(
+			AssetHubWestend,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::ForceCreated { asset_id, owner }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == asset_owner,
+				},
+			]
+		);
+
+		assert!(<AssetHubWestend as AssetHubWestendPallet>::Assets::asset_exists(ASSET_ID));
+	});
+}
+
+/// Parachain should be able to send XCM paying its fee with sufficient asset
+/// in the System Parachain
+#[test]
+fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
+	let para_sovereign_account = AssetHubWestend::sovereign_account_id_of(
+		AssetHubWestend::sibling_location_of(PenpalWestendA::para_id()),
+	);
+
+	// Force create and mint assets for Parachain's sovereign account
+	AssetHubWestend::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		para_sovereign_account.clone(),
+		ASSET_MIN_BALANCE * 1000000000,
+	);
+
+	// We just need a call that can pass the `SafeCallFilter`
+	// Call values are not relevant
+	let call = AssetHubWestend::force_create_asset_call(
+		ASSET_ID,
+		para_sovereign_account.clone(),
+		true,
+		ASSET_MIN_BALANCE,
+	);
+
+	let origin_kind = OriginKind::SovereignAccount;
+	let fee_amount = ASSET_MIN_BALANCE * 1000000;
+	let native_asset =
+		(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into();
+
+	let root_origin = <PenpalWestendA as Chain>::RuntimeOrigin::root();
+	let system_para_destination =
+		PenpalWestendA::sibling_location_of(AssetHubWestend::para_id()).into();
+	let xcm = xcm_transact_paid_execution(
+		call,
+		origin_kind,
+		native_asset,
+		para_sovereign_account.clone(),
+	);
+
+	PenpalWestendA::execute_with(|| {
+		assert_ok!(<PenpalWestendA as PenpalWestendAPallet>::PolkadotXcm::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		AssetHubWestend::assert_xcm_pallet_sent();
+	});
+
+	AssetHubWestend::execute_with(|| {
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+		AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts(
+			2_176_414_000,
+			203_593,
+		)));
+
+		assert_expected_events!(
+			AssetHubWestend,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == para_sovereign_account,
+					balance: *balance == fee_amount,
+				},
+				RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
+					asset_id: *asset_id == ASSET_ID,
+				},
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/set_xcm_versions.rs
new file mode 100644
index 00000000000..76e38083659
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/set_xcm_versions.rs
@@ -0,0 +1,97 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::*;
+
+#[test]
+fn relay_sets_system_para_xcm_supported_version() {
+	// Init tests variables
+	let sudo_origin = <Westend as Chain>::RuntimeOrigin::root();
+	let system_para_destination: MultiLocation =
+		Westend::child_location_of(AssetHubWestend::para_id());
+
+	// Relay Chain sets supported version for Asset Parachain
+	Westend::execute_with(|| {
+		assert_ok!(<Westend as WestendPallet>::XcmPallet::force_xcm_version(
+			sudo_origin,
+			bx!(system_para_destination),
+			XCM_V3
+		));
+
+		type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
+
+		assert_expected_events!(
+			Westend,
+			vec![
+				RuntimeEvent::XcmPallet(pallet_xcm::Event::SupportedVersionChanged {
+					location,
+					version: XCM_V3
+				}) => { location: *location == system_para_destination, },
+			]
+		);
+	});
+}
+
+#[test]
+fn system_para_sets_relay_xcm_supported_version() {
+	// Init test variables
+	let sudo_origin = <Westend as Chain>::RuntimeOrigin::root();
+	let parent_location = AssetHubWestend::parent_location();
+	let system_para_destination: VersionedMultiLocation =
+		Westend::child_location_of(AssetHubWestend::para_id()).into();
+	let call = <AssetHubWestend as Chain>::RuntimeCall::PolkadotXcm(pallet_xcm::Call::<
+		<AssetHubWestend as Chain>::Runtime,
+	>::force_xcm_version {
+		location: bx!(parent_location),
+		version: XCM_V3,
+	})
+	.encode()
+	.into();
+	let origin_kind = OriginKind::Superuser;
+
+	let xcm = xcm_transact_unpaid_execution(call, origin_kind);
+
+	// System Parachain sets supported version for Relay Chain throught it
+	Westend::execute_with(|| {
+		assert_ok!(<Westend as WestendPallet>::XcmPallet::send(
+			sudo_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		Westend::assert_xcm_pallet_sent();
+	});
+
+	// System Parachain receive the XCM message
+	AssetHubWestend::execute_with(|| {
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+		AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(
+			1_019_210_000,
+			200_000,
+		)));
+
+		assert_expected_events!(
+			AssetHubWestend,
+			vec![
+				RuntimeEvent::PolkadotXcm(pallet_xcm::Event::SupportedVersionChanged {
+					location,
+					version: XCM_V3
+				}) => { location: *location == parent_location, },
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs
index fabdb9349ef..03ac7514608 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs
@@ -1,23 +1,18 @@
 use crate::*;
-use frame_support::{instances::Instance2, BoundedVec};
-use sp_runtime::{DispatchError, ModuleError};
-use xcm_emulator::Parachain;
 
 #[test]
 fn swap_locally_on_chain_using_local_assets() {
-	const ASSET_ID: u32 = 1;
-
 	let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get());
 	let asset_one = Box::new(MultiLocation {
 		parents: 0,
-		interior: X2(PalletInstance(50), GeneralIndex(ASSET_ID.into())),
+		interior: X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())),
 	});
 
 	AssetHubWestend::execute_with(|| {
-		type RuntimeEvent = <AssetHubWestend as Parachain>::RuntimeEvent;
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::Assets::create(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			ASSET_ID.into(),
 			AssetHubWestendSender::get().into(),
 			1000,
@@ -25,14 +20,14 @@ fn swap_locally_on_chain_using_local_assets() {
 		assert!(<AssetHubWestend as AssetHubWestendPallet>::Assets::asset_exists(ASSET_ID));
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::Assets::mint(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			ASSET_ID.into(),
 			AssetHubWestendSender::get().into(),
 			3_000_000_000_000,
 		));
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::create_pool(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			asset_native.clone(),
 			asset_one.clone(),
 		));
@@ -45,7 +40,7 @@ fn swap_locally_on_chain_using_local_assets() {
 		);
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::add_liquidity(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			asset_native.clone(),
 			asset_one.clone(),
 			1_000_000_000_000,
@@ -65,7 +60,7 @@ fn swap_locally_on_chain_using_local_assets() {
 		let path = BoundedVec::<_, _>::truncate_from(vec![asset_native.clone(), asset_one.clone()]);
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::swap_exact_tokens_for_tokens(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			path,
 			100,
 			1,
@@ -84,7 +79,7 @@ fn swap_locally_on_chain_using_local_assets() {
 		);
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::remove_liquidity(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			asset_native,
 			asset_one,
 			1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved.
@@ -99,14 +94,13 @@ fn swap_locally_on_chain_using_local_assets() {
 fn swap_locally_on_chain_using_foreign_assets() {
 	use frame_support::weights::WeightToFee;
 
-	const ASSET_ID: u32 = 1;
 	let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get());
 
 	let foreign_asset1_at_asset_hub_westend = Box::new(MultiLocation {
 		parents: 1,
 		interior: X3(
-			Parachain(PenpalWestend::para_id().into()),
-			PalletInstance(50),
+			Parachain(PenpalWestendA::para_id().into()),
+			PalletInstance(ASSETS_PALLET_ID),
 			GeneralIndex(ASSET_ID.into()),
 		),
 	});
@@ -116,18 +110,18 @@ fn swap_locally_on_chain_using_foreign_assets() {
 			.into();
 
 	let penpal_location =
-		MultiLocation { parents: 1, interior: X1(Parachain(PenpalWestend::para_id().into())) };
+		MultiLocation { parents: 1, interior: X1(Parachain(PenpalWestendA::para_id().into())) };
 
 	// 1. Create asset on penpal:
-	PenpalWestend::execute_with(|| {
-		assert_ok!(<PenpalWestend as PenpalWestendPallet>::Assets::create(
-			<PenpalWestend as Parachain>::RuntimeOrigin::signed(PenpalWestendSender::get()),
+	PenpalWestendA::execute_with(|| {
+		assert_ok!(<PenpalWestendA as PenpalWestendAPallet>::Assets::create(
+			<PenpalWestendA as Chain>::RuntimeOrigin::signed(PenpalWestendASender::get()),
 			ASSET_ID.into(),
-			PenpalWestendSender::get().into(),
+			PenpalWestendASender::get().into(),
 			1000,
 		));
 
-		assert!(<PenpalWestend as PenpalWestendPallet>::Assets::asset_exists(ASSET_ID));
+		assert!(<PenpalWestendA as PenpalWestendAPallet>::Assets::asset_exists(ASSET_ID));
 	});
 
 	// 2. Create foreign asset on asset_hub_westend:
@@ -137,21 +131,21 @@ fn swap_locally_on_chain_using_foreign_assets() {
 	let sov_penpal_on_asset_hub_westend = AssetHubWestend::sovereign_account_id_of(penpal_location);
 
 	AssetHubWestend::fund_accounts(vec![
-		(AssetHubWestendSender::get(), 5_000_000), // An account to swap dot for something else.
-		(sov_penpal_on_asset_hub_westend.clone(), 1000_000_000_000_000_000),
+		(AssetHubWestendSender::get().into(), 5_000_000 * WESTEND_ED),
+		(sov_penpal_on_asset_hub_westend.clone().into(), 1000_000_000_000_000_000 * WESTEND_ED),
 	]);
 
 	let sov_penpal_on_asset_hub_westend_as_location: MultiLocation = MultiLocation {
 		parents: 0,
-		interior: X1(AccountId32 {
+		interior: X1(AccountId32Junction {
 			network: None,
 			id: sov_penpal_on_asset_hub_westend.clone().into(),
 		}),
 	};
 
 	let call_foreign_assets_create =
-		<AssetHubWestend as Para>::RuntimeCall::ForeignAssets(pallet_assets::Call::<
-			<AssetHubWestend as Para>::Runtime,
+		<AssetHubWestend as Chain>::RuntimeCall::ForeignAssets(pallet_assets::Call::<
+			<AssetHubWestend as Chain>::Runtime,
 			Instance2,
 		>::create {
 			id: *foreign_asset1_at_asset_hub_westend,
@@ -183,18 +177,18 @@ fn swap_locally_on_chain_using_foreign_assets() {
 	]));
 
 	// Send XCM message from penpal => asset_hub_westend
-	let sudo_penpal_origin = <PenpalWestend as Parachain>::RuntimeOrigin::root();
-	PenpalWestend::execute_with(|| {
-		assert_ok!(<PenpalWestend as PenpalWestendPallet>::PolkadotXcm::send(
+	let sudo_penpal_origin = <PenpalWestendA as Chain>::RuntimeOrigin::root();
+	PenpalWestendA::execute_with(|| {
+		assert_ok!(<PenpalWestendA as PenpalWestendAPallet>::PolkadotXcm::send(
 			sudo_penpal_origin.clone(),
 			bx!(assets_para_destination.clone()),
 			bx!(xcm),
 		));
 
-		type RuntimeEvent = <PenpalWestend as Parachain>::RuntimeEvent;
+		type RuntimeEvent = <PenpalWestendA as Chain>::RuntimeEvent;
 
 		assert_expected_events!(
-			PenpalWestend,
+			PenpalWestendA,
 			vec![
 				RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent { .. }) => {},
 			]
@@ -212,10 +206,10 @@ fn swap_locally_on_chain_using_foreign_assets() {
 		// (While it might be nice to use batch,
 		// currently that's disabled due to safe call filters.)
 
-		type RuntimeEvent = <AssetHubWestend as Parachain>::RuntimeEvent;
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
 		// 3. Mint foreign asset (in reality this should be a teleport or some such)
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::ForeignAssets::mint(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(
 				sov_penpal_on_asset_hub_westend.clone().into()
 			),
 			*foreign_asset1_at_asset_hub_westend,
@@ -232,7 +226,7 @@ fn swap_locally_on_chain_using_foreign_assets() {
 
 		// 4. Create pool:
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::create_pool(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			asset_native.clone(),
 			foreign_asset1_at_asset_hub_westend.clone(),
 		));
@@ -246,7 +240,7 @@ fn swap_locally_on_chain_using_foreign_assets() {
 
 		// 5. Add liquidity:
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::add_liquidity(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(
 				sov_penpal_on_asset_hub_westend.clone()
 			),
 			asset_native.clone(),
@@ -274,7 +268,7 @@ fn swap_locally_on_chain_using_foreign_assets() {
 		]);
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::swap_exact_tokens_for_tokens(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 			path,
 			100000,
 			1000,
@@ -294,7 +288,7 @@ fn swap_locally_on_chain_using_foreign_assets() {
 
 		// 7. Remove liquidity
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::remove_liquidity(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(
 				sov_penpal_on_asset_hub_westend.clone()
 			),
 			asset_native,
@@ -309,8 +303,6 @@ fn swap_locally_on_chain_using_foreign_assets() {
 
 #[test]
 fn cannot_create_pool_from_pool_assets() {
-	const ASSET_ID: u32 = 1;
-
 	let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get());
 	let mut asset_one = asset_hub_westend_runtime::xcm_config::PoolAssetsPalletLocation::get();
 	asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets");
@@ -319,7 +311,7 @@ fn cannot_create_pool_from_pool_assets() {
 		let pool_owner_account_id = asset_hub_westend_runtime::AssetConversionOrigin::get();
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::PoolAssets::create(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(pool_owner_account_id.clone()),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(pool_owner_account_id.clone()),
 			ASSET_ID.into(),
 			pool_owner_account_id.clone().into(),
 			1000,
@@ -327,7 +319,7 @@ fn cannot_create_pool_from_pool_assets() {
 		assert!(<AssetHubWestend as AssetHubWestendPallet>::PoolAssets::asset_exists(ASSET_ID));
 
 		assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::PoolAssets::mint(
-			<AssetHubWestend as Parachain>::RuntimeOrigin::signed(pool_owner_account_id),
+			<AssetHubWestend as Chain>::RuntimeOrigin::signed(pool_owner_account_id),
 			ASSET_ID.into(),
 			AssetHubWestendSender::get().into(),
 			3_000_000_000_000,
@@ -335,7 +327,7 @@ fn cannot_create_pool_from_pool_assets() {
 
 		assert_matches::assert_matches!(
 			<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::create_pool(
-				<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
+				<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
 				asset_native.clone(),
 				Box::new(asset_one),
 			),
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs
index c8a57933b05..577f9ee85bd 100644
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs
+++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs
@@ -16,63 +16,347 @@
 
 use crate::*;
 
+fn relay_origin_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
+
+	Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(632_207_000, 7_186)));
+
+	assert_expected_events!(
+		Westend,
+		vec![
+			// Amount to teleport is withdrawn from Sender
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == t.sender.account_id,
+				amount: *amount == t.args.amount,
+			},
+			// Amount to teleport is deposited in Relay's `CheckAccount`
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, amount }) => {
+				who: *who == <Westend as WestendPallet>::XcmPallet::check_account(),
+				amount:  *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn relay_dest_assertions(t: SystemParaToRelayTest) {
+	type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
+
+	Westend::assert_ump_queue_processed(
+		true,
+		Some(AssetHubWestend::para_id()),
+		Some(Weight::from_parts(308_222_000, 7_186)),
+	);
+
+	assert_expected_events!(
+		Westend,
+		vec![
+			// Amount is witdrawn from Relay Chain's `CheckAccount`
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == <Westend as WestendPallet>::XcmPallet::check_account(),
+				amount: *amount == t.args.amount,
+			},
+			// Amount minus fees are deposited in Receiver's account
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
+				who: *who == t.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) {
+	Westend::assert_ump_queue_processed(
+		false,
+		Some(AssetHubWestend::para_id()),
+		Some(Weight::from_parts(148_705_000, 3_593)),
+	);
+}
+
+fn para_origin_assertions(t: SystemParaToRelayTest) {
+	type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+	AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
+		533_910_000,
+		7167,
+	)));
+
+	AssetHubWestend::assert_parachain_system_ump_sent();
+
+	assert_expected_events!(
+		AssetHubWestend,
+		vec![
+			// Amount is withdrawn from Sender's account
+			RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
+				who: *who == t.sender.account_id,
+				amount: *amount == t.args.amount,
+			},
+		]
+	);
+}
+
+fn para_dest_assertions(t: RelayToSystemParaTest) {
+	type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+	AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(164_733_000, 0)));
+
+	assert_expected_events!(
+		AssetHubWestend,
+		vec![
+			// Amount minus fees are deposited in Receiver's account
+			RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
+				who: *who == t.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Westend as WestendPallet>::XcmPallet::limited_teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+fn relay_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
+	<Westend as WestendPallet>::XcmPallet::teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+	)
+}
+
+fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
+	<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::limited_teleport_assets(
+		t.signed_origin,
+		bx!(t.args.dest.into()),
+		bx!(t.args.beneficiary.into()),
+		bx!(t.args.assets.into()),
+		t.args.fee_asset_item,
+		t.args.weight_limit,
+	)
+}
+
+// TODO: Uncomment when https://github.com/paritytech/polkadot/pull/7424 is merged
+// fn system_para_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
+// 	<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::teleport_assets(
+// 		t.signed_origin,
+// 		bx!(t.args.dest),
+// 		bx!(t.args.beneficiary),
+// 		bx!(t.args.assets),
+// 		t.args.fee_asset_item,
+// 	)
+// }
+
+/// Limited Teleport of native asset from Relay Chain to the System Parachain should work
 #[test]
-fn teleport_native_assets_from_relay_to_assets_para() {
-	// Init tests variables
-	let amount = POLKADOT_ED * 1000;
-	let relay_sender_balance_before = Westend::account_data_of(WestendSender::get()).free;
-	let para_receiver_balance_before =
-		AssetHubWestend::account_data_of(AssetHubWestendReceiver::get()).free;
-
-	let origin = <Westend as Relay>::RuntimeOrigin::signed(WestendSender::get());
-	let assets_para_destination: VersionedMultiLocation =
-		Westend::child_location_of(AssetHubWestend::para_id()).into();
-	let beneficiary: VersionedMultiLocation =
-		AccountId32 { network: None, id: AssetHubWestendReceiver::get().into() }.into();
-	let native_assets: VersionedMultiAssets = (Here, amount).into();
-	let fee_asset_item = 0;
-	let weight_limit = WeightLimit::Unlimited;
-
-	// Send XCM message from Relay Chain
-	Westend::execute_with(|| {
-		assert_ok!(<Westend as WestendPallet>::XcmPallet::limited_teleport_assets(
-			origin,
-			bx!(assets_para_destination),
-			bx!(beneficiary),
-			bx!(native_assets),
-			fee_asset_item,
-			weight_limit,
-		));
-
-		type RuntimeEvent = <Westend as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Westend,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }) => {},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubWestend::execute_with(|| {
-		type RuntimeEvent = <AssetHubWestend as Para>::RuntimeEvent;
-
-		assert_expected_events!(
-			AssetHubWestend,
-			vec![
-				RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => {
-					who: *who == AssetHubWestendReceiver::get().into(),
-				},
-			]
-		);
-	});
-
-	// Check if balances are updated accordingly in Relay Chain and Assets Parachain
-	let relay_sender_balance_after = Westend::account_data_of(WestendSender::get()).free;
-	let para_sender_balance_after =
-		AssetHubWestend::account_data_of(AssetHubWestendReceiver::get()).free;
-
-	assert_eq!(relay_sender_balance_before - amount, relay_sender_balance_after);
-	assert!(para_sender_balance_after > para_receiver_balance_before);
+fn limited_teleport_native_assets_from_relay_to_system_para_works() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = WESTEND_ED * 1000;
+	let test_args = TestContext {
+		sender: WestendSender::get(),
+		receiver: AssetHubWestendReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Westend>(relay_origin_assertions);
+	test.set_assertion::<AssetHubWestend>(para_dest_assertions);
+	test.set_dispatchable::<Westend>(relay_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
 }
+
+/// Limited Teleport of native asset from System Parachain to Relay Chain
+/// should work when there is enough balance in Relay Chain's `CheckAccount`
+#[test]
+fn limited_teleport_native_assets_back_from_system_para_to_relay_works() {
+	// Dependency - Relay Chain's `CheckAccount` should have enough balance
+	limited_teleport_native_assets_from_relay_to_system_para_works();
+
+	// Init values for Relay Chain
+	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+	let destination = AssetHubWestend::parent_location();
+	let beneficiary_id = WestendReceiver::get();
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: WestendReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubWestend>(para_origin_assertions);
+	test.set_assertion::<Westend>(relay_dest_assertions);
+	test.set_dispatchable::<AssetHubWestend>(system_para_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
+}
+
+/// Limited Teleport of native asset from System Parachain to Relay Chain
+/// should't work when there is not enough balance in Relay Chain's `CheckAccount`
+#[test]
+fn limited_teleport_native_assets_from_system_para_to_relay_fails() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+	let destination = AssetHubWestend::parent_location().into();
+	let beneficiary_id = WestendReceiver::get().into();
+	let assets = (Parent, amount_to_send).into();
+
+	let test_args = TestContext {
+		sender: AssetHubWestendSender::get(),
+		receiver: WestendReceiver::get(),
+		args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
+	};
+
+	let mut test = SystemParaToRelayTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<AssetHubWestend>(para_origin_assertions);
+	test.set_assertion::<Westend>(relay_dest_assertions_fail);
+	test.set_dispatchable::<AssetHubWestend>(system_para_limited_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance does not change
+	assert_eq!(receiver_balance_after, receiver_balance_before);
+}
+
+/// Teleport of native asset from Relay Chain to the System Parachain should work
+#[test]
+fn teleport_native_assets_from_relay_to_system_para_works() {
+	// Init values for Relay Chain
+	let amount_to_send: Balance = WESTEND_ED * 1000;
+	let test_args = TestContext {
+		sender: WestendSender::get(),
+		receiver: AssetHubWestendReceiver::get(),
+		args: relay_test_args(amount_to_send),
+	};
+
+	let mut test = RelayToSystemParaTest::new(test_args);
+
+	let sender_balance_before = test.sender.balance;
+	let receiver_balance_before = test.receiver.balance;
+
+	test.set_assertion::<Westend>(relay_origin_assertions);
+	test.set_assertion::<AssetHubWestend>(para_dest_assertions);
+	test.set_dispatchable::<Westend>(relay_teleport_assets);
+	test.assert();
+
+	let sender_balance_after = test.sender.balance;
+	let receiver_balance_after = test.receiver.balance;
+
+	// Sender's balance is reduced
+	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+	// Receiver's balance is increased
+	assert!(receiver_balance_after > receiver_balance_before);
+}
+
+// TODO: Uncomment when https://github.com/paritytech/polkadot/pull/7424 is merged
+
+// Right now it is failing in the Relay Chain with a
+// `messageQueue.ProcessingFailed` event `error: Unsupported`.
+// The reason is the `Weigher` in `pallet_xcm` is not properly calculating the `remote_weight`
+// and it cause an `Overweight` error in `AllowTopLevelPaidExecutionFrom` barrier
+
+// /// Teleport of native asset from System Parachains to the Relay Chain
+// /// should work when there is enough balance in Relay Chain's `CheckAccount`
+// #[test]
+// fn teleport_native_assets_back_from_system_para_to_relay_works() {
+// 	// Dependency - Relay Chain's `CheckAccount` should have enough balance
+// 	teleport_native_assets_from_relay_to_system_para_works();
+
+// 	// Init values for Relay Chain
+// 	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+// 	let test_args = TestContext {
+// 		sender: AssetHubWestendSender::get(),
+// 		receiver: WestendReceiver::get(),
+// 		args: get_para_dispatch_args(amount_to_send),
+// 	};
+
+// 	let mut test = SystemParaToRelayTest::new(test_args);
+
+// 	let sender_balance_before = test.sender.balance;
+// 	let receiver_balance_before = test.receiver.balance;
+
+// 	test.set_assertion::<AssetHubWestend>(para_origin_assertions);
+// 	test.set_assertion::<Westend>(relay_dest_assertions);
+// 	test.set_dispatchable::<AssetHubWestend>(system_para_teleport_assets);
+// 	test.assert();
+
+// 	let sender_balance_after = test.sender.balance;
+// 	let receiver_balance_after = test.receiver.balance;
+
+// 	// Sender's balance is reduced
+// 	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+// 	// Receiver's balance is increased
+// 	assert!(receiver_balance_after > receiver_balance_before);
+// }
+
+// /// Teleport of native asset from System Parachain to Relay Chain
+// /// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount`
+// #[test]
+// fn teleport_native_assets_from_system_para_to_relay_fails() {
+// 	// Init values for Relay Chain
+// 	let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
+//  let assets = (Parent, amount_to_send).into();
+//
+// 	let test_args = TestContext {
+// 		sender: AssetHubWestendSender::get(),
+// 		receiver: WestendReceiver::get(),
+// 		args: system_para_test_args(amount_to_send),
+//      assets,
+//      None
+// 	};
+
+// 	let mut test = SystemParaToRelayTest::new(test_args);
+
+// 	let sender_balance_before = test.sender.balance;
+// 	let receiver_balance_before = test.receiver.balance;
+
+// 	test.set_assertion::<AssetHubWestend>(para_origin_assertions);
+// 	test.set_assertion::<Westend>(relay_dest_assertions);
+// 	test.set_dispatchable::<AssetHubWestend>(system_para_teleport_assets);
+// 	test.assert();
+
+// 	let sender_balance_after = test.sender.balance;
+// 	let receiver_balance_after = test.receiver.balance;
+
+// 	// Sender's balance is reduced
+// 	assert_eq!(sender_balance_before - amount_to_send, sender_balance_after);
+// 	// Receiver's balance does not change
+// 	assert_eq!(receiver_balance_after, receiver_balance_before);
+// }
diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/transact.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/transact.rs
deleted file mode 100644
index e71a9174848..00000000000
--- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/transact.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright Parity Technologies (UK) Ltd.
-// This file is part of Cumulus.
-
-// Cumulus is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Cumulus is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
-
-use crate::*;
-
-#[test]
-fn transact_sudo_from_relay_to_assets_para() {
-	// Init tests variables
-	// Call to be executed in Assets Parachain
-	const ASSET_ID: u32 = 1;
-
-	let call = <AssetHubWestend as Para>::RuntimeCall::Assets(pallet_assets::Call::<
-		<AssetHubWestend as Para>::Runtime,
-		Instance1,
-	>::force_create {
-		id: ASSET_ID.into(),
-		is_sufficient: true,
-		min_balance: 1000,
-		owner: AssetHubWestendSender::get().into(),
-	})
-	.encode()
-	.into();
-
-	// XcmPallet send arguments
-	let sudo_origin = <Westend as Relay>::RuntimeOrigin::root();
-	let assets_para_destination: VersionedMultiLocation =
-		Westend::child_location_of(AssetHubWestend::para_id()).into();
-
-	let weight_limit = WeightLimit::Unlimited;
-	let require_weight_at_most = Weight::from_parts(1000000000, 200000);
-	let origin_kind = OriginKind::Superuser;
-	let check_origin = None;
-
-	let xcm = VersionedXcm::from(Xcm(vec![
-		UnpaidExecution { weight_limit, check_origin },
-		Transact { require_weight_at_most, origin_kind, call },
-	]));
-
-	// Send XCM message from Relay Chain
-	Westend::execute_with(|| {
-		assert_ok!(<Westend as WestendPallet>::XcmPallet::send(
-			sudo_origin,
-			bx!(assets_para_destination),
-			bx!(xcm),
-		));
-
-		type RuntimeEvent = <Westend as Relay>::RuntimeEvent;
-
-		assert_expected_events!(
-			Westend,
-			vec![
-				RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
-			]
-		);
-	});
-
-	// Receive XCM message in Assets Parachain
-	AssetHubWestend::execute_with(|| {
-		assert!(<AssetHubWestend as AssetHubWestendPallet>::Assets::asset_exists(ASSET_ID));
-	});
-}
diff --git a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml
index d61de30915a..901b3a99512 100644
--- a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml
@@ -29,7 +29,7 @@ pallet-xcm = { default-features = false, git = "https://github.com/paritytech/po
 # Cumulus
 parachains-common = { path = "../../../../common" }
 cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
-bridge-hub-rococo-runtime = { path = "../../../../runtimes/bridge-hubs/bridge-hub-rococo" }
+cumulus-pallet-parachain-system = { path = "../../../../../pallets/parachain-system" }
 pallet-bridge-messages = { default-features = false, path = "../../../../../bridges/modules/messages" }
 bp-messages = { default-features = false, path = "../../../../../bridges/primitives/messages" }
 
diff --git a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/lib.rs
index 105212b1907..2a4927d857c 100644
--- a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/lib.rs
@@ -16,33 +16,73 @@
 
 pub use bp_messages::LaneId;
 pub use codec::Encode;
-pub use frame_support::{assert_ok, pallet_prelude::Weight};
+pub use frame_support::{
+	assert_err, assert_ok,
+	instances::Instance1,
+	pallet_prelude::Weight,
+	sp_runtime::{AccountId32, DispatchError, DispatchResult, MultiAddress},
+	traits::{fungibles::Inspect, OriginTrait},
+};
 pub use integration_tests_common::{
 	constants::{
 		accounts::{ALICE, BOB},
-		rococo::{ED as ROCOCO_ED, ED as WOCOCO_ED},
+		asset_hub_kusama::ED as ASSET_HUB_ROCOCO_ED,
+		kusama::ED as ROCOCO_ED,
 		PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
 	},
-	AccountId, AssetHubWococo, BridgeHubPolkadot, BridgeHubPolkadotPallet,
-	BridgeHubPolkadotReceiver, BridgeHubPolkadotSender, BridgeHubRococo, BridgeHubRococoPallet,
-	BridgeHubRococoReceiver, BridgeHubRococoSender, BridgeHubWococo, Collectives,
-	CollectivesPallet, CollectivesReceiver, CollectivesSender, Kusama, KusamaPallet,
-	PenpalPolkadot, PenpalPolkadotReceiver, PenpalPolkadotSender, Polkadot, PolkadotMockNet,
-	PolkadotPallet, PolkadotReceiver, PolkadotSender, Rococo, RococoMockNet, RococoPallet,
-	RococoReceiver, RococoSender,
+	lazy_static::lazy_static,
+	xcm_transact_paid_execution, xcm_transact_unpaid_execution, AssetHubRococo,
+	AssetHubRococoPallet, AssetHubRococoReceiver, AssetHubRococoSender, AssetHubWococo,
+	AssetHubWococoPallet, AssetHubWococoReceiver, AssetHubWococoSender, BridgeHubRococo,
+	BridgeHubRococoPallet, BridgeHubRococoReceiver, BridgeHubRococoSender, BridgeHubWococo,
+	BridgeHubWococoPallet, BridgeHubWococoReceiver, BridgeHubWococoSender, Collectives,
+	CollectivesPallet, CollectivesReceiver, CollectivesSender, PenpalRococoA, PenpalRococoAPallet,
+	PenpalRococoAReceiver, PenpalRococoASender, Rococo, RococoMockNet, RococoPallet,
+	RococoReceiver, RococoSender, Wococo, WococoMockNet, WococoPallet, WococoReceiver,
+	WococoSender,
 };
-// pub use polkadot_core_primitives::InboundDownwardMessage;
+pub use parachains_common::{AccountId, Balance};
+pub use polkadot_core_primitives::InboundDownwardMessage;
+pub use polkadot_parachain::primitives::{HrmpChannelId, Id};
+pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
 pub use xcm::{
 	prelude::*,
 	v3::{
 		Error,
 		NetworkId::{Rococo as RococoId, Wococo as WococoId},
 	},
+	DoubleEncoded,
 };
 pub use xcm_emulator::{
 	assert_expected_events, bx, cumulus_pallet_dmp_queue, helpers::weight_within_threshold,
-	Parachain as Para, RelayChain as Relay, TestExt,
+	AccountId32Junction, Chain, ParaId, Parachain as Para, RelayChain as Relay, Test, TestArgs,
+	TestContext, TestExt, TestExternalities,
 };
 
+pub const ASSET_ID: u32 = 1;
+pub const ASSET_MIN_BALANCE: u128 = 1000;
+pub const ASSETS_PALLET_ID: u8 = 50;
+
+pub type RelayToSystemParaTest = Test<Rococo, AssetHubRococo>;
+pub type SystemParaToRelayTest = Test<AssetHubRococo, Rococo>;
+pub type SystemParaToParaTest = Test<AssetHubRococo, PenpalRococoA>;
+
+/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
+pub fn relay_test_args(amount: Balance) -> TestArgs {
+	TestArgs {
+		dest: Rococo::child_location_of(AssetHubRococo::para_id()),
+		beneficiary: AccountId32Junction {
+			network: None,
+			id: AssetHubRococoReceiver::get().into(),
+		}
+		.into(),
+		amount,
+		assets: (Here, amount).into(),
+		asset_id: None,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/example.rs b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/example.rs
index 1d06d7f9fcd..5b11337a7e6 100644
--- a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/example.rs
+++ b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/example.rs
@@ -20,7 +20,7 @@ use crate::*;
 fn example() {
 	// Init tests variables
 	// XcmPallet send arguments
-	let sudo_origin = <Rococo as Relay>::RuntimeOrigin::root();
+	let sudo_origin = <Rococo as Chain>::RuntimeOrigin::root();
 	let destination = Rococo::child_location_of(BridgeHubRococo::para_id()).into();
 	let weight_limit = WeightLimit::Unlimited;
 	let check_origin = None;
@@ -45,7 +45,7 @@ fn example() {
 			bx!(xcm),
 		));
 
-		type RuntimeEvent = <Rococo as Relay>::RuntimeEvent;
+		type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
 
 		assert_expected_events!(
 			Rococo,
@@ -56,7 +56,7 @@ fn example() {
 	});
 	// Receive XCM message in Bridge Hub source Parachain
 	BridgeHubRococo::execute_with(|| {
-		type RuntimeEvent = <BridgeHubRococo as Para>::RuntimeEvent;
+		type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
 
 		assert_expected_events!(
 			BridgeHubRococo,
@@ -76,7 +76,7 @@ fn example() {
 	// Wococo GLobal Consensus
 	// Receive XCM message in Bridge Hub target Parachain
 	BridgeHubWococo::execute_with(|| {
-		type RuntimeEvent = <BridgeHubWococo as Para>::RuntimeEvent;
+		type RuntimeEvent = <BridgeHubWococo as Chain>::RuntimeEvent;
 
 		assert_expected_events!(
 			BridgeHubWococo,
@@ -87,7 +87,7 @@ fn example() {
 	});
 	// Receive embeded XCM message within `ExportMessage` in Parachain destination
 	AssetHubWococo::execute_with(|| {
-		type RuntimeEvent = <AssetHubWococo as Para>::RuntimeEvent;
+		type RuntimeEvent = <AssetHubWococo as Chain>::RuntimeEvent;
 
 		assert_expected_events!(
 			AssetHubWococo,
diff --git a/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/Cargo.toml b/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/Cargo.toml
index 2f81d34b51a..8663f4b0b4a 100644
--- a/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/Cargo.toml
@@ -5,7 +5,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2021"
 description = "Polkadot Collectives parachain runtime integration tests based on xcm-emulator"
 
-[dev-dependencies]
+[dependencies]
 codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false }
 
 # Substrate
@@ -31,6 +31,7 @@ pallet-xcm = { default-features = false, git = "https://github.com/paritytech/po
 # Cumulus
 parachains-common = { path = "../../../../common" }
 cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
+cumulus-pallet-parachain-system = { path = "../../../../../pallets/parachain-system" }
 collectives-polkadot-runtime = { path = "../../../../runtimes/collectives/collectives-polkadot" }
 asset-hub-polkadot-runtime = { path = "../../../../runtimes/assets/asset-hub-polkadot" }
 
diff --git a/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/lib.rs b/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/lib.rs
index d83ddfffd99..b71ee65a222 100644
--- a/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/lib.rs
@@ -14,17 +14,88 @@
 // You should have received a copy of the GNU General Public License
 // along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
 
-//! Collectives Parachain integration tests based on xcm-emulator.
-
-#![cfg(test)]
-
-pub use frame_support::assert_ok;
+pub use codec::Encode;
+pub use frame_support::{
+	assert_err, assert_ok,
+	instances::Instance1,
+	pallet_prelude::Weight,
+	sp_runtime::{AccountId32, DispatchError, DispatchResult, MultiAddress},
+	traits::{fungibles::Inspect, OriginTrait},
+};
 pub use integration_tests_common::{
-	constants::accounts::ALICE, AccountId, AssetHubPolkadot as AssetHub,
-	AssetHubPolkadotPallet as AssetHubPallet, Collectives, CollectivesPallet, Polkadot,
-	PolkadotMockNet,
+	constants::{
+		accounts::{ALICE, BOB},
+		asset_hub_polkadot::ED as ASSET_HUB_POLKADOT_ED,
+		polkadot::ED as POLKADOT_ED,
+		PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
+	},
+	lazy_static::lazy_static,
+	xcm_transact_paid_execution, xcm_transact_unpaid_execution, AssetHubPolkadot,
+	AssetHubPolkadotPallet, AssetHubPolkadotReceiver, AssetHubPolkadotSender, BridgeHubPolkadot,
+	BridgeHubPolkadotPallet, BridgeHubPolkadotReceiver, BridgeHubPolkadotSender, Collectives,
+	CollectivesPallet, CollectivesReceiver, CollectivesSender, PenpalPolkadotA,
+	PenpalPolkadotAPallet, PenpalPolkadotAReceiver, PenpalPolkadotASender, PenpalPolkadotB,
+	PenpalPolkadotBPallet, PenpalPolkadotBReceiver, PenpalPolkadotBSender, Polkadot,
+	PolkadotMockNet, PolkadotPallet, PolkadotReceiver, PolkadotSender,
+};
+pub use parachains_common::{AccountId, Balance};
+pub use polkadot_core_primitives::InboundDownwardMessage;
+pub use polkadot_parachain::primitives::{HrmpChannelId, Id};
+pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
+pub use xcm::{
+	prelude::*,
+	v3::{Error, NetworkId::Polkadot as PolkadotId},
+	DoubleEncoded,
+};
+pub use xcm_emulator::{
+	assert_expected_events, bx, cumulus_pallet_dmp_queue, helpers::weight_within_threshold,
+	AccountId32Junction, Chain, ParaId, Parachain as Para, RelayChain as Relay, Test, TestArgs,
+	TestContext, TestExt, TestExternalities,
 };
-pub use xcm::prelude::*;
-pub use xcm_emulator::{assert_expected_events, Parachain};
 
+pub const ASSET_ID: u32 = 1;
+pub const ASSET_MIN_BALANCE: u128 = 1000;
+pub const ASSETS_PALLET_ID: u8 = 50;
+
+pub type RelayToSystemParaTest = Test<Polkadot, AssetHubPolkadot>;
+pub type SystemParaToRelayTest = Test<AssetHubPolkadot, Polkadot>;
+pub type SystemParaToParaTest = Test<AssetHubPolkadot, PenpalPolkadotA>;
+
+/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
+pub fn relay_test_args(amount: Balance) -> TestArgs {
+	TestArgs {
+		dest: Polkadot::child_location_of(AssetHubPolkadot::para_id()),
+		beneficiary: AccountId32Junction {
+			network: None,
+			id: AssetHubPolkadotReceiver::get().into(),
+		}
+		.into(),
+		amount,
+		assets: (Here, amount).into(),
+		asset_id: None,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
+/// Returns a `TestArgs` instance to de used for the System Parachain accross integraton tests
+pub fn system_para_test_args(
+	dest: MultiLocation,
+	beneficiary_id: AccountId32,
+	amount: Balance,
+	assets: MultiAssets,
+	asset_id: Option<u32>,
+) -> TestArgs {
+	TestArgs {
+		dest,
+		beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
+		amount,
+		assets,
+		asset_id,
+		fee_asset_item: 0,
+		weight_limit: WeightLimit::Unlimited,
+	}
+}
+
+#[cfg(test)]
 mod tests;
diff --git a/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/tests/fellowship.rs b/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/tests/fellowship.rs
index 233fd67083d..e13090c1a51 100644
--- a/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/tests/fellowship.rs
+++ b/cumulus/parachains/integration-tests/emulated/collectives/collectives-polkadot/src/tests/fellowship.rs
@@ -34,8 +34,8 @@ fn pay_salary() {
 	let pay_to = Polkadot::account_id_of(ALICE);
 	let pay_amount = 9000;
 
-	AssetHub::execute_with(|| {
-		type AssetHubAssets = <AssetHub as AssetHubPallet>::Assets;
+	AssetHubPolkadot::execute_with(|| {
+		type AssetHubAssets = <AssetHubPolkadot as AssetHubPolkadotPallet>::Assets;
 
 		assert_ok!(<AssetHubAssets as Create<_>>::create(
 			asset_id,
@@ -47,7 +47,7 @@ fn pay_salary() {
 	});
 
 	Collectives::execute_with(|| {
-		type RuntimeEvent = <Collectives as Parachain>::RuntimeEvent;
+		type RuntimeEvent = <Collectives as Chain>::RuntimeEvent;
 
 		assert_ok!(FellowshipSalaryPaymaster::pay(&pay_to, (), pay_amount));
 		assert_expected_events!(
@@ -58,11 +58,11 @@ fn pay_salary() {
 		);
 	});
 
-	AssetHub::execute_with(|| {
-		type RuntimeEvent = <AssetHub as Parachain>::RuntimeEvent;
+	AssetHubPolkadot::execute_with(|| {
+		type RuntimeEvent = <AssetHubPolkadot as Chain>::RuntimeEvent;
 
 		assert_expected_events!(
-			AssetHub,
+			AssetHubPolkadot,
 			vec![
 				RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => {
 					asset_id: id == &asset_id,
diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml
index ec7fd21fac7..6a0fa51e6b2 100644
--- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml
@@ -7,6 +7,8 @@ description = "Common resources for integration testing with xcm-emulator"
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false }
+lazy_static = "1.4.0"
+paste = "1.0.14"
 
 # Substrate
 grandpa = { package = "sc-consensus-grandpa", git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -20,6 +22,7 @@ sp-consensus-babe = { default-features = false, git = "https://github.com/parity
 pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
 pallet-assets = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
 pallet-staking = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-message-queue = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
 pallet-im-online = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
 beefy-primitives = { package = "sp-consensus-beefy", git = "https://github.com/paritytech/substrate", branch = "master" }
 
@@ -54,6 +57,9 @@ bridge-hub-kusama-runtime = { path = "../../../runtimes/bridge-hubs/bridge-hub-k
 bridge-hub-polkadot-runtime = { path = "../../../runtimes/bridge-hubs/bridge-hub-polkadot" }
 bridge-hub-rococo-runtime = { path = "../../../runtimes/bridge-hubs/bridge-hub-rococo" }
 xcm-emulator = { default-features = false, path = "../../../../xcm/xcm-emulator" }
+cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../pallets/xcmp-queue" }
+cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system" }
 bp-messages = { path = "../../../../bridges/primitives/messages"}
 bp-runtime = { path = "../../../../bridges/primitives/runtime"}
 pallet-bridge-messages = { path = "../../../../bridges/modules/messages" }
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/constants.rs b/cumulus/parachains/integration-tests/emulated/common/src/constants.rs
index 8c658b0b2bb..ed529b867bc 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/constants.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/constants.rs
@@ -2,8 +2,12 @@ use beefy_primitives::ecdsa_crypto::AuthorityId as BeefyId;
 use grandpa::AuthorityId as GrandpaId;
 use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use parachains_common::{AccountId, AssetHubPolkadotAuraId, AuraId, Balance, BlockNumber};
+use polkadot_parachain::primitives::{HeadData, ValidationCode};
 use polkadot_primitives::{AssignmentId, ValidatorId};
-use polkadot_runtime_parachains::configuration::HostConfiguration;
+use polkadot_runtime_parachains::{
+	configuration::HostConfiguration,
+	paras::{ParaGenesisArgs, ParaKind},
+};
 use polkadot_service::chain_spec::get_authority_keys_from_seed_no_beefy;
 use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
 use sp_consensus_babe::AuthorityId as BabeId;
@@ -129,6 +133,13 @@ pub mod polkadot {
 			max_upward_message_size: 51200,
 			max_upward_message_num_per_candidate: 10,
 			max_downward_message_size: 51200,
+			hrmp_sender_deposit: 100_000_000_000,
+			hrmp_recipient_deposit: 100_000_000_000,
+			hrmp_channel_max_capacity: 1000,
+			hrmp_channel_max_message_size: 102400,
+			hrmp_channel_max_total_size: 102400,
+			hrmp_max_parachain_outbound_channels: 30,
+			hrmp_max_parachain_inbound_channels: 30,
 			..Default::default()
 		}
 	}
@@ -206,6 +217,41 @@ pub mod polkadot {
 				..Default::default()
 			},
 			configuration: polkadot_runtime::ConfigurationConfig { config: get_host_config() },
+			paras: polkadot_runtime::ParasConfig {
+				paras: vec![
+					(
+						asset_hub_polkadot::PARA_ID.into(),
+						ParaGenesisArgs {
+							genesis_head: HeadData::default(),
+							validation_code: ValidationCode(
+								asset_hub_polkadot_runtime::WASM_BINARY.unwrap().to_vec(),
+							),
+							para_kind: ParaKind::Parachain,
+						},
+					),
+					(
+						penpal::PARA_ID_A.into(),
+						ParaGenesisArgs {
+							genesis_head: HeadData::default(),
+							validation_code: ValidationCode(
+								penpal_runtime::WASM_BINARY.unwrap().to_vec(),
+							),
+							para_kind: ParaKind::Parachain,
+						},
+					),
+					(
+						penpal::PARA_ID_B.into(),
+						ParaGenesisArgs {
+							genesis_head: HeadData::default(),
+							validation_code: ValidationCode(
+								penpal_runtime::WASM_BINARY.unwrap().to_vec(),
+							),
+							para_kind: ParaKind::Parachain,
+						},
+					),
+				],
+				..Default::default()
+			},
 			..Default::default()
 		};
 
@@ -228,6 +274,13 @@ pub mod westend {
 			max_upward_message_size: 51200,
 			max_upward_message_num_per_candidate: 10,
 			max_downward_message_size: 51200,
+			hrmp_sender_deposit: 100_000_000_000,
+			hrmp_recipient_deposit: 100_000_000_000,
+			hrmp_channel_max_capacity: 1000,
+			hrmp_channel_max_message_size: 102400,
+			hrmp_channel_max_total_size: 102400,
+			hrmp_max_parachain_outbound_channels: 30,
+			hrmp_max_parachain_inbound_channels: 30,
 			..Default::default()
 		}
 	}
@@ -327,6 +380,13 @@ pub mod kusama {
 			max_upward_message_size: 51200,
 			max_upward_message_num_per_candidate: 10,
 			max_downward_message_size: 51200,
+			hrmp_sender_deposit: 5_000_000_000_000,
+			hrmp_recipient_deposit: 5_000_000_000_000,
+			hrmp_channel_max_capacity: 1000,
+			hrmp_channel_max_message_size: 102400,
+			hrmp_channel_max_total_size: 102400,
+			hrmp_max_parachain_outbound_channels: 30,
+			hrmp_max_parachain_inbound_channels: 30,
 			..Default::default()
 		}
 	}
@@ -403,6 +463,41 @@ pub mod kusama {
 				..Default::default()
 			},
 			configuration: kusama_runtime::ConfigurationConfig { config: get_host_config() },
+			paras: kusama_runtime::ParasConfig {
+				paras: vec![
+					(
+						asset_hub_kusama::PARA_ID.into(),
+						ParaGenesisArgs {
+							genesis_head: HeadData::default(),
+							validation_code: ValidationCode(
+								asset_hub_kusama_runtime::WASM_BINARY.unwrap().to_vec(),
+							),
+							para_kind: ParaKind::Parachain,
+						},
+					),
+					(
+						penpal::PARA_ID_A.into(),
+						ParaGenesisArgs {
+							genesis_head: HeadData::default(),
+							validation_code: ValidationCode(
+								penpal_runtime::WASM_BINARY.unwrap().to_vec(),
+							),
+							para_kind: ParaKind::Parachain,
+						},
+					),
+					(
+						penpal::PARA_ID_B.into(),
+						ParaGenesisArgs {
+							genesis_head: HeadData::default(),
+							validation_code: ValidationCode(
+								penpal_runtime::WASM_BINARY.unwrap().to_vec(),
+							),
+							para_kind: ParaKind::Parachain,
+						},
+					),
+				],
+				..Default::default()
+			},
 			..Default::default()
 		};
 
@@ -419,10 +514,18 @@ pub mod rococo {
 
 	pub fn get_host_config() -> HostConfiguration<BlockNumber> {
 		HostConfiguration {
+			max_upward_queue_count: 10,
 			max_upward_queue_size: 51200,
 			max_upward_message_size: 51200,
 			max_upward_message_num_per_candidate: 10,
 			max_downward_message_size: 51200,
+			hrmp_sender_deposit: 0,
+			hrmp_recipient_deposit: 0,
+			hrmp_channel_max_capacity: 1000,
+			hrmp_channel_max_message_size: 102400,
+			hrmp_channel_max_total_size: 102400,
+			hrmp_max_parachain_outbound_channels: 30,
+			hrmp_max_parachain_inbound_channels: 30,
 			..Default::default()
 		}
 	}
@@ -674,7 +777,8 @@ pub mod asset_hub_kusama {
 // Penpal
 pub mod penpal {
 	use super::*;
-	pub const PARA_ID: u32 = 2000;
+	pub const PARA_ID_A: u32 = 2000;
+	pub const PARA_ID_B: u32 = 2001;
 	pub const ED: Balance = penpal_runtime::EXISTENTIAL_DEPOSIT;
 
 	pub fn genesis(para_id: u32) -> Storage {
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs
index 9c4acf4637e..92c68f4dd61 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs
@@ -1,4 +1,5 @@
 use super::{BridgeHubRococo, BridgeHubWococo};
+// pub use paste;
 use bp_messages::{
 	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
 	LaneId, MessageKey, OutboundLaneData,
@@ -8,7 +9,7 @@ use codec::Decode;
 pub use cumulus_primitives_core::{DmpMessageHandler, XcmpMessageHandler};
 use pallet_bridge_messages::{Config, Instance1, Instance2, OutboundLanes, Pallet};
 use sp_core::Get;
-use xcm_emulator::{BridgeMessage, BridgeMessageDispatchError, BridgeMessageHandler, Parachain};
+use xcm_emulator::{BridgeMessage, BridgeMessageDispatchError, BridgeMessageHandler, Chain};
 
 pub struct BridgeHubMessageHandler<S, T, I> {
 	_marker: std::marker::PhantomData<(S, T, I)>,
@@ -28,12 +29,12 @@ impl From<u32> for LaneIdWrapper {
 	}
 }
 
-type BridgeHubRococoRuntime = <BridgeHubRococo as Parachain>::Runtime;
-type BridgeHubWococoRuntime = <BridgeHubWococo as Parachain>::Runtime;
+type BridgeHubRococoRuntime = <BridgeHubRococo as Chain>::Runtime;
+type BridgeHubWococoRuntime = <BridgeHubWococo as Chain>::Runtime;
 
 // TODO: uncomment when https://github.com/paritytech/cumulus/pull/2528 is merged
-// type BridgeHubPolkadotRuntime = <BridgeHubPolkadot as Parachain>::Runtime;
-// type BridgeHubKusamaRuntime = <BridgeHubKusama as Parachain>::Runtime;
+// type BridgeHubPolkadotRuntime = <BridgeHubPolkadot as Chain>::Runtime;
+// type BridgeHubKusamaRuntime = <BridgeHubKusama as Chain>::Runtime;
 
 pub type RococoWococoMessageHandler =
 	BridgeHubMessageHandler<BridgeHubRococoRuntime, BridgeHubWococoRuntime, Instance2>;
@@ -124,3 +125,473 @@ where
 		OutboundLanes::<S, Instance1>::insert(LaneIdWrapper::from(lane_id).0, new_data);
 	}
 }
+
+#[macro_export]
+macro_rules! impl_accounts_helpers_for_relay_chain {
+	( $chain:ident ) => {
+		$crate::paste::paste! {
+			impl $chain {
+				/// Fund a set of accounts with a balance
+				pub fn fund_accounts(accounts: Vec<(AccountId, Balance)>) {
+					Self::execute_with(|| {
+						for account in accounts {
+							assert_ok!(<Self as [<$chain Pallet>]>::Balances::force_set_balance(
+								<Self as Chain>::RuntimeOrigin::root(),
+								account.0.into(),
+								account.1,
+							));
+						}
+					});
+				}
+				/// Fund a sovereign account based on its Parachain Id
+				pub fn fund_para_sovereign(amount: Balance, para_id: ParaId) -> sp_runtime::AccountId32 {
+					let sovereign_account = Self::sovereign_account_id_of_child_para(para_id);
+					Self::fund_accounts(vec![(sovereign_account.clone(), amount)]);
+					sovereign_account
+				}
+			}
+		}
+	};
+}
+
+#[macro_export]
+macro_rules! impl_assert_events_helpers_for_relay_chain {
+	( $chain:ident ) => {
+		$crate::paste::paste! {
+			type [<$chain RuntimeEvent>] = <$chain as Chain>::RuntimeEvent;
+
+			impl $chain {
+				/// Asserts a dispatchable is completely executed and XCM sent
+				pub fn assert_xcm_pallet_attempted_complete(expected_weight: Option<Weight>) {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::XcmPallet(
+								pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }
+							) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+							},
+						]
+					);
+				}
+
+				/// Asserts a dispatchable is incompletely executed and XCM sent
+				pub fn assert_xcm_pallet_attempted_incomplete(
+					expected_weight: Option<Weight>,
+					expected_error: Option<Error>,
+				) {
+					assert_expected_events!(
+						Self,
+						vec![
+							// Dispatchable is properly executed and XCM message sent
+							[<$chain RuntimeEvent>]::XcmPallet(
+								pallet_xcm::Event::Attempted { outcome: Outcome::Incomplete(weight, error) }
+							) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+								error: *error == expected_error.unwrap_or(*error),
+							},
+						]
+					);
+				}
+
+				/// Asserts a XCM message is sent
+				pub fn assert_xcm_pallet_sent() {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
+						]
+					);
+				}
+
+				/// Asserts a XCM from System Parachain is succesfully received and proccessed
+				pub fn assert_ump_queue_processed(
+					expected_success: bool,
+					expected_id: Option<ParaId>,
+					expected_weight: Option<Weight>,
+				) {
+					assert_expected_events!(
+						Self,
+						vec![
+							// XCM is succesfully received and proccessed
+							[<$chain RuntimeEvent>]::MessageQueue(pallet_message_queue::Event::Processed {
+								origin: AggregateMessageOrigin::Ump(UmpQueueId::Para(id)),
+								weight_used,
+								success,
+								..
+							}) => {
+								id: *id == expected_id.unwrap_or(*id),
+								weight_used: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight_used),
+									*weight_used
+								),
+								success: *success == expected_success,
+							},
+						]
+					);
+				}
+			}
+		}
+	};
+}
+
+#[macro_export]
+macro_rules! impl_hrmp_channels_helpers_for_relay_chain {
+	( $chain:ident ) => {
+		$crate::paste::paste! {
+			impl $chain {
+				/// Init open channel request with another Parachain
+				pub fn init_open_channel_call(
+					recipient_para_id: ParaId,
+					max_capacity: u32,
+					max_message_size: u32,
+				) -> DoubleEncoded<()> {
+					<Self as Chain>::RuntimeCall::Hrmp(polkadot_runtime_parachains::hrmp::Call::<
+						<Self as Chain>::Runtime,
+					>::hrmp_init_open_channel {
+						recipient: recipient_para_id,
+						proposed_max_capacity: max_capacity,
+						proposed_max_message_size: max_message_size,
+					})
+					.encode()
+					.into()
+				}
+				/// Recipient Parachain accept the open request from another Parachain
+				pub fn accept_open_channel_call(sender_para_id: ParaId) -> DoubleEncoded<()> {
+					<Self as Chain>::RuntimeCall::Hrmp(polkadot_runtime_parachains::hrmp::Call::<
+						<Self as Chain>::Runtime,
+					>::hrmp_accept_open_channel {
+						sender: sender_para_id,
+					})
+					.encode()
+					.into()
+				}
+
+				/// A root origin force to open a channel between two Parachains
+				pub fn force_process_hrmp_open(sender: ParaId, recipient: ParaId) {
+					Self::execute_with(|| {
+						let relay_root_origin = <Self as Chain>::RuntimeOrigin::root();
+
+						// Force process HRMP open channel requests without waiting for the next session
+						assert_ok!(<Self as [<$chain Pallet>]>::Hrmp::force_process_hrmp_open(
+							relay_root_origin,
+							0
+						));
+
+						let channel_id = HrmpChannelId { sender, recipient };
+
+						let hrmp_channel_exist = polkadot_runtime_parachains::hrmp::HrmpChannels::<
+							<Self as Chain>::Runtime,
+						>::contains_key(&channel_id);
+
+						// Check the HRMP channel has been successfully registrered
+						assert!(hrmp_channel_exist)
+					});
+				}
+			}
+		}
+	};
+}
+
+#[macro_export]
+macro_rules! impl_accounts_helpers_for_parachain {
+	( $chain:ident ) => {
+		$crate::paste::paste! {
+			impl $chain {
+				/// Fund a set of accounts with a balance
+				pub fn fund_accounts(accounts: Vec<(AccountId, Balance)>) {
+					Self::execute_with(|| {
+						for account in accounts {
+							assert_ok!(<Self as [<$chain Pallet>]>::Balances::force_set_balance(
+								<Self as Chain>::RuntimeOrigin::root(),
+								account.0.into(),
+								account.1,
+							));
+						}
+					});
+				}
+			}
+		}
+	};
+}
+
+#[macro_export]
+macro_rules! impl_assert_events_helpers_for_parachain {
+	( $chain:ident ) => {
+		$crate::paste::paste! {
+			type [<$chain RuntimeEvent>] = <$chain as Chain>::RuntimeEvent;
+
+			impl $chain {
+				/// Asserts a dispatchable is completely executed and XCM sent
+				pub fn assert_xcm_pallet_attempted_complete(expected_weight: Option<Weight>) {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::PolkadotXcm(
+								pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }
+							) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+							},
+						]
+					);
+				}
+
+				/// Asserts a dispatchable is incompletely executed and XCM sent
+				pub fn assert_xcm_pallet_attempted_incomplete(
+					expected_weight: Option<Weight>,
+					expected_error: Option<Error>,
+				) {
+					assert_expected_events!(
+						Self,
+						vec![
+							// Dispatchable is properly executed and XCM message sent
+							[<$chain RuntimeEvent>]::PolkadotXcm(
+								pallet_xcm::Event::Attempted { outcome: Outcome::Incomplete(weight, error) }
+							) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+								error: *error == expected_error.unwrap_or(*error),
+							},
+						]
+					);
+				}
+
+				/// Asserts a dispatchable throws and error when trying to be sent
+				pub fn assert_xcm_pallet_attempted_error(expected_error: Option<Error>) {
+					assert_expected_events!(
+						Self,
+						vec![
+							// Execution fails in the origin with `Barrier`
+							[<$chain RuntimeEvent>]::PolkadotXcm(
+								pallet_xcm::Event::Attempted { outcome: Outcome::Error(error) }
+							) => {
+								error: *error == expected_error.unwrap_or(*error),
+							},
+						]
+					);
+				}
+
+				/// Asserts a XCM message is sent
+				pub fn assert_xcm_pallet_sent() {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::PolkadotXcm(pallet_xcm::Event::Sent { .. }) => {},
+						]
+					);
+				}
+
+				/// Asserts a XCM message is sent to Relay Chain
+				pub fn assert_parachain_system_ump_sent() {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::ParachainSystem(
+								cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
+							) => {},
+						]
+					);
+				}
+
+				/// Asserts a XCM from Relay Chain is completely executed
+				pub fn assert_dmp_queue_complete(expected_weight: Option<Weight>) {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward {
+								outcome: Outcome::Complete(weight), ..
+							}) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+							},
+						]
+					);
+				}
+
+				/// Asserts a XCM from Relay Chain is incompletely executed
+				pub fn assert_dmp_queue_incomplete(
+					expected_weight: Option<Weight>,
+					expected_error: Option<Error>,
+				) {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward {
+								outcome: Outcome::Incomplete(weight, error), ..
+							}) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+								error: *error == expected_error.unwrap_or(*error),
+							},
+						]
+					);
+				}
+
+				/// Asserts a XCM from another Parachain is completely executed
+				pub fn assert_xcmp_queue_success(expected_weight: Option<Weight>) {
+					assert_expected_events!(
+						Self,
+						vec![
+							[<$chain RuntimeEvent>]::XcmpQueue(
+								cumulus_pallet_xcmp_queue::Event::Success { weight, .. }
+							) => {
+								weight: weight_within_threshold(
+									(REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD),
+									expected_weight.unwrap_or(*weight),
+									*weight
+								),
+							},
+						]
+					);
+				}
+			}
+		}
+	};
+}
+
+#[macro_export]
+macro_rules! impl_assets_helpers_for_parachain {
+	( $chain:ident, $relay_chain:ident ) => {
+		$crate::paste::paste! {
+			impl $chain {
+				/// Returns the encoded call for `force_create` from the assets pallet
+				pub fn force_create_asset_call(
+					asset_id: u32,
+					owner: AccountId,
+					is_sufficient: bool,
+					min_balance: Balance,
+				) -> DoubleEncoded<()> {
+					<Self as Chain>::RuntimeCall::Assets(pallet_assets::Call::<
+						<Self as Chain>::Runtime,
+						Instance1,
+					>::force_create {
+						id: asset_id.into(),
+						owner: owner.into(),
+						is_sufficient,
+						min_balance,
+					})
+					.encode()
+					.into()
+				}
+
+				/// Returns a `VersionedXcm` for `force_create` from the assets pallet
+				pub fn force_create_asset_xcm(
+					origin_kind: OriginKind,
+					asset_id: u32,
+					owner: AccountId,
+					is_sufficient: bool,
+					min_balance: Balance,
+				) -> VersionedXcm<()> {
+					let call = Self::force_create_asset_call(asset_id, owner, is_sufficient, min_balance);
+					xcm_transact_unpaid_execution(call, origin_kind)
+				}
+
+				/// Mint assets making use of the assets pallet
+				pub fn mint_asset(
+					signed_origin: <Self as Chain>::RuntimeOrigin,
+					id: u32,
+					beneficiary: AccountId,
+					amount_to_mint: u128,
+				) {
+					Self::execute_with(|| {
+						assert_ok!(<Self as [<$chain Pallet>]>::Assets::mint(
+							signed_origin,
+							id.into(),
+							beneficiary.clone().into(),
+							amount_to_mint
+						));
+
+						type RuntimeEvent = <$chain as Chain>::RuntimeEvent;
+
+						assert_expected_events!(
+							Self,
+							vec![
+								RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, owner, amount }) => {
+									asset_id: *asset_id == id,
+									owner: *owner == beneficiary.clone().into(),
+									amount: *amount == amount_to_mint,
+								},
+							]
+						);
+					});
+				}
+
+				/// Force create and mint assets making use of the assets pallet
+				pub fn force_create_and_mint_asset(
+					id: u32,
+					min_balance: u128,
+					is_sufficient: bool,
+					asset_owner: AccountId,
+					amount_to_mint: u128,
+				) {
+					// Init values for Relay Chain
+					let root_origin = <$relay_chain as Chain>::RuntimeOrigin::root();
+					let destination = <$relay_chain>::child_location_of(<$chain>::para_id());
+					let xcm = Self::force_create_asset_xcm(
+						OriginKind::Superuser,
+						id,
+						asset_owner.clone(),
+						is_sufficient,
+						min_balance,
+					);
+
+					<$relay_chain>::execute_with(|| {
+						assert_ok!(<$relay_chain as [<$relay_chain Pallet>]>::XcmPallet::send(
+							root_origin,
+							bx!(destination.into()),
+							bx!(xcm),
+						));
+
+						<$relay_chain>::assert_xcm_pallet_sent();
+					});
+
+					Self::execute_with(|| {
+						Self::assert_dmp_queue_complete(Some(Weight::from_parts(1_019_445_000, 200_000)));
+
+						type RuntimeEvent = <$chain as Chain>::RuntimeEvent;
+
+						assert_expected_events!(
+							Self,
+							vec![
+								// Asset has been created
+								RuntimeEvent::Assets(pallet_assets::Event::ForceCreated { asset_id, owner }) => {
+									asset_id: *asset_id == id,
+									owner: *owner == asset_owner.clone(),
+								},
+							]
+						);
+
+						assert!(<Self as [<$chain Pallet>]>::Assets::asset_exists(id.into()));
+					});
+
+					let signed_origin = <Self as Chain>::RuntimeOrigin::signed(asset_owner.clone());
+
+					// Mint asset for System Parachain's sender
+					Self::mint_asset(signed_origin, id, asset_owner, amount_to_mint);
+				}
+			}
+		}
+	};
+}
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs
index 23b05a54c72..7ef57027c45 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs
@@ -1,121 +1,114 @@
+pub use lazy_static;
 pub mod constants;
 pub mod impls;
 
+pub use codec::Encode;
 pub use constants::{
 	accounts::{ALICE, BOB},
 	asset_hub_kusama, asset_hub_polkadot, asset_hub_westend, bridge_hub_kusama,
 	bridge_hub_polkadot, bridge_hub_rococo, collectives, kusama, penpal, polkadot, rococo, westend,
+	PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD,
+};
+use frame_support::{
+	assert_ok, instances::Instance1, parameter_types, sp_tracing, traits::fungibles::Inspect,
 };
 pub use impls::{RococoWococoMessageHandler, WococoRococoMessageHandler};
-
-use frame_support::{parameter_types, sp_io, sp_tracing};
-pub use parachains_common::{AccountId, AssetHubPolkadotAuraId, AuraId, Balance, BlockNumber};
+pub use parachains_common::{AccountId, Balance};
+pub use paste;
+use polkadot_parachain::primitives::HrmpChannelId;
+pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
 pub use sp_core::{sr25519, storage::Storage, Get};
-use xcm::prelude::*;
 use xcm_emulator::{
-	decl_test_bridges, decl_test_networks, decl_test_parachains, decl_test_relay_chains,
-	decl_test_sender_receiver_accounts_parameter_types, BridgeMessageHandler, Parachain,
-	RelayChain, TestExt,
+	assert_expected_events, bx, decl_test_bridges, decl_test_networks, decl_test_parachains,
+	decl_test_relay_chains, decl_test_sender_receiver_accounts_parameter_types,
+	helpers::weight_within_threshold, BridgeMessageHandler, Chain, DefaultMessageProcessor, ParaId,
+	Parachain, RelayChain, TestExt,
+};
+
+pub use xcm::{
+	prelude::{
+		AccountId32, All, BuyExecution, DepositAsset, MultiAsset, MultiAssets, MultiLocation,
+		OriginKind, Outcome, RefundSurplus, Transact, UnpaidExecution, VersionedXcm, Weight,
+		WeightLimit, WithdrawAsset, Xcm, X1,
+	},
+	v3::Error,
+	DoubleEncoded,
 };
-use xcm_executor::traits::ConvertLocation;
 
 decl_test_relay_chains! {
 	#[api_version(5)]
 	pub struct Polkadot {
 		genesis = polkadot::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: polkadot_runtime::Runtime,
-			RuntimeOrigin: polkadot_runtime::RuntimeOrigin,
-			RuntimeCall: polkadot_runtime::RuntimeCall,
-			RuntimeEvent: polkadot_runtime::RuntimeEvent,
-			MessageQueue: polkadot_runtime::MessageQueue,
-			XcmConfig: polkadot_runtime::xcm_config::XcmConfig,
+		runtime = polkadot_runtime,
+		core = {
+			MessageProcessor: DefaultMessageProcessor<Polkadot>,
 			SovereignAccountOf: polkadot_runtime::xcm_config::SovereignAccountOf,
-			System: polkadot_runtime::System,
-			Balances: polkadot_runtime::Balances,
 		},
-		pallets_extra = {
+		pallets = {
 			XcmPallet: polkadot_runtime::XcmPallet,
+			Balances: polkadot_runtime::Balances,
+			Hrmp: polkadot_runtime::Hrmp,
 		}
 	},
 	#[api_version(5)]
 	pub struct Kusama {
 		genesis = kusama::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: kusama_runtime::Runtime,
-			RuntimeOrigin: kusama_runtime::RuntimeOrigin,
-			RuntimeCall: kusama_runtime::RuntimeCall,
-			RuntimeEvent: kusama_runtime::RuntimeEvent,
-			MessageQueue: kusama_runtime::MessageQueue,
-			XcmConfig: kusama_runtime::xcm_config::XcmConfig,
+		runtime = kusama_runtime,
+		core = {
+			MessageProcessor: DefaultMessageProcessor<Kusama>,
 			SovereignAccountOf: kusama_runtime::xcm_config::SovereignAccountOf,
-			System: kusama_runtime::System,
-			Balances: kusama_runtime::Balances,
 		},
-		pallets_extra = {
+		pallets = {
 			XcmPallet: kusama_runtime::XcmPallet,
+			Balances: kusama_runtime::Balances,
+			Hrmp: kusama_runtime::Hrmp,
 		}
 	},
 	#[api_version(5)]
 	pub struct Westend {
 		genesis = westend::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: westend_runtime::Runtime,
-			RuntimeOrigin: westend_runtime::RuntimeOrigin,
-			RuntimeCall: westend_runtime::RuntimeCall,
-			RuntimeEvent: westend_runtime::RuntimeEvent,
-			MessageQueue: westend_runtime::MessageQueue,
-			XcmConfig: westend_runtime::xcm_config::XcmConfig,
+		runtime = westend_runtime,
+		core = {
+			MessageProcessor: DefaultMessageProcessor<Westend>,
 			SovereignAccountOf: westend_runtime::xcm_config::LocationConverter, //TODO: rename to SovereignAccountOf,
-			System: westend_runtime::System,
-			Balances: westend_runtime::Balances,
 		},
-		pallets_extra = {
+		pallets = {
 			XcmPallet: westend_runtime::XcmPallet,
 			Sudo: westend_runtime::Sudo,
+			Balances: westend_runtime::Balances,
 		}
 	},
 	#[api_version(5)]
 	pub struct Rococo {
 		genesis = rococo::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: rococo_runtime::Runtime,
-			RuntimeOrigin: rococo_runtime::RuntimeOrigin,
-			RuntimeCall: rococo_runtime::RuntimeCall,
-			RuntimeEvent: rococo_runtime::RuntimeEvent,
-			MessageQueue: rococo_runtime::MessageQueue,
-			XcmConfig: rococo_runtime::xcm_config::XcmConfig,
+		runtime = rococo_runtime,
+		core = {
+			MessageProcessor: DefaultMessageProcessor<Rococo>,
 			SovereignAccountOf: rococo_runtime::xcm_config::LocationConverter, //TODO: rename to SovereignAccountOf,
-			System: rococo_runtime::System,
-			Balances: rococo_runtime::Balances,
 		},
-		pallets_extra = {
+		pallets = {
 			XcmPallet: rococo_runtime::XcmPallet,
 			Sudo: rococo_runtime::Sudo,
+			Balances: rococo_runtime::Balances,
 		}
 	},
 	#[api_version(5)]
 	pub struct Wococo {
 		genesis = rococo::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: rococo_runtime::Runtime,
-			RuntimeOrigin: rococo_runtime::RuntimeOrigin,
-			RuntimeCall: rococo_runtime::RuntimeCall,
-			RuntimeEvent: rococo_runtime::RuntimeEvent,
-			MessageQueue: rococo_runtime::MessageQueue,
-			XcmConfig: rococo_runtime::xcm_config::XcmConfig,
+		runtime = rococo_runtime,
+		core = {
+			MessageProcessor: DefaultMessageProcessor<Wococo>,
 			SovereignAccountOf: rococo_runtime::xcm_config::LocationConverter, //TODO: rename to SovereignAccountOf,
-			System: rococo_runtime::System,
-			Balances: rococo_runtime::Balances,
 		},
-		pallets_extra = {
+		pallets = {
 			XcmPallet: rococo_runtime::XcmPallet,
 			Sudo: rococo_runtime::Sudo,
+			Balances: rococo_runtime::Balances,
 		}
 	}
 }
@@ -125,81 +118,74 @@ decl_test_parachains! {
 	pub struct AssetHubPolkadot {
 		genesis = asset_hub_polkadot::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: asset_hub_polkadot_runtime::Runtime,
-			RuntimeOrigin: asset_hub_polkadot_runtime::RuntimeOrigin,
-			RuntimeCall: asset_hub_polkadot_runtime::RuntimeCall,
-			RuntimeEvent: asset_hub_polkadot_runtime::RuntimeEvent,
+		runtime = asset_hub_polkadot_runtime,
+		core = {
 			XcmpMessageHandler: asset_hub_polkadot_runtime::XcmpQueue,
 			DmpMessageHandler: asset_hub_polkadot_runtime::DmpQueue,
 			LocationToAccountId: asset_hub_polkadot_runtime::xcm_config::LocationToAccountId,
-			System: asset_hub_polkadot_runtime::System,
-			Balances: asset_hub_polkadot_runtime::Balances,
-			ParachainSystem: asset_hub_polkadot_runtime::ParachainSystem,
 			ParachainInfo: asset_hub_polkadot_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: asset_hub_polkadot_runtime::PolkadotXcm,
 			Assets: asset_hub_polkadot_runtime::Assets,
+			Balances: asset_hub_polkadot_runtime::Balances,
 		}
 	},
 	pub struct Collectives {
 		genesis = collectives::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: collectives_polkadot_runtime::Runtime,
-			RuntimeOrigin: collectives_polkadot_runtime::RuntimeOrigin,
-			RuntimeCall: collectives_polkadot_runtime::RuntimeCall,
-			RuntimeEvent: collectives_polkadot_runtime::RuntimeEvent,
+		runtime = collectives_polkadot_runtime,
+		core = {
 			XcmpMessageHandler: collectives_polkadot_runtime::XcmpQueue,
 			DmpMessageHandler: collectives_polkadot_runtime::DmpQueue,
 			LocationToAccountId: collectives_polkadot_runtime::xcm_config::LocationToAccountId,
-			System: collectives_polkadot_runtime::System,
-			Balances: collectives_polkadot_runtime::Balances,
-			ParachainSystem: collectives_polkadot_runtime::ParachainSystem,
 			ParachainInfo: collectives_polkadot_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: collectives_polkadot_runtime::PolkadotXcm,
+			Balances: collectives_polkadot_runtime::Balances,
 		}
 	},
 	pub struct BridgeHubPolkadot {
 		genesis = bridge_hub_polkadot::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: bridge_hub_polkadot_runtime::Runtime,
-			RuntimeOrigin: bridge_hub_polkadot_runtime::RuntimeOrigin,
-			RuntimeCall: bridge_hub_polkadot_runtime::RuntimeCall,
-			RuntimeEvent: bridge_hub_polkadot_runtime::RuntimeEvent,
+		runtime = bridge_hub_polkadot_runtime,
+		core = {
 			XcmpMessageHandler: bridge_hub_polkadot_runtime::XcmpQueue,
 			DmpMessageHandler: bridge_hub_polkadot_runtime::DmpQueue,
 			LocationToAccountId: bridge_hub_polkadot_runtime::xcm_config::LocationToAccountId,
-			System: bridge_hub_polkadot_runtime::System,
-			Balances: bridge_hub_polkadot_runtime::Balances,
-			ParachainSystem: bridge_hub_polkadot_runtime::ParachainSystem,
 			ParachainInfo: bridge_hub_polkadot_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: bridge_hub_polkadot_runtime::PolkadotXcm,
 		}
 	},
-	pub struct PenpalPolkadot {
-		genesis = penpal::genesis(penpal::PARA_ID),
+	pub struct PenpalPolkadotA {
+		genesis = penpal::genesis(penpal::PARA_ID_A),
 		on_init = (),
-		runtime = {
-			Runtime: penpal_runtime::Runtime,
-			RuntimeOrigin: penpal_runtime::RuntimeOrigin,
-			RuntimeCall: penpal_runtime::RuntimeCall,
-			RuntimeEvent: penpal_runtime::RuntimeEvent,
+		runtime = penpal_runtime,
+		core = {
 			XcmpMessageHandler: penpal_runtime::XcmpQueue,
 			DmpMessageHandler: penpal_runtime::DmpQueue,
 			LocationToAccountId: penpal_runtime::xcm_config::LocationToAccountId,
-			System: penpal_runtime::System,
-			Balances: penpal_runtime::Balances,
-			ParachainSystem: penpal_runtime::ParachainSystem,
 			ParachainInfo: penpal_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
+			PolkadotXcm: penpal_runtime::PolkadotXcm,
+			Assets: penpal_runtime::Assets,
+		}
+	},
+	pub struct PenpalPolkadotB {
+		genesis = penpal::genesis(penpal::PARA_ID_B),
+		on_init = (),
+		runtime = penpal_runtime,
+		core = {
+			XcmpMessageHandler: penpal_runtime::XcmpQueue,
+			DmpMessageHandler: penpal_runtime::DmpQueue,
+			LocationToAccountId: penpal_runtime::xcm_config::LocationToAccountId,
+			ParachainInfo: penpal_runtime::ParachainInfo,
+		},
+		pallets = {
 			PolkadotXcm: penpal_runtime::PolkadotXcm,
 			Assets: penpal_runtime::Assets,
 		}
@@ -208,62 +194,60 @@ decl_test_parachains! {
 	pub struct AssetHubKusama {
 		genesis = asset_hub_kusama::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: asset_hub_kusama_runtime::Runtime,
-			RuntimeOrigin: asset_hub_kusama_runtime::RuntimeOrigin,
-			RuntimeCall: asset_hub_kusama_runtime::RuntimeCall,
-			RuntimeEvent: asset_hub_kusama_runtime::RuntimeEvent,
+		runtime = asset_hub_kusama_runtime,
+		core = {
 			XcmpMessageHandler: asset_hub_kusama_runtime::XcmpQueue,
 			DmpMessageHandler: asset_hub_kusama_runtime::DmpQueue,
 			LocationToAccountId: asset_hub_kusama_runtime::xcm_config::LocationToAccountId,
-			System: asset_hub_kusama_runtime::System,
-			Balances: asset_hub_kusama_runtime::Balances,
-			ParachainSystem: asset_hub_kusama_runtime::ParachainSystem,
 			ParachainInfo: asset_hub_kusama_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: asset_hub_kusama_runtime::PolkadotXcm,
 			Assets: asset_hub_kusama_runtime::Assets,
 			ForeignAssets: asset_hub_kusama_runtime::Assets,
+			Balances: asset_hub_kusama_runtime::Balances,
 		}
 	},
 	pub struct BridgeHubKusama {
 		genesis = bridge_hub_kusama::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: bridge_hub_kusama_runtime::Runtime,
-			RuntimeOrigin: bridge_hub_kusama_runtime::RuntimeOrigin,
-			RuntimeCall: bridge_hub_kusama_runtime::RuntimeCall,
-			RuntimeEvent: bridge_hub_kusama_runtime::RuntimeEvent,
+		runtime = bridge_hub_kusama_runtime,
+		core = {
 			XcmpMessageHandler: bridge_hub_kusama_runtime::XcmpQueue,
 			DmpMessageHandler: bridge_hub_kusama_runtime::DmpQueue,
 			LocationToAccountId: bridge_hub_kusama_runtime::xcm_config::LocationToAccountId,
-			System: bridge_hub_kusama_runtime::System,
-			Balances: bridge_hub_kusama_runtime::Balances,
-			ParachainSystem: bridge_hub_kusama_runtime::ParachainSystem,
 			ParachainInfo: bridge_hub_kusama_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: bridge_hub_kusama_runtime::PolkadotXcm,
 		}
 	},
-	pub struct PenpalKusama {
-		genesis = penpal::genesis(penpal::PARA_ID),
+	pub struct PenpalKusamaA {
+		genesis = penpal::genesis(penpal::PARA_ID_A),
+		on_init = (),
+		runtime = penpal_runtime,
+		core = {
+			XcmpMessageHandler: penpal_runtime::XcmpQueue,
+			DmpMessageHandler: penpal_runtime::DmpQueue,
+			LocationToAccountId: penpal_runtime::xcm_config::LocationToAccountId,
+			ParachainInfo: penpal_runtime::ParachainInfo,
+		},
+		pallets = {
+			PolkadotXcm: penpal_runtime::PolkadotXcm,
+			Assets: penpal_runtime::Assets,
+		}
+	},
+	pub struct PenpalKusamaB {
+		genesis = penpal::genesis(penpal::PARA_ID_B),
 		on_init = (),
-		runtime = {
-			Runtime: penpal_runtime::Runtime,
-			RuntimeOrigin: penpal_runtime::RuntimeOrigin,
-			RuntimeCall: penpal_runtime::RuntimeCall,
-			RuntimeEvent: penpal_runtime::RuntimeEvent,
+		runtime = penpal_runtime,
+		core = {
 			XcmpMessageHandler: penpal_runtime::XcmpQueue,
 			DmpMessageHandler: penpal_runtime::DmpQueue,
 			LocationToAccountId: penpal_runtime::xcm_config::LocationToAccountId,
-			System: penpal_runtime::System,
-			Balances: penpal_runtime::Balances,
-			ParachainSystem: penpal_runtime::ParachainSystem,
 			ParachainInfo: penpal_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: penpal_runtime::PolkadotXcm,
 			Assets: penpal_runtime::Assets,
 		}
@@ -272,44 +256,33 @@ decl_test_parachains! {
 	pub struct AssetHubWestend {
 		genesis = asset_hub_westend::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: asset_hub_westend_runtime::Runtime,
-			RuntimeOrigin: asset_hub_westend_runtime::RuntimeOrigin,
-			RuntimeCall: asset_hub_westend_runtime::RuntimeCall,
-			RuntimeEvent: asset_hub_westend_runtime::RuntimeEvent,
+		runtime = asset_hub_westend_runtime,
+		core = {
 			XcmpMessageHandler: asset_hub_westend_runtime::XcmpQueue,
 			DmpMessageHandler: asset_hub_westend_runtime::DmpQueue,
 			LocationToAccountId: asset_hub_westend_runtime::xcm_config::LocationToAccountId,
-			System: asset_hub_westend_runtime::System,
-			Balances: asset_hub_westend_runtime::Balances,
-			ParachainSystem: asset_hub_westend_runtime::ParachainSystem,
 			ParachainInfo: asset_hub_westend_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm,
+			Balances: asset_hub_westend_runtime::Balances,
 			Assets: asset_hub_westend_runtime::Assets,
 			ForeignAssets: asset_hub_westend_runtime::ForeignAssets,
 			PoolAssets: asset_hub_westend_runtime::PoolAssets,
 			AssetConversion: asset_hub_westend_runtime::AssetConversion,
 		}
 	},
-	pub struct PenpalWestend {
-		genesis = penpal::genesis(penpal::PARA_ID),
+	pub struct PenpalWestendA {
+		genesis = penpal::genesis(penpal::PARA_ID_A),
 		on_init = (),
-		runtime = {
-			Runtime: penpal_runtime::Runtime,
-			RuntimeOrigin: penpal_runtime::RuntimeOrigin,
-			RuntimeCall: penpal_runtime::RuntimeCall,
-			RuntimeEvent: penpal_runtime::RuntimeEvent,
+		runtime = penpal_runtime,
+		core = {
 			XcmpMessageHandler: penpal_runtime::XcmpQueue,
 			DmpMessageHandler: penpal_runtime::DmpQueue,
 			LocationToAccountId: penpal_runtime::xcm_config::LocationToAccountId,
-			System: penpal_runtime::System,
-			Balances: penpal_runtime::Balances,
-			ParachainSystem: penpal_runtime::ParachainSystem,
 			ParachainInfo: penpal_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: penpal_runtime::PolkadotXcm,
 			Assets: penpal_runtime::Assets,
 		}
@@ -318,85 +291,78 @@ decl_test_parachains! {
 	pub struct BridgeHubRococo {
 		genesis = bridge_hub_rococo::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: bridge_hub_rococo_runtime::Runtime,
-			RuntimeOrigin: bridge_hub_rococo_runtime::RuntimeOrigin,
-			RuntimeCall: bridge_hub_rococo_runtime::RuntimeCall,
-			RuntimeEvent: bridge_hub_rococo_runtime::RuntimeEvent,
+		runtime = bridge_hub_rococo_runtime,
+		core = {
 			XcmpMessageHandler: bridge_hub_rococo_runtime::XcmpQueue,
 			DmpMessageHandler: bridge_hub_rococo_runtime::DmpQueue,
 			LocationToAccountId: bridge_hub_rococo_runtime::xcm_config::LocationToAccountId,
-			System: bridge_hub_rococo_runtime::System,
-			Balances: bridge_hub_rococo_runtime::Balances,
-			ParachainSystem: bridge_hub_rococo_runtime::ParachainSystem,
 			ParachainInfo: bridge_hub_rococo_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: bridge_hub_rococo_runtime::PolkadotXcm,
+			Balances: bridge_hub_rococo_runtime::Balances,
 		}
 	},
+	// AssetHubRococo (aka Rockmine/Rockmine2) mirrors AssetHubKusama
 	pub struct AssetHubRococo {
-		genesis = asset_hub_polkadot::genesis(),
+		genesis = asset_hub_kusama::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: asset_hub_polkadot_runtime::Runtime,
-			RuntimeOrigin: asset_hub_polkadot_runtime::RuntimeOrigin,
-			RuntimeCall: asset_hub_polkadot_runtime::RuntimeCall,
-			RuntimeEvent: asset_hub_polkadot_runtime::RuntimeEvent,
-			XcmpMessageHandler: asset_hub_polkadot_runtime::XcmpQueue,
-			DmpMessageHandler: asset_hub_polkadot_runtime::DmpQueue,
-			LocationToAccountId: asset_hub_polkadot_runtime::xcm_config::LocationToAccountId,
-			System: asset_hub_polkadot_runtime::System,
-			Balances: asset_hub_polkadot_runtime::Balances,
-			ParachainSystem: asset_hub_polkadot_runtime::ParachainSystem,
-			ParachainInfo: asset_hub_polkadot_runtime::ParachainInfo,
+		runtime = asset_hub_kusama_runtime,
+		core = {
+			XcmpMessageHandler: asset_hub_kusama_runtime::XcmpQueue,
+			DmpMessageHandler: asset_hub_kusama_runtime::DmpQueue,
+			LocationToAccountId: asset_hub_kusama_runtime::xcm_config::LocationToAccountId,
+			ParachainInfo: asset_hub_kusama_runtime::ParachainInfo,
 		},
-		pallets_extra = {
-			PolkadotXcm: asset_hub_polkadot_runtime::PolkadotXcm,
-			Assets: asset_hub_polkadot_runtime::Assets,
+		pallets = {
+			PolkadotXcm: asset_hub_kusama_runtime::PolkadotXcm,
+			Assets: asset_hub_kusama_runtime::Assets,
 		}
 	},
 	// Wococo Parachains
 	pub struct BridgeHubWococo {
 		genesis = bridge_hub_rococo::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: bridge_hub_rococo_runtime::Runtime,
-			RuntimeOrigin: bridge_hub_rococo_runtime::RuntimeOrigin,
-			RuntimeCall: bridge_hub_rococo_runtime::RuntimeCall,
-			RuntimeEvent: bridge_hub_rococo_runtime::RuntimeEvent,
+		runtime = bridge_hub_rococo_runtime,
+		core = {
 			XcmpMessageHandler: bridge_hub_rococo_runtime::XcmpQueue,
 			DmpMessageHandler: bridge_hub_rococo_runtime::DmpQueue,
 			LocationToAccountId: bridge_hub_rococo_runtime::xcm_config::LocationToAccountId,
-			System: bridge_hub_rococo_runtime::System,
-			Balances: bridge_hub_rococo_runtime::Balances,
-			ParachainSystem: bridge_hub_rococo_runtime::ParachainSystem,
 			ParachainInfo: bridge_hub_rococo_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: bridge_hub_rococo_runtime::PolkadotXcm,
 		}
 	},
 	pub struct AssetHubWococo {
 		genesis = asset_hub_polkadot::genesis(),
 		on_init = (),
-		runtime = {
-			Runtime: asset_hub_polkadot_runtime::Runtime,
-			RuntimeOrigin: asset_hub_polkadot_runtime::RuntimeOrigin,
-			RuntimeCall: asset_hub_polkadot_runtime::RuntimeCall,
-			RuntimeEvent: asset_hub_polkadot_runtime::RuntimeEvent,
+		runtime = asset_hub_polkadot_runtime,
+		core = {
 			XcmpMessageHandler: asset_hub_polkadot_runtime::XcmpQueue,
 			DmpMessageHandler: asset_hub_polkadot_runtime::DmpQueue,
 			LocationToAccountId: asset_hub_polkadot_runtime::xcm_config::LocationToAccountId,
-			System: asset_hub_polkadot_runtime::System,
-			Balances: asset_hub_polkadot_runtime::Balances,
-			ParachainSystem: asset_hub_polkadot_runtime::ParachainSystem,
 			ParachainInfo: asset_hub_polkadot_runtime::ParachainInfo,
 		},
-		pallets_extra = {
+		pallets = {
 			PolkadotXcm: asset_hub_polkadot_runtime::PolkadotXcm,
 			Assets: asset_hub_polkadot_runtime::Assets,
 		}
+	},
+	pub struct PenpalRococoA {
+		genesis = penpal::genesis(penpal::PARA_ID_A),
+		on_init = (),
+		runtime = penpal_runtime,
+		core = {
+			XcmpMessageHandler: penpal_runtime::XcmpQueue,
+			DmpMessageHandler: penpal_runtime::DmpQueue,
+			LocationToAccountId: penpal_runtime::xcm_config::LocationToAccountId,
+			ParachainInfo: penpal_runtime::ParachainInfo,
+		},
+		pallets = {
+			PolkadotXcm: penpal_runtime::PolkadotXcm,
+			Assets: penpal_runtime::Assets,
+		}
 	}
 }
 
@@ -405,9 +371,10 @@ decl_test_networks! {
 		relay_chain = Polkadot,
 		parachains = vec![
 			AssetHubPolkadot,
-			PenpalPolkadot,
 			Collectives,
 			BridgeHubPolkadot,
+			PenpalPolkadotA,
+			PenpalPolkadotB,
 		],
 		// TODO: uncomment when https://github.com/paritytech/cumulus/pull/2528 is merged
 		// bridge = PolkadotKusamaMockBridge
@@ -417,8 +384,9 @@ decl_test_networks! {
 		relay_chain = Kusama,
 		parachains = vec![
 			AssetHubKusama,
-			PenpalKusama,
+			PenpalKusamaA,
 			BridgeHubKusama,
+			PenpalKusamaB,
 		],
 		// TODO: uncomment when https://github.com/paritytech/cumulus/pull/2528 is merged
 		// bridge = KusamaPolkadotMockBridge
@@ -428,7 +396,7 @@ decl_test_networks! {
 		relay_chain = Westend,
 		parachains = vec![
 			AssetHubWestend,
-			PenpalWestend,
+			PenpalWestendA,
 		],
 		bridge = ()
 	},
@@ -437,6 +405,7 @@ decl_test_networks! {
 		parachains = vec![
 			AssetHubRococo,
 			BridgeHubRococo,
+			PenpalRococoA,
 		],
 		bridge = RococoWococoMockBridge
 	},
@@ -474,6 +443,51 @@ decl_test_bridges! {
 	// }
 }
 
+// Polkadot implementation
+impl_accounts_helpers_for_relay_chain!(Polkadot);
+impl_assert_events_helpers_for_relay_chain!(Polkadot);
+impl_hrmp_channels_helpers_for_relay_chain!(Polkadot);
+
+// Kusama implementation
+impl_accounts_helpers_for_relay_chain!(Kusama);
+impl_assert_events_helpers_for_relay_chain!(Kusama);
+impl_hrmp_channels_helpers_for_relay_chain!(Kusama);
+
+// Westend implementation
+impl_accounts_helpers_for_relay_chain!(Westend);
+impl_assert_events_helpers_for_relay_chain!(Westend);
+
+// Rococo implementation
+impl_accounts_helpers_for_relay_chain!(Rococo);
+impl_assert_events_helpers_for_relay_chain!(Rococo);
+
+// Wococo implementation
+impl_accounts_helpers_for_relay_chain!(Wococo);
+impl_assert_events_helpers_for_relay_chain!(Wococo);
+
+// AssetHubPolkadot implementation
+impl_accounts_helpers_for_parachain!(AssetHubPolkadot);
+impl_assets_helpers_for_parachain!(AssetHubPolkadot, Polkadot);
+impl_assert_events_helpers_for_parachain!(AssetHubPolkadot);
+
+// AssetHubKusama implementation
+impl_accounts_helpers_for_parachain!(AssetHubKusama);
+impl_assets_helpers_for_parachain!(AssetHubKusama, Kusama);
+impl_assert_events_helpers_for_parachain!(AssetHubKusama);
+
+// AssetHubWestend implementation
+impl_accounts_helpers_for_parachain!(AssetHubWestend);
+impl_assets_helpers_for_parachain!(AssetHubWestend, Westend);
+impl_assert_events_helpers_for_parachain!(AssetHubWestend);
+
+// Collectives implementation
+impl_accounts_helpers_for_parachain!(Collectives);
+impl_assert_events_helpers_for_parachain!(Collectives);
+
+// BridgeHubRococo implementation
+impl_accounts_helpers_for_parachain!(BridgeHubRococo);
+impl_assert_events_helpers_for_parachain!(BridgeHubRococo);
+
 decl_test_sender_receiver_accounts_parameter_types! {
 	// Relays
 	Polkadot { sender: ALICE, receiver: BOB },
@@ -495,7 +509,51 @@ decl_test_sender_receiver_accounts_parameter_types! {
 	BridgeHubRococo { sender: ALICE, receiver: BOB },
 	BridgeHubWococo { sender: ALICE, receiver: BOB },
 	// Penpals
-	PenpalPolkadot { sender: ALICE, receiver: BOB },
-	PenpalKusama { sender: ALICE, receiver: BOB },
-	PenpalWestend { sender: ALICE, receiver: BOB }
+	PenpalPolkadotA { sender: ALICE, receiver: BOB },
+	PenpalPolkadotB { sender: ALICE, receiver: BOB },
+	PenpalKusamaA { sender: ALICE, receiver: BOB },
+	PenpalKusamaB { sender: ALICE, receiver: BOB },
+	PenpalWestendA { sender: ALICE, receiver: BOB },
+	PenpalRococoA { sender: ALICE, receiver: BOB }
+}
+
+/// Helper method to build a XCM with a `Transact` instruction and paying for its execution
+pub fn xcm_transact_paid_execution(
+	call: DoubleEncoded<()>,
+	origin_kind: OriginKind,
+	native_asset: MultiAsset,
+	beneficiary: AccountId,
+) -> VersionedXcm<()> {
+	let weight_limit = WeightLimit::Unlimited;
+	let require_weight_at_most = Weight::from_parts(1000000000, 200000);
+	let native_assets: MultiAssets = native_asset.clone().into();
+
+	VersionedXcm::from(Xcm(vec![
+		WithdrawAsset(native_assets),
+		BuyExecution { fees: native_asset, weight_limit },
+		Transact { require_weight_at_most, origin_kind, call },
+		RefundSurplus,
+		DepositAsset {
+			assets: All.into(),
+			beneficiary: MultiLocation {
+				parents: 0,
+				interior: X1(AccountId32 { network: None, id: beneficiary.into() }),
+			},
+		},
+	]))
+}
+
+/// Helper method to build a XCM with a `Transact` instruction without paying for its execution
+pub fn xcm_transact_unpaid_execution(
+	call: DoubleEncoded<()>,
+	origin_kind: OriginKind,
+) -> VersionedXcm<()> {
+	let weight_limit = WeightLimit::Unlimited;
+	let require_weight_at_most = Weight::from_parts(1000000000, 200000);
+	let check_origin = None;
+
+	VersionedXcm::from(Xcm(vec![
+		UnpaidExecution { weight_limit, check_origin },
+		Transact { require_weight_at_most, origin_kind, call },
+	]))
 }
diff --git a/cumulus/xcm/xcm-emulator/Cargo.toml b/cumulus/xcm/xcm-emulator/Cargo.toml
index 3a1402109e2..25d44913bdb 100644
--- a/cumulus/xcm/xcm-emulator/Cargo.toml
+++ b/cumulus/xcm/xcm-emulator/Cargo.toml
@@ -8,9 +8,9 @@ edition = "2021"
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 paste = "1.0.14"
-quote = "1.0.32"
-casey = "0.4.0"
 log = { version = "0.4.19", default-features = false }
+lazy_static = "1.4.0"
+impl-trait-for-tuples = "0.2.2"
 
 # Substrate
 frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -18,9 +18,9 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "mast
 sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" }
-sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
 pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
 pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master" }
 
@@ -36,5 +36,6 @@ parachains-common = { path = "../../parachains/common" }
 
 # Polkadot
 xcm = { git = "https://github.com/paritytech/polkadot", branch = "master" }
+xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master" }
 polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
 polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" }
diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs
index 115a32ebf24..f881cdd1fca 100644
--- a/cumulus/xcm/xcm-emulator/src/lib.rs
+++ b/cumulus/xcm/xcm-emulator/src/lib.rs
@@ -15,37 +15,54 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 pub use codec::{Decode, Encode};
+pub use lazy_static::lazy_static;
 pub use log;
 pub use paste;
-pub use std::{collections::HashMap, error::Error, fmt, thread::LocalKey};
+pub use std::{
+	any::type_name,
+	collections::HashMap,
+	error::Error,
+	fmt,
+	marker::PhantomData,
+	ops::Deref,
+	sync::{Condvar, Mutex},
+	thread::LocalKey,
+};
 
 // Substrate
 pub use frame_support::{
 	assert_ok,
-	traits::{EnqueueMessage, Get, Hooks, ProcessMessage, ProcessMessageError, ServiceQueues},
+	dispatch::EncodeLike,
+	sp_runtime::{AccountId32, DispatchResult},
+	traits::{
+		tokens::currency::Currency, EnqueueMessage, Get, Hooks, OriginTrait, ProcessMessage,
+		ProcessMessageError, ServiceQueues,
+	},
 	weights::{Weight, WeightMeter},
 };
-pub use frame_system::AccountInfo;
+pub use frame_system::{AccountInfo, Config as SystemConfig, Pallet as SystemPallet};
 pub use pallet_balances::AccountData;
 pub use sp_arithmetic::traits::Bounded;
-pub use sp_core::{storage::Storage, Pair, H256};
-pub use sp_io;
+pub use sp_core::{sr25519, storage::Storage, Pair, H256};
+pub use sp_io::TestExternalities;
 pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, fmt::Debug};
 pub use sp_trie::StorageProof;
 
 //Cumulus
 pub use cumulus_pallet_dmp_queue;
-pub use cumulus_pallet_parachain_system;
-pub use cumulus_pallet_xcmp_queue;
+pub use cumulus_pallet_parachain_system::{self, Pallet as ParachainSystemPallet};
+pub use cumulus_pallet_xcmp_queue::{Config as XcmpQueueConfig, Pallet as XcmpQueuePallet};
 pub use cumulus_primitives_core::{
 	self, relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler, ParaId,
 	PersistedValidationData, XcmpMessageHandler,
 };
 pub use cumulus_primitives_parachain_inherent::ParachainInherentData;
 pub use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
-pub use pallet_message_queue;
+pub use pallet_message_queue::{
+	Config as MessageQueueConfig, Event as MessageQueueEvent, Pallet as MessageQueuePallet,
+};
 pub use parachain_info;
-pub use parachains_common::{AccountId, BlockNumber};
+pub use parachains_common::{AccountId, Balance, BlockNumber};
 pub use polkadot_primitives;
 pub use polkadot_runtime_parachains::{
 	dmp,
@@ -53,7 +70,11 @@ pub use polkadot_runtime_parachains::{
 };
 
 // Polkadot
-pub use xcm::v3::prelude::*;
+pub use xcm::{
+	v3::prelude::{AccountId32 as AccountId32Junction, Parachain as ParachainJunction, *},
+	VersionedMultiAssets, VersionedMultiLocation,
+};
+pub use xcm_executor::traits::ConvertLocation;
 
 thread_local! {
 	/// Downward messages, each message is: `(to_para_id, [(relay_block_number, msg)])`
@@ -72,29 +93,60 @@ thread_local! {
 	pub static UPWARD_MESSAGES: RefCell<HashMap<String, VecDeque<(u32, Vec<u8>)>>> = RefCell::new(HashMap::new());
 	/// Bridged messages, each message is: `BridgeMessage`
 	pub static BRIDGED_MESSAGES: RefCell<HashMap<String, VecDeque<BridgeMessage>>> = RefCell::new(HashMap::new());
-	/// Global incremental relay chain block number
-	pub static RELAY_BLOCK_NUMBER: RefCell<HashMap<String, u32>> = RefCell::new(HashMap::new());
 	/// Parachains Ids a the Network
 	pub static PARA_IDS: RefCell<HashMap<String, Vec<u32>>> = RefCell::new(HashMap::new());
 	/// Flag indicating if global variables have been initialized for a certain Network
 	pub static INITIALIZED: RefCell<HashMap<String, bool>> = RefCell::new(HashMap::new());
 }
 
+pub trait CheckAssertion<Origin, Destination, Hops, Args>
+where
+	Origin: Chain + Clone,
+	Destination: Chain + Clone,
+	Origin::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Destination::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Hops: Clone,
+	Args: Clone,
+{
+	fn check_assertion(test: Test<Origin, Destination, Hops, Args>);
+}
+
+#[impl_trait_for_tuples::impl_for_tuples(5)]
+impl<Origin, Destination, Hops, Args> CheckAssertion<Origin, Destination, Hops, Args> for Tuple
+where
+	Origin: Chain + Clone,
+	Destination: Chain + Clone,
+	Origin::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Destination::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Hops: Clone,
+	Args: Clone,
+{
+	fn check_assertion(test: Test<Origin, Destination, Hops, Args>) {
+		for_tuples!( #(
+			Tuple::check_assertion(test.clone());
+		)* );
+	}
+}
+
 pub trait TestExt {
-	fn build_new_ext(storage: Storage) -> sp_io::TestExternalities;
-	fn new_ext() -> sp_io::TestExternalities;
+	fn build_new_ext(storage: Storage) -> TestExternalities;
+	fn new_ext() -> TestExternalities;
+	fn move_ext_out(id: &'static str);
+	fn move_ext_in(id: &'static str);
 	fn reset_ext();
 	fn execute_with<R>(execute: impl FnOnce() -> R) -> R;
 	fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R;
 }
 
 impl TestExt for () {
-	fn build_new_ext(_storage: Storage) -> sp_io::TestExternalities {
-		sp_io::TestExternalities::default()
+	fn build_new_ext(_storage: Storage) -> TestExternalities {
+		TestExternalities::default()
 	}
-	fn new_ext() -> sp_io::TestExternalities {
-		sp_io::TestExternalities::default()
+	fn new_ext() -> TestExternalities {
+		TestExternalities::default()
 	}
+	fn move_ext_out(_id: &'static str) {}
+	fn move_ext_in(_id: &'static str) {}
 	fn reset_ext() {}
 	fn execute_with<R>(execute: impl FnOnce() -> R) -> R {
 		execute()
@@ -105,12 +157,15 @@ impl TestExt for () {
 }
 
 pub trait Network {
+	type Relay: RelayChain;
 	type Bridge: Bridge;
 
+	fn name() -> &'static str;
 	fn init();
+	fn reset();
 	fn para_ids() -> Vec<u32>;
 	fn relay_block_number() -> u32;
-	fn set_relay_block_number(block_number: u32);
+	fn set_relay_block_number(number: u32);
 	fn process_messages();
 	fn has_unprocessed_messages() -> bool;
 	fn process_downward_messages();
@@ -126,15 +181,13 @@ pub trait Network {
 pub trait NetworkComponent {
 	type Network: Network;
 
-	fn network_name() -> &'static str;
-
 	fn send_horizontal_messages<I: Iterator<Item = (ParaId, RelayBlockNumber, Vec<u8>)>>(
 		to_para_id: u32,
 		iter: I,
 	) {
 		HORIZONTAL_MESSAGES.with(|b| {
 			b.borrow_mut()
-				.get_mut(Self::network_name())
+				.get_mut(Self::Network::name())
 				.unwrap()
 				.push_back((to_para_id, iter.collect()))
 		});
@@ -143,7 +196,7 @@ pub trait NetworkComponent {
 	fn send_upward_message(from_para_id: u32, msg: Vec<u8>) {
 		UPWARD_MESSAGES.with(|b| {
 			b.borrow_mut()
-				.get_mut(Self::network_name())
+				.get_mut(Self::Network::name())
 				.unwrap()
 				.push_back((from_para_id, msg))
 		});
@@ -155,7 +208,7 @@ pub trait NetworkComponent {
 	) {
 		DOWNWARD_MESSAGES.with(|b| {
 			b.borrow_mut()
-				.get_mut(Self::network_name())
+				.get_mut(Self::Network::name())
 				.unwrap()
 				.push_back((to_para_id, iter.collect()))
 		});
@@ -163,33 +216,67 @@ pub trait NetworkComponent {
 
 	fn send_bridged_messages(msg: BridgeMessage) {
 		BRIDGED_MESSAGES
-			.with(|b| b.borrow_mut().get_mut(Self::network_name()).unwrap().push_back(msg));
+			.with(|b| b.borrow_mut().get_mut(Self::Network::name()).unwrap().push_back(msg));
 	}
 }
 
-pub trait RelayChain: ProcessMessage {
-	type Runtime;
-	type RuntimeOrigin;
+pub trait Chain: TestExt + NetworkComponent {
+	type Runtime: SystemConfig;
 	type RuntimeCall;
+	type RuntimeOrigin;
 	type RuntimeEvent;
-	type XcmConfig;
-	type SovereignAccountOf;
 	type System;
-	type Balances;
+
+	fn account_id_of(seed: &str) -> AccountId {
+		helpers::get_account_id_from_seed::<sr25519::Public>(seed)
+	}
+
+	fn account_data_of(account: AccountId) -> AccountData<Balance>;
+
+	fn events() -> Vec<<Self as Chain>::RuntimeEvent>;
 }
 
-pub trait Parachain: XcmpMessageHandler + DmpMessageHandler {
-	type Runtime;
-	type RuntimeOrigin;
-	type RuntimeCall;
-	type RuntimeEvent;
-	type XcmpMessageHandler;
-	type DmpMessageHandler;
-	type LocationToAccountId;
-	type System;
-	type Balances;
+pub trait RelayChain: Chain {
+	type MessageProcessor: ProcessMessage;
+	type SovereignAccountOf: ConvertLocation<AccountId>;
+
+	fn child_location_of(id: ParaId) -> MultiLocation {
+		(Ancestor(0), ParachainJunction(id.into())).into()
+	}
+
+	fn sovereign_account_id_of(location: MultiLocation) -> AccountId {
+		Self::SovereignAccountOf::convert_location(&location).unwrap()
+	}
+
+	fn sovereign_account_id_of_child_para(id: ParaId) -> AccountId {
+		Self::sovereign_account_id_of(Self::child_location_of(id))
+	}
+}
+
+pub trait Parachain: Chain {
+	type XcmpMessageHandler: XcmpMessageHandler;
+	type DmpMessageHandler: DmpMessageHandler;
+	type LocationToAccountId: ConvertLocation<AccountId>;
+	type ParachainInfo: Get<ParaId>;
 	type ParachainSystem;
-	type ParachainInfo;
+
+	fn para_id() -> ParaId {
+		Self::ext_wrapper(|| Self::ParachainInfo::get())
+	}
+
+	fn parent_location() -> MultiLocation {
+		(Parent).into()
+	}
+
+	fn sibling_location_of(para_id: ParaId) -> MultiLocation {
+		(Parent, X1(ParachainJunction(para_id.into()))).into()
+	}
+
+	fn sovereign_account_id_of(location: MultiLocation) -> AccountId {
+		Self::LocationToAccountId::convert_location(&location).unwrap()
+	}
+
+	fn prepare_for_xcmp();
 }
 
 pub trait Bridge {
@@ -250,19 +337,6 @@ impl fmt::Display for BridgeMessageDispatchError {
 	}
 }
 
-/// Helper function to generate an account ID from seed.
-pub fn get_account_id_from_seed<TPublic: sp_core::Public>(seed: &str) -> AccountId
-where
-	sp_runtime::MultiSigner:
-		From<<<TPublic as sp_runtime::CryptoType>::Pair as sp_core::Pair>::Public>,
-{
-	use sp_runtime::traits::IdentifyAccount;
-	let pubkey = TPublic::Pair::from_string(&format!("//{}", seed), None)
-		.expect("static values are valid; qed")
-		.public();
-	sp_runtime::MultiSigner::from(pubkey).into_account()
-}
-
 // Relay Chain Implementation
 #[macro_export]
 macro_rules! decl_test_relay_chains {
@@ -272,18 +346,13 @@ macro_rules! decl_test_relay_chains {
 			pub struct $name:ident {
 				genesis = $genesis:expr,
 				on_init = $on_init:expr,
-				runtime = {
-					Runtime: $runtime:path,
-					RuntimeOrigin: $runtime_origin:path,
-					RuntimeCall: $runtime_call:path,
-					RuntimeEvent: $runtime_event:path,
-					MessageQueue: $mq:path,
-					XcmConfig: $xcm_config:path,
+				runtime = $runtime:ident,
+				core = {
+					MessageProcessor: $mp:path,
 					SovereignAccountOf: $sovereign_acc_of:path,
-					System: $system:path,
-					Balances: $balances:path,
+
 				},
-				pallets_extra = {
+				pallets = {
 					$($pallet_name:ident: $pallet_path:path,)*
 				}
 			}
@@ -291,17 +360,31 @@ macro_rules! decl_test_relay_chains {
 		+
 	) => {
 		$(
+			#[derive(Clone)]
 			pub struct $name;
 
+			impl Chain for $name {
+				type Runtime = $runtime::Runtime;
+				type RuntimeCall = $runtime::RuntimeCall;
+				type RuntimeOrigin = $runtime::RuntimeOrigin;
+				type RuntimeEvent = $runtime::RuntimeEvent;
+				type System = $crate::SystemPallet::<Self::Runtime>;
+
+				fn account_data_of(account: AccountId) -> $crate::AccountData<Balance> {
+					Self::ext_wrapper(|| $crate::SystemPallet::<Self::Runtime>::account(account).data.into())
+				}
+
+				fn events() -> Vec<<Self as Chain>::RuntimeEvent> {
+					Self::System::events()
+						.iter()
+						.map(|record| record.event.clone())
+						.collect()
+				}
+			}
+
 			impl RelayChain for $name {
-				type Runtime = $runtime;
-				type RuntimeOrigin = $runtime_origin;
-				type RuntimeCall = $runtime_call;
-				type RuntimeEvent = $runtime_event;
-				type XcmConfig = $xcm_config;
 				type SovereignAccountOf = $sovereign_acc_of;
-				type System = $system;
-				type Balances = $balances;
+				type MessageProcessor = $mp;
 			}
 
 			$crate::paste::paste! {
@@ -318,43 +401,8 @@ macro_rules! decl_test_relay_chains {
 				}
 			}
 
-			impl $crate::ProcessMessage for $name {
-				type Origin = $crate::ParaId;
-
-				fn process_message(
-					msg: &[u8],
-					para: Self::Origin,
-					meter: &mut $crate::WeightMeter,
-					_id: &mut XcmHash
-				) -> Result<bool, $crate::ProcessMessageError> {
-					use $crate::{Weight, AggregateMessageOrigin, UmpQueueId, ServiceQueues, EnqueueMessage};
-					use $mq as message_queue;
-					use $runtime_event as runtime_event;
-
-					Self::ext_wrapper(|| {
-						<$mq as EnqueueMessage<AggregateMessageOrigin>>::enqueue_message(
-							msg.try_into().expect("Message too long"),
-							AggregateMessageOrigin::Ump(UmpQueueId::Para(para.clone()))
-						);
-
-						<$system>::reset_events();
-						<$mq as ServiceQueues>::service_queues(Weight::MAX);
-						let events = <$system>::events();
-						let event = events.last().expect("There must be at least one event");
-
-						match &event.event {
-							runtime_event::MessageQueue(
-								$crate::pallet_message_queue::Event::Processed {origin, ..}) => {
-								assert_eq!(origin, &AggregateMessageOrigin::Ump(UmpQueueId::Para(para)));
-							},
-							event => panic!("Unexpected event: {:#?}", event),
-						}
-						Ok(true)
-					})
-				}
-			}
-
 			$crate::__impl_test_ext_for_relay_chain!($name, $genesis, $on_init, $api_version);
+			$crate::__impl_check_assertion!($name);
 		)+
 	};
 }
@@ -364,34 +412,99 @@ macro_rules! __impl_test_ext_for_relay_chain {
 	// entry point: generate ext name
 	($name:ident, $genesis:expr, $on_init:expr, $api_version:tt) => {
 		$crate::paste::paste! {
-			$crate::__impl_test_ext_for_relay_chain!(@impl $name, $genesis, $on_init, [<ParachainHostV $api_version>], [<EXT_ $name:upper>]);
+			$crate::__impl_test_ext_for_relay_chain!(
+				@impl $name,
+				$genesis,
+				$on_init,
+				[<ParachainHostV $api_version>],
+				[<LOCAL_EXT_ $name:upper>],
+				[<GLOBAL_EXT_ $name:upper>]
+			);
 		}
 	};
 	// impl
-	(@impl $name:ident, $genesis:expr, $on_init:expr, $api_version:ident, $ext_name:ident) => {
+	(@impl $name:ident, $genesis:expr, $on_init:expr, $api_version:ident, $local_ext:ident, $global_ext:ident) => {
 		thread_local! {
-			pub static $ext_name: $crate::RefCell<$crate::sp_io::TestExternalities>
+			pub static $local_ext: $crate::RefCell<$crate::TestExternalities>
 				= $crate::RefCell::new(<$name>::build_new_ext($genesis));
 		}
 
+		$crate::lazy_static! {
+			pub static ref $global_ext: $crate::Mutex<$crate::RefCell<$crate::HashMap<String, $crate::TestExternalities>>>
+				= $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new()));
+		}
+
 		impl TestExt for $name {
-			fn build_new_ext(storage: $crate::Storage) -> $crate::sp_io::TestExternalities {
-				let mut ext = sp_io::TestExternalities::new(storage);
+			fn build_new_ext(storage: $crate::Storage) -> $crate::TestExternalities {
+				use $crate::{NetworkComponent, Network, Chain};
+
+				let mut ext = $crate::TestExternalities::new(storage);
+
 				ext.execute_with(|| {
 					#[allow(clippy::no_effect)]
 					$on_init;
 					sp_tracing::try_init_simple();
-					<Self as RelayChain>::System::set_block_number(1);
+
+					let mut block_number = <Self as Chain>::System::block_number();
+					block_number = std::cmp::max(1, block_number);
+					<Self as Chain>::System::set_block_number(block_number);
 				});
 				ext
 			}
 
-			fn new_ext() -> $crate::sp_io::TestExternalities {
+			fn new_ext() -> $crate::TestExternalities {
 				<$name>::build_new_ext($genesis)
 			}
 
+			fn move_ext_out(id: &'static str) {
+				use $crate::Deref;
+
+				// Take TestExternality from thread_local
+				let local_ext = $local_ext.with(|v| {
+					v.take()
+				});
+
+				// Get TestExternality from lazy_static
+				let global_ext_guard = $global_ext.lock().unwrap();
+
+				// Replace TestExternality in lazy_static by TestExternality from thread_local
+				global_ext_guard.deref().borrow_mut().insert(id.to_string(), local_ext);
+			}
+
+			fn move_ext_in(id: &'static str) {
+				use $crate::Deref;
+
+				let mut global_ext_unlocked = false;
+
+				// Keep the mutex unlocked until TesExternality from lazy_static
+				// has been updated
+				while !global_ext_unlocked {
+					// Get TesExternality from lazy_static
+					let global_ext_result = $global_ext.try_lock();
+
+					if let Ok(global_ext_guard) = global_ext_result {
+						// Unlock the mutex as long as the condition is not met
+						if !global_ext_guard.deref().borrow().contains_key(id) {
+							drop(global_ext_guard);
+						} else {
+							global_ext_unlocked = true;
+						}
+					}
+				}
+
+				// Now that we know that lazy_static TestExt has been updated, we lock its mutex
+				let mut global_ext_guard = $global_ext.lock().unwrap();
+
+				// and set TesExternality from lazy_static into TesExternality for local_thread
+				let global_ext = global_ext_guard.deref();
+
+				$local_ext.with(|v| {
+					v.replace(global_ext.take().remove(id).unwrap());
+				});
+			}
+
 			fn reset_ext() {
-				$ext_name.with(|v| *v.borrow_mut() = <$name>::build_new_ext($genesis));
+				$local_ext.with(|v| *v.borrow_mut() = <$name>::build_new_ext($genesis));
 			}
 
 			fn execute_with<R>(execute: impl FnOnce() -> R) -> R {
@@ -399,17 +512,18 @@ macro_rules! __impl_test_ext_for_relay_chain {
 				// Make sure the Network is initialized
 				<$name as NetworkComponent>::Network::init();
 
-				let r = $ext_name.with(|v| v.borrow_mut().execute_with(execute));
+				// Execute
+				let r = $local_ext.with(|v| v.borrow_mut().execute_with(execute));
 
-				// send messages if needed
-				$ext_name.with(|v| {
+				// Send messages if needed
+				$local_ext.with(|v| {
 					v.borrow_mut().execute_with(|| {
 						use $crate::polkadot_primitives::runtime_api::runtime_decl_for_parachain_host::$api_version;
 
 						//TODO: mark sent count & filter out sent msg
 						for para_id in<$name as NetworkComponent>::Network::para_ids() {
 							// downward messages
-							let downward_messages = <Self as RelayChain>::Runtime::dmq_contents(para_id.into())
+							let downward_messages = <Self as Chain>::Runtime::dmq_contents(para_id.into())
 								.into_iter()
 								.map(|inbound| (inbound.sent_at, inbound.msg));
 							if downward_messages.len() == 0 {
@@ -420,6 +534,14 @@ macro_rules! __impl_test_ext_for_relay_chain {
 							// Note: no need to handle horizontal messages, as the
 							// simulator directly sends them to dest (not relayed).
 						}
+
+						// log events
+						Self::events().iter().for_each(|event| {
+							$crate::log::debug!(target: concat!("events::", stringify!($name)), "{:?}", event);
+						});
+
+						// clean events
+						<Self as Chain>::System::reset_events();
 					})
 				});
 
@@ -429,7 +551,7 @@ macro_rules! __impl_test_ext_for_relay_chain {
 			}
 
 			fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R {
-				$ext_name.with(|v| {
+				$local_ext.with(|v| {
 					v.borrow_mut().execute_with(|| {
 						func()
 					})
@@ -439,56 +561,6 @@ macro_rules! __impl_test_ext_for_relay_chain {
 	};
 }
 
-#[macro_export]
-macro_rules! __impl_relay {
-	($network:ident, $relay_chain:ty) => {
-		impl $crate::NetworkComponent for $relay_chain {
-			type Network = $network;
-
-			fn network_name() -> &'static str {
-				stringify!($network)
-			}
-		}
-
-		impl $relay_chain {
-			pub fn child_location_of(id: $crate::ParaId) -> MultiLocation {
-				(Ancestor(0), Parachain(id.into())).into()
-			}
-
-			pub fn account_id_of(seed: &str) -> $crate::AccountId {
-				$crate::get_account_id_from_seed::<sr25519::Public>(seed)
-			}
-
-			pub fn account_data_of(account: AccountId) -> $crate::AccountData<Balance> {
-				Self::ext_wrapper(|| <Self as RelayChain>::System::account(account).data)
-			}
-
-			pub fn sovereign_account_id_of(location: $crate::MultiLocation) -> $crate::AccountId {
-				<Self as RelayChain>::SovereignAccountOf::convert_location(&location).unwrap()
-			}
-
-			pub fn fund_accounts(accounts: Vec<(AccountId, Balance)>) {
-				Self::ext_wrapper(|| {
-					for account in accounts {
-						let _ = <Self as RelayChain>::Balances::force_set_balance(
-							<Self as RelayChain>::RuntimeOrigin::root(),
-							account.0.into(),
-							account.1.into(),
-						);
-					}
-				});
-			}
-
-			pub fn events() -> Vec<<Self as RelayChain>::RuntimeEvent> {
-				<Self as RelayChain>::System::events()
-					.iter()
-					.map(|record| record.event.clone())
-					.collect()
-			}
-		}
-	};
-}
-
 // Parachain Implementation
 #[macro_export]
 macro_rules! decl_test_parachains {
@@ -497,20 +569,14 @@ macro_rules! decl_test_parachains {
 			pub struct $name:ident {
 				genesis = $genesis:expr,
 				on_init = $on_init:expr,
-				runtime = {
-					Runtime: $runtime:path,
-					RuntimeOrigin: $runtime_origin:path,
-					RuntimeCall: $runtime_call:path,
-					RuntimeEvent: $runtime_event:path,
+				runtime = $runtime:ident,
+				core = {
 					XcmpMessageHandler: $xcmp_message_handler:path,
 					DmpMessageHandler: $dmp_message_handler:path,
 					LocationToAccountId: $location_to_account:path,
-					System: $system:path,
-					Balances: $balances_pallet:path,
-					ParachainSystem: $parachain_system:path,
 					ParachainInfo: $parachain_info:path,
 				},
-				pallets_extra = {
+				pallets = {
 					$($pallet_name:ident: $pallet_path:path,)*
 				}
 			}
@@ -518,20 +584,55 @@ macro_rules! decl_test_parachains {
 		+
 	) => {
 		$(
+			#[derive(Clone)]
 			pub struct $name;
 
+			impl Chain for $name {
+				type Runtime = $runtime::Runtime;
+				type RuntimeCall = $runtime::RuntimeCall;
+				type RuntimeOrigin = $runtime::RuntimeOrigin;
+				type RuntimeEvent = $runtime::RuntimeEvent;
+				type System = $crate::SystemPallet::<Self::Runtime>;
+
+				fn account_data_of(account: AccountId) -> $crate::AccountData<Balance> {
+					Self::ext_wrapper(|| $crate::SystemPallet::<Self::Runtime>::account(account).data.into())
+				}
+
+				fn events() -> Vec<<Self as Chain>::RuntimeEvent> {
+					Self::System::events()
+						.iter()
+						.map(|record| record.event.clone())
+						.collect()
+				}
+			}
+
 			impl Parachain for $name {
-				type Runtime = $runtime;
-				type RuntimeOrigin = $runtime_origin;
-				type RuntimeCall = $runtime_call;
-				type RuntimeEvent = $runtime_event;
 				type XcmpMessageHandler = $xcmp_message_handler;
 				type DmpMessageHandler = $dmp_message_handler;
 				type LocationToAccountId = $location_to_account;
-				type System = $system;
-				type Balances = $balances_pallet;
-				type ParachainSystem = $parachain_system;
+				type ParachainSystem = $crate::ParachainSystemPallet<<Self as Chain>::Runtime>;
 				type ParachainInfo = $parachain_info;
+
+				fn prepare_for_xcmp() {
+					use $crate::{Network, NetworkComponent, Hooks};
+
+					let para_id = Self::para_id();
+
+					<Self as TestExt>::ext_wrapper(|| {
+						let block_number = <Self as Chain>::System::block_number();
+						let mut relay_block_number = <Self as NetworkComponent>::Network::relay_block_number();
+
+						let _ = <Self as Parachain>::ParachainSystem::set_validation_data(
+							<Self as Chain>::RuntimeOrigin::none(),
+							<Self as NetworkComponent>::Network::hrmp_channel_parachain_inherent_data(
+								para_id.into(),
+								relay_block_number,
+							),
+						);
+						// set `AnnouncedHrmpMessagesPerCandidate`
+						<Self as Parachain>::ParachainSystem::on_initialize(block_number);
+					});
+				}
 			}
 
 			$crate::paste::paste! {
@@ -548,79 +649,103 @@ macro_rules! decl_test_parachains {
 				}
 			}
 
-			$crate::__impl_xcm_handlers_for_parachain!($name);
 			$crate::__impl_test_ext_for_parachain!($name, $genesis, $on_init);
+			$crate::__impl_check_assertion!($name);
 		)+
 	};
 }
 
-#[macro_export]
-macro_rules! __impl_xcm_handlers_for_parachain {
-	($name:ident) => {
-		impl $crate::XcmpMessageHandler for $name {
-			fn handle_xcmp_messages<
-				'a,
-				I: Iterator<Item = ($crate::ParaId, $crate::RelayBlockNumber, &'a [u8])>,
-			>(
-				iter: I,
-				max_weight: $crate::Weight,
-			) -> $crate::Weight {
-				use $crate::{TestExt, XcmpMessageHandler};
-
-				$name::ext_wrapper(|| {
-					<Self as Parachain>::XcmpMessageHandler::handle_xcmp_messages(iter, max_weight)
-				})
-			}
-		}
-
-		impl $crate::DmpMessageHandler for $name {
-			fn handle_dmp_messages(
-				iter: impl Iterator<Item = ($crate::RelayBlockNumber, Vec<u8>)>,
-				max_weight: $crate::Weight,
-			) -> $crate::Weight {
-				use $crate::{DmpMessageHandler, TestExt};
-
-				$name::ext_wrapper(|| {
-					<Self as Parachain>::DmpMessageHandler::handle_dmp_messages(iter, max_weight)
-				})
-			}
-		}
-	};
-}
-
 #[macro_export]
 macro_rules! __impl_test_ext_for_parachain {
 	// entry point: generate ext name
 	($name:ident, $genesis:expr, $on_init:expr) => {
 		$crate::paste::paste! {
-			$crate::__impl_test_ext_for_parachain!(@impl $name, $genesis, $on_init, [<EXT_ $name:upper>]);
+			$crate::__impl_test_ext_for_parachain!(@impl $name, $genesis, $on_init, [<LOCAL_EXT_ $name:upper>], [<GLOBAL_EXT_ $name:upper>]);
 		}
 	};
 	// impl
-	(@impl $name:ident, $genesis:expr, $on_init:expr, $ext_name:ident) => {
+	(@impl $name:ident, $genesis:expr, $on_init:expr, $local_ext:ident, $global_ext:ident) => {
 		thread_local! {
-			pub static $ext_name: $crate::RefCell<$crate::sp_io::TestExternalities>
+			pub static $local_ext: $crate::RefCell<$crate::TestExternalities>
 				= $crate::RefCell::new(<$name>::build_new_ext($genesis));
 		}
 
+		$crate::lazy_static! {
+			pub static ref $global_ext: $crate::Mutex<$crate::RefCell<$crate::HashMap<String, $crate::TestExternalities>>>
+				= $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new()));
+		}
+
 		impl TestExt for $name {
-			fn build_new_ext(storage: $crate::Storage) -> $crate::sp_io::TestExternalities {
-				let mut ext = sp_io::TestExternalities::new(storage);
+			fn build_new_ext(storage: $crate::Storage) -> $crate::TestExternalities {
+				use $crate::{NetworkComponent, Network, Chain};
+
+				let mut ext = $crate::TestExternalities::new(storage);
+
 				ext.execute_with(|| {
 					#[allow(clippy::no_effect)]
 					$on_init;
 					sp_tracing::try_init_simple();
-					<Self as Parachain>::System::set_block_number(1);
+
+					let mut block_number = <Self as Chain>::System::block_number();
+					block_number = std::cmp::max(1, block_number);
+					<Self as Chain>::System::set_block_number(block_number);
 				});
 				ext
 			}
 
-			fn new_ext() -> $crate::sp_io::TestExternalities {
+			fn new_ext() -> $crate::TestExternalities {
 				<$name>::build_new_ext($genesis)
 			}
 
+			fn move_ext_out(id: &'static str) {
+				use $crate::Deref;
+
+				// Take TestExternality from thread_local
+				let local_ext = $local_ext.with(|v| {
+					v.take()
+				});
+
+				// Get TestExternality from lazy_static
+				let global_ext_guard = $global_ext.lock().unwrap();
+
+				// Replace TestExternality in lazy_static by TestExternality from thread_local
+				global_ext_guard.deref().borrow_mut().insert(id.to_string(), local_ext);
+			}
+
+			fn move_ext_in(id: &'static str) {
+				use $crate::Deref;
+
+				let mut global_ext_unlocked = false;
+
+				// Keep the mutex unlocked until TesExternality from lazy_static
+				// has been updated
+				while !global_ext_unlocked {
+					// Get TesExternality from lazy_static
+					let global_ext_result = $global_ext.try_lock();
+
+					if let Ok(global_ext_guard) = global_ext_result {
+						// Unlock the mutex as long as the condition is not met
+						if !global_ext_guard.deref().borrow().contains_key(id) {
+							drop(global_ext_guard);
+						} else {
+							global_ext_unlocked = true;
+						}
+					}
+				}
+
+				// Now that we know that lazy_static TestExt has been updated, we lock its mutex
+				let mut global_ext_guard = $global_ext.lock().unwrap();
+
+				// and set TesExternality from lazy_static into TesExternality for local_thread
+				let global_ext = global_ext_guard.deref();
+
+				$local_ext.with(|v| {
+					v.replace(global_ext.take().remove(id).unwrap());
+				});
+			}
+
 			fn reset_ext() {
-				$ext_name.with(|v| *v.borrow_mut() = <$name>::build_new_ext($genesis));
+				$local_ext.with(|v| *v.borrow_mut() = <$name>::build_new_ext($genesis));
 			}
 
 			fn execute_with<R>(execute: impl FnOnce() -> R) -> R {
@@ -629,32 +754,32 @@ macro_rules! __impl_test_ext_for_parachain {
 				// Make sure the Network is initialized
 				<$name as NetworkComponent>::Network::init();
 
-				let mut relay_block_number = <$name as NetworkComponent>::Network::relay_block_number();
-				relay_block_number += 1;
-				<$name as NetworkComponent>::Network::set_relay_block_number(relay_block_number);
-
 				let para_id = <$name>::para_id().into();
 
-				$ext_name.with(|v| {
+				// Initialize block
+				$local_ext.with(|v| {
 					v.borrow_mut().execute_with(|| {
-						// Make sure it has been recorded properly
-						let relay_block_number = <$name as NetworkComponent>::Network::relay_block_number();
+						// Increase block number
+						let mut relay_block_number = <$name as NetworkComponent>::Network::relay_block_number();
+						relay_block_number += 1;
+						<$name as NetworkComponent>::Network::set_relay_block_number(relay_block_number);
+
 						let _ = <Self as Parachain>::ParachainSystem::set_validation_data(
-							<Self as Parachain>::RuntimeOrigin::none(),
+							<Self as Chain>::RuntimeOrigin::none(),
 							<$name as NetworkComponent>::Network::hrmp_channel_parachain_inherent_data(para_id, relay_block_number),
 						);
 					})
 				});
 
+				// Execute
+				let r = $local_ext.with(|v| v.borrow_mut().execute_with(execute));
 
-				let r = $ext_name.with(|v| v.borrow_mut().execute_with(execute));
-
-				// send messages if needed
-				$ext_name.with(|v| {
+				// Finalize block and send messages if needed
+				$local_ext.with(|v| {
 					v.borrow_mut().execute_with(|| {
 						use sp_runtime::traits::Header as HeaderT;
 
-						let block_number = <Self as Parachain>::System::block_number();
+						let block_number = <Self as Chain>::System::block_number();
 						let mock_header = HeaderT::new(
 							0,
 							Default::default(),
@@ -693,6 +818,14 @@ macro_rules! __impl_test_ext_for_parachain {
 
 						// clean messages
 						<Self as Parachain>::ParachainSystem::on_initialize(block_number);
+
+						// log events
+						Self::events().iter().for_each(|event| {
+							$crate::log::debug!(target: concat!("events::", stringify!($name)), "{:?}", event);
+						});
+
+						// clean events
+						<Self as Chain>::System::reset_events();
 					})
 				});
 
@@ -702,7 +835,7 @@ macro_rules! __impl_test_ext_for_parachain {
 			}
 
 			fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R {
-				$ext_name.with(|v| {
+				$local_ext.with(|v| {
 					v.borrow_mut().execute_with(|| {
 						func()
 					})
@@ -712,85 +845,6 @@ macro_rules! __impl_test_ext_for_parachain {
 	};
 }
 
-#[macro_export]
-macro_rules! __impl_parachain {
-	($network:ident, $parachain:ty) => {
-		impl $crate::NetworkComponent for $parachain {
-			type Network = $network;
-
-			fn network_name() -> &'static str {
-				stringify!($network)
-			}
-		}
-
-		impl $parachain {
-			pub fn para_id() -> $crate::ParaId {
-				Self::ext_wrapper(|| <Self as Parachain>::ParachainInfo::get())
-			}
-
-			pub fn parent_location() -> $crate::MultiLocation {
-				(Parent).into()
-			}
-
-			pub fn sibling_location_of(para_id: $crate::ParaId) -> $crate::MultiLocation {
-				(Parent, X1(Parachain(para_id.into()))).into()
-			}
-
-			pub fn account_id_of(seed: &str) -> $crate::AccountId {
-				$crate::get_account_id_from_seed::<sr25519::Public>(seed)
-			}
-
-			pub fn account_data_of(account: AccountId) -> $crate::AccountData<Balance> {
-				Self::ext_wrapper(|| <Self as Parachain>::System::account(account).data)
-			}
-
-			pub fn sovereign_account_id_of(location: $crate::MultiLocation) -> $crate::AccountId {
-				<Self as Parachain>::LocationToAccountId::convert_location(&location).unwrap()
-			}
-
-			pub fn fund_accounts(accounts: Vec<(AccountId, Balance)>) {
-				Self::ext_wrapper(|| {
-					for account in accounts {
-						let _ = <Self as Parachain>::Balances::force_set_balance(
-							<Self as Parachain>::RuntimeOrigin::root(),
-							account.0.into(),
-							account.1.into(),
-						);
-					}
-				});
-			}
-
-			pub fn events() -> Vec<<Self as Parachain>::RuntimeEvent> {
-				<Self as Parachain>::System::events()
-					.iter()
-					.map(|record| record.event.clone())
-					.collect()
-			}
-
-			fn prepare_for_xcmp() {
-				use $crate::{Network, NetworkComponent};
-				let para_id = Self::para_id();
-
-				<Self as TestExt>::ext_wrapper(|| {
-					use $crate::{Get, Hooks};
-
-					let block_number = <Self as Parachain>::System::block_number();
-
-					let _ = <Self as Parachain>::ParachainSystem::set_validation_data(
-						<Self as Parachain>::RuntimeOrigin::none(),
-						<Self as NetworkComponent>::Network::hrmp_channel_parachain_inherent_data(
-							para_id.into(),
-							1,
-						),
-					);
-					// set `AnnouncedHrmpMessagesPerCandidate`
-					<Self as Parachain>::ParachainSystem::on_initialize(block_number);
-				});
-			}
-		}
-	};
-}
-
 // Network Implementation
 #[macro_export]
 macro_rules! decl_test_networks {
@@ -807,37 +861,38 @@ macro_rules! decl_test_networks {
 		$(
 			pub struct $name;
 
-			impl $name {
-				pub fn reset() {
+			impl $crate::Network for $name {
+				type Relay = $relay_chain;
+				type Bridge = $bridge;
+
+				fn name() -> &'static str {
+					$crate::type_name::<Self>()
+				}
+
+				fn reset() {
 					use $crate::{TestExt, VecDeque};
 
-					$crate::INITIALIZED.with(|b| b.borrow_mut().remove(stringify!($name)));
-					$crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name)));
-					$crate::DMP_DONE.with(|b| b.borrow_mut().remove(stringify!($name)));
-					$crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name)));
-					$crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name)));
-					$crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name)));
-					$crate::RELAY_BLOCK_NUMBER.with(|b| b.borrow_mut().remove(stringify!($name)));
+					$crate::INITIALIZED.with(|b| b.borrow_mut().remove(Self::name()));
+					$crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
+					$crate::DMP_DONE.with(|b| b.borrow_mut().remove(Self::name()));
+					$crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
+					$crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
+					$crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
 
 					<$relay_chain>::reset_ext();
 					$( <$parachain>::reset_ext(); )*
 				}
-			}
-
-			impl $crate::Network for $name {
-				type Bridge = $bridge;
 
 				fn init() {
 					// If Network has not been itialized yet, it gets initialized
-					if $crate::INITIALIZED.with(|b| b.borrow_mut().get(stringify!($name)).is_none()) {
-						$crate::INITIALIZED.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), true));
-						$crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new()));
-						$crate::DMP_DONE.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new()));
-						$crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new()));
-						$crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new()));
-						$crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new()));
-						$crate::RELAY_BLOCK_NUMBER.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), 1));
-						$crate::PARA_IDS.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), Self::para_ids()));
+					if $crate::INITIALIZED.with(|b| b.borrow_mut().get(Self::name()).is_none()) {
+						$crate::INITIALIZED.with(|b| b.borrow_mut().insert(Self::name().to_string(), true));
+						$crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
+						$crate::DMP_DONE.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
+						$crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
+						$crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
+						$crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
+						$crate::PARA_IDS.with(|b| b.borrow_mut().insert(Self::name().to_string(), Self::para_ids()));
 
 						$( <$parachain>::prepare_for_xcmp(); )*
 					}
@@ -850,11 +905,15 @@ macro_rules! decl_test_networks {
 				}
 
 				fn relay_block_number() -> u32 {
-					$crate::RELAY_BLOCK_NUMBER.with(|v| *v.clone().borrow().get(stringify!($name)).unwrap())
+					Self::Relay::ext_wrapper(|| {
+						<Self::Relay as Chain>::System::block_number()
+					})
 				}
 
-				fn set_relay_block_number(block_number: u32) {
-					$crate::RELAY_BLOCK_NUMBER.with(|v| v.borrow_mut().insert(stringify!($name).to_string(), block_number));
+				fn set_relay_block_number(number: u32) {
+					Self::Relay::ext_wrapper(|| {
+						<Self::Relay as Chain>::System::set_block_number(number);
+					})
 				}
 
 				fn process_messages() {
@@ -867,10 +926,10 @@ macro_rules! decl_test_networks {
 				}
 
 				fn has_unprocessed_messages() -> bool {
-					$crate::DOWNWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty())
-					|| $crate::HORIZONTAL_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty())
-					|| $crate::UPWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty())
-					|| $crate::BRIDGED_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty())
+					$crate::DOWNWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
+					|| $crate::HORIZONTAL_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
+					|| $crate::UPWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
+					|| $crate::BRIDGED_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
 				}
 
 				fn process_downward_messages() {
@@ -878,11 +937,11 @@ macro_rules! decl_test_networks {
 					use polkadot_parachain::primitives::RelayChainBlockNumber;
 
 					while let Some((to_para_id, messages))
-						= $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) {
+						= $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
 						$(
 							let para_id: u32 = <$parachain>::para_id().into();
 
-							if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().contains(&to_para_id)) && para_id == to_para_id {
+							if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().contains(&to_para_id)) && para_id == to_para_id {
 								let mut msg_dedup: Vec<(RelayChainBlockNumber, Vec<u8>)> = Vec::new();
 								for m in &messages {
 									msg_dedup.push((m.0, m.1.clone()));
@@ -890,13 +949,15 @@ macro_rules! decl_test_networks {
 								msg_dedup.dedup();
 
 								let msgs = msg_dedup.clone().into_iter().filter(|m| {
-									!$crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap_or(&mut $crate::VecDeque::new()).contains(&(to_para_id, m.0, m.1.clone())))
+									!$crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap_or(&mut $crate::VecDeque::new()).contains(&(to_para_id, m.0, m.1.clone())))
 								}).collect::<Vec<(RelayChainBlockNumber, Vec<u8>)>>();
 								if msgs.len() != 0 {
-									<$parachain>::handle_dmp_messages(msgs.clone().into_iter(), $crate::Weight::max_value());
+									<$parachain>::ext_wrapper(|| {
+										<$parachain as Parachain>::DmpMessageHandler::handle_dmp_messages(msgs.clone().into_iter(), $crate::Weight::max_value());
+									});
 									$crate::log::debug!(target: concat!("dmp::", stringify!($name)) , "DMP messages processed {:?} to para_id {:?}", msgs.clone(), &to_para_id);
 									for m in msgs {
-										$crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().push_back((to_para_id, m.0, m.1)));
+										$crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().push_back((to_para_id, m.0, m.1)));
 									}
 								}
 							}
@@ -908,13 +969,15 @@ macro_rules! decl_test_networks {
 					use $crate::{XcmpMessageHandler, Bounded};
 
 					while let Some((to_para_id, messages))
-						= $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) {
+						= $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
 						let iter = messages.iter().map(|(p, b, m)| (*p, *b, &m[..])).collect::<Vec<_>>().into_iter();
 						$(
 							let para_id: u32 = <$parachain>::para_id().into();
 
-							if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().contains(&to_para_id)) && para_id == to_para_id {
-								<$parachain>::handle_xcmp_messages(iter.clone(), $crate::Weight::max_value());
+							if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().contains(&to_para_id)) && para_id == to_para_id {
+								<$parachain>::ext_wrapper(|| {
+									<$parachain as Parachain>::XcmpMessageHandler::handle_xcmp_messages(iter.clone(), $crate::Weight::max_value());
+								});
 								$crate::log::debug!(target: concat!("hrmp::", stringify!($name)) , "HRMP messages processed {:?} to para_id {:?}", &messages, &to_para_id);
 							}
 						)*
@@ -924,14 +987,16 @@ macro_rules! decl_test_networks {
 				fn process_upward_messages() {
 					use $crate::{Bounded, ProcessMessage, WeightMeter};
 					use sp_core::Encode;
-					while let Some((from_para_id, msg)) = $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) {
+					while let Some((from_para_id, msg)) = $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
 						let mut weight_meter = WeightMeter::max_limit();
-						let _ =  <$relay_chain>::process_message(
-							&msg[..],
-							from_para_id.into(),
-							&mut weight_meter,
-							&mut msg.using_encoded(sp_core::blake2_256),
-						);
+						<$relay_chain>::ext_wrapper(|| {
+							let _ =  <$relay_chain as RelayChain>::MessageProcessor::process_message(
+								&msg[..],
+								from_para_id.into(),
+								&mut weight_meter,
+								&mut msg.using_encoded(sp_core::blake2_256),
+							);
+						});
 						$crate::log::debug!(target: concat!("ump::", stringify!($name)) , "Upward message processed {:?} from para_id {:?}", &msg, &from_para_id);
 					}
 				}
@@ -941,7 +1006,7 @@ macro_rules! decl_test_networks {
 					// Make sure both, including the target `Network` are initialized
 					<Self::Bridge as Bridge>::init();
 
-					while let Some(msg) = $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) {
+					while let Some(msg) = $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
 						let dispatch_result = <<Self::Bridge as $crate::Bridge>::Target as TestExt>::ext_wrapper(|| {
 							<<Self::Bridge as Bridge>::Handler as BridgeMessageHandler>::dispatch_target_inbound_message(msg.clone())
 						});
@@ -969,7 +1034,7 @@ macro_rules! decl_test_networks {
 
 					// egress channel
 					let e_index = sproof.hrmp_egress_channel_index.get_or_insert_with(Vec::new);
-					for recipient_para_id in $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().clone()) {
+					for recipient_para_id in $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().clone()) {
 						let recipient_para_id = $crate::ParaId::from(recipient_para_id);
 						if let Err(idx) = e_index.binary_search(&recipient_para_id) {
 							e_index.insert(idx, recipient_para_id);
@@ -1007,10 +1072,14 @@ macro_rules! decl_test_networks {
 				}
 			}
 
-			$crate::__impl_relay!($name, $relay_chain);
+			impl $crate::NetworkComponent for $relay_chain {
+				type Network = $name;
+			}
 
 			$(
-				$crate::__impl_parachain!($name, $parachain);
+				impl $crate::NetworkComponent for $parachain {
+					type Network = $name;
+				}
 			)*
 		)+
 	};
@@ -1039,7 +1108,7 @@ macro_rules! decl_test_bridges {
 
 				fn init() {
 					use $crate::{NetworkComponent, Network};
-					// Make sure source and target `Network` has been initialized
+					// Make sure source and target `Network` have been initialized
 					<$source as NetworkComponent>::Network::init();
 					<$target as NetworkComponent>::Network::init();
 				}
@@ -1048,42 +1117,109 @@ macro_rules! decl_test_bridges {
 	};
 }
 
+#[macro_export]
+macro_rules! __impl_check_assertion {
+	($chain:ident) => {
+		impl<Origin, Destination, Hops, Args>
+			$crate::CheckAssertion<Origin, Destination, Hops, Args> for $chain
+		where
+			Origin: Chain + Clone,
+			Destination: Chain + Clone,
+			Origin::RuntimeOrigin: $crate::OriginTrait<AccountId = $crate::AccountId32> + Clone,
+			Destination::RuntimeOrigin:
+				$crate::OriginTrait<AccountId = $crate::AccountId32> + Clone,
+			Hops: Clone,
+			Args: Clone,
+		{
+			fn check_assertion(test: $crate::Test<Origin, Destination, Hops, Args>) {
+				let chain_name = std::any::type_name::<$chain>();
+
+				<$chain>::execute_with(|| {
+					if let Some(dispatchable) = test.hops_dispatchable.get(chain_name) {
+						$crate::assert_ok!(dispatchable(test.clone()));
+					}
+					if let Some(assertion) = test.hops_assertion.get(chain_name) {
+						assertion(test);
+					}
+				});
+			}
+		}
+	};
+}
+
 #[macro_export]
 macro_rules! assert_expected_events {
 	( $chain:ident, vec![$( $event_pat:pat => { $($attr:ident : $condition:expr, )* }, )*] ) => {
 		let mut message: Vec<String> = Vec::new();
+		let mut events = <$chain>::events();
+
 		$(
+			let mut event_received = false;
 			let mut meet_conditions = true;
+			let mut index_match = 0;
 			let mut event_message: Vec<String> = Vec::new();
 
-			let event_received = <$chain>::events().iter().any(|event| {
-				$crate::log::debug!(target: concat!("events::", stringify!($chain)), "{:?}", event);
-
+			for (index, event) in events.iter().enumerate() {
+				// Have to reset the variable to override a previous partial match
+				meet_conditions = true;
 				match event {
 					$event_pat => {
+						event_received = true;
+						let mut conditions_message: Vec<String> = Vec::new();
+
 						$(
-							if !$condition {
-								event_message.push(format!(" - The attribute {:?} = {:?} did not met the condition {:?}\n", stringify!($attr), $attr, stringify!($condition)));
-								meet_conditions &= $condition
+							// We only want to record condition error messages in case it did not happened before
+							// Only the first partial match is recorded
+							if !$condition && event_message.is_empty() {
+								conditions_message.push(
+									format!(
+										" - The attribute {:?} = {:?} did not met the condition {:?}\n",
+										stringify!($attr),
+										$attr,
+										stringify!($condition)
+									)
+								);
 							}
+							meet_conditions &= $condition;
 						)*
-						true
+
+						// Set the index where we found a perfect match
+						if event_received && meet_conditions {
+							index_match = index;
+							break;
+						} else {
+							event_message.extend(conditions_message);
+						}
 					},
-					_ => false
+					_ => {}
 				}
-			});
+			}
 
 			if event_received && !meet_conditions  {
-				message.push(format!("\n\nEvent \x1b[31m{}\x1b[0m was received but some of its attributes did not meet the conditions:\n{}", stringify!($event_pat), event_message.concat()));
+				message.push(
+					format!(
+						"\n\n{}::\x1b[31m{}\x1b[0m was received but some of its attributes did not meet the conditions:\n{}",
+						stringify!($chain),
+						stringify!($event_pat),
+						event_message.concat()
+					)
+				);
 			} else if !event_received {
-				message.push(format!("\n\nEvent \x1b[31m{}\x1b[0m was never received", stringify!($event_pat)));
+				message.push(format!("\n\n{}::\x1b[31m{}\x1b[0m was never received", stringify!($chain), stringify!($event_pat)));
+			} else {
+				// If we find a perfect match we remove the event to avoid being potentially assessed multiple times
+				events.remove(index_match);
 			}
 		)*
+
 		if !message.is_empty() {
+			// Log events as they will not be logged after the panic
+			<$chain>::events().iter().for_each(|event| {
+				$crate::log::debug!(target: concat!("events::", stringify!($chain)), "{:?}", event);
+			});
 			panic!("{}", message.concat())
 		}
 	}
-
 }
 
 #[macro_export]
@@ -1107,8 +1243,140 @@ macro_rules! decl_test_sender_receiver_accounts_parameter_types {
 	};
 }
 
+pub struct DefaultMessageProcessor<T>(PhantomData<T>);
+impl<T> ProcessMessage for DefaultMessageProcessor<T>
+where
+	T: Chain + RelayChain,
+	T::Runtime: MessageQueueConfig,
+	<<T::Runtime as MessageQueueConfig>::MessageProcessor as ProcessMessage>::Origin:
+		PartialEq<AggregateMessageOrigin>,
+	MessageQueuePallet<T::Runtime>: EnqueueMessage<AggregateMessageOrigin> + ServiceQueues,
+{
+	type Origin = ParaId;
+
+	fn process_message(
+		msg: &[u8],
+		para: Self::Origin,
+		_meter: &mut WeightMeter,
+		_id: &mut XcmHash,
+	) -> Result<bool, ProcessMessageError> {
+		MessageQueuePallet::<T::Runtime>::enqueue_message(
+			msg.try_into().expect("Message too long"),
+			AggregateMessageOrigin::Ump(UmpQueueId::Para(para)),
+		);
+		MessageQueuePallet::<T::Runtime>::service_queues(Weight::MAX);
+
+		Ok(true)
+	}
+}
+
+/// Struct that keeps account's id and balance
+#[derive(Clone)]
+pub struct TestAccount {
+	pub account_id: AccountId,
+	pub balance: Balance,
+}
+
+/// Default `Args` provided by xcm-emulator to be stored in a `Test` instance
+#[derive(Clone)]
+pub struct TestArgs {
+	pub dest: MultiLocation,
+	pub beneficiary: MultiLocation,
+	pub amount: Balance,
+	pub assets: MultiAssets,
+	pub asset_id: Option<u32>,
+	pub fee_asset_item: u32,
+	pub weight_limit: WeightLimit,
+}
+
+/// Auxiliar struct to help creating a new `Test` instance
+pub struct TestContext<T> {
+	pub sender: AccountId,
+	pub receiver: AccountId,
+	pub args: T,
+}
+
+/// Struct that help with tests where either dispatchables or assertions need
+/// to be reused. The struct keeps the test's arguments of your choice in the generic `Args`.
+/// These arguments can be easily reused and shared between the assertions functions
+/// and dispatchables functions, which are also stored in `Test`.
+/// `Origin` corresponds to the chain where the XCM interaction starts with an initial execution.
+/// `Destination` corresponds to the last chain where an effect of the intial execution is expected happen.
+/// `Hops` refer all the ordered intermediary chains an initial XCM execution can provoke some effect.
+#[derive(Clone)]
+pub struct Test<Origin, Destination, Hops = (), Args = TestArgs>
+where
+	Origin: Chain + Clone,
+	Destination: Chain + Clone,
+	Origin::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Destination::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Hops: Clone,
+{
+	pub sender: TestAccount,
+	pub receiver: TestAccount,
+	pub signed_origin: Origin::RuntimeOrigin,
+	pub root_origin: Origin::RuntimeOrigin,
+	pub hops_assertion: HashMap<String, fn(Self)>,
+	pub hops_dispatchable: HashMap<String, fn(Self) -> DispatchResult>,
+	pub args: Args,
+	_marker: PhantomData<(Destination, Hops)>,
+}
+
+/// `Test` implementation
+impl<Origin, Destination, Hops, Args> Test<Origin, Destination, Hops, Args>
+where
+	Args: Clone,
+	Origin: Chain + Clone + CheckAssertion<Origin, Destination, Hops, Args>,
+	Destination: Chain + Clone + CheckAssertion<Origin, Destination, Hops, Args>,
+	Origin::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Destination::RuntimeOrigin: OriginTrait<AccountId = AccountId32> + Clone,
+	Hops: Clone + CheckAssertion<Origin, Destination, Hops, Args>,
+{
+	/// Creates a new `Test` instance
+	pub fn new(test_args: TestContext<Args>) -> Self {
+		Test {
+			sender: TestAccount {
+				account_id: test_args.sender.clone(),
+				balance: Origin::account_data_of(test_args.sender.clone()).free,
+			},
+			receiver: TestAccount {
+				account_id: test_args.receiver.clone(),
+				balance: Destination::account_data_of(test_args.receiver.clone()).free,
+			},
+			signed_origin: <Origin as Chain>::RuntimeOrigin::signed(test_args.sender),
+			root_origin: <Origin as Chain>::RuntimeOrigin::root(),
+			hops_assertion: Default::default(),
+			hops_dispatchable: Default::default(),
+			args: test_args.args,
+			_marker: Default::default(),
+		}
+	}
+	/// Stores an assertion in a particular Chain
+	pub fn set_assertion<Hop>(&mut self, assertion: fn(Self)) {
+		let chain_name = std::any::type_name::<Hop>();
+		self.hops_assertion.insert(chain_name.to_string(), assertion);
+	}
+	/// Stores an assertion in a particular Chain
+	pub fn set_dispatchable<Hop>(&mut self, dispatchable: fn(Self) -> DispatchResult) {
+		let chain_name = std::any::type_name::<Hop>();
+		self.hops_dispatchable.insert(chain_name.to_string(), dispatchable);
+	}
+	/// Executes all dispatchables and assertions in order from `Origin` to `Destination`
+	pub fn assert(&mut self) {
+		Origin::check_assertion(self.clone());
+		Hops::check_assertion(self.clone());
+		Destination::check_assertion(self.clone());
+		Self::update_balances(self);
+	}
+	/// Updates sender and receiver balances
+	fn update_balances(&mut self) {
+		self.sender.balance = Origin::account_data_of(self.sender.account_id.clone()).free;
+		self.receiver.balance = Destination::account_data_of(self.receiver.account_id.clone()).free;
+	}
+}
+
 pub mod helpers {
-	use super::Weight;
+	use super::*;
 
 	pub fn within_threshold(threshold: u64, expected_value: u64, current_value: u64) -> bool {
 		let margin = (current_value * threshold) / 100;
@@ -1130,4 +1398,17 @@ pub mod helpers {
 
 		ref_time_within && proof_size_within
 	}
+
+	/// Helper function to generate an account ID from seed.
+	pub fn get_account_id_from_seed<TPublic: sp_core::Public>(seed: &str) -> AccountId
+	where
+		sp_runtime::MultiSigner:
+			From<<<TPublic as sp_runtime::CryptoType>::Pair as sp_core::Pair>::Public>,
+	{
+		use sp_runtime::traits::IdentifyAccount;
+		let pubkey = TPublic::Pair::from_string(&format!("//{}", seed), None)
+			.expect("static values are valid; qed")
+			.public();
+		sp_runtime::MultiSigner::from(pubkey).into_account()
+	}
 }
-- 
GitLab