diff --git a/Cargo.lock b/Cargo.lock
index 6de8fcfb1afd123ec4d210e6c02a9a9a4bc6d725..05386203452c21a6ea23d013d01e7ebc26227eca 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -824,6 +824,7 @@ dependencies = [
  "sp-runtime",
  "staging-xcm",
  "staging-xcm-executor",
+ "xcm-runtime-apis",
 ]
 
 [[package]]
@@ -2507,9 +2508,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
 
 [[package]]
 name = "cc"
-version = "1.1.6"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
+checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
 dependencies = [
  "jobserver",
  "libc",
@@ -2751,12 +2752,12 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.10"
+version = "4.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f6b81fb3c84f5563d509c59b5a48d935f689e993afa90fe39047f05adef9142"
+checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
 dependencies = [
  "clap_builder",
- "clap_derive 4.5.8",
+ "clap_derive 4.5.11",
 ]
 
 [[package]]
@@ -2770,9 +2771,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.10"
+version = "4.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca6706fd5224857d9ac5eb9355f6683563cc0541c7cd9d014043b57cbec78ac"
+checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
 dependencies = [
  "anstream",
  "anstyle",
@@ -2787,7 +2788,7 @@ version = "4.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
 ]
 
 [[package]]
@@ -2805,9 +2806,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.8"
+version = "4.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
+checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
 dependencies = [
  "heck 0.5.0",
  "proc-macro2 1.0.82",
@@ -3613,7 +3614,7 @@ dependencies = [
  "anes",
  "cast",
  "ciborium",
- "clap 4.5.10",
+ "clap 4.5.11",
  "criterion-plot",
  "futures",
  "is-terminal",
@@ -3747,7 +3748,7 @@ dependencies = [
 name = "cumulus-client-cli"
 version = "0.7.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "parity-scale-codec",
  "sc-chain-spec",
  "sc-cli",
@@ -4210,7 +4211,7 @@ name = "cumulus-pov-validator"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "clap 4.5.10",
+ "clap 4.5.11",
  "parity-scale-codec",
  "polkadot-node-primitives",
  "polkadot-parachain-primitives",
@@ -4537,7 +4538,7 @@ name = "cumulus-test-service"
 version = "0.1.0"
 dependencies = [
  "async-trait",
- "clap 4.5.10",
+ "clap 4.5.11",
  "criterion",
  "cumulus-client-cli",
  "cumulus-client-collator",
@@ -5802,7 +5803,7 @@ dependencies = [
  "Inflector",
  "array-bytes",
  "chrono",
- "clap 4.5.10",
+ "clap 4.5.11",
  "comfy-table",
  "frame-benchmarking",
  "frame-support",
@@ -5894,7 +5895,7 @@ dependencies = [
 name = "frame-election-solution-type-fuzzer"
 version = "2.0.0-alpha.5"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "frame-election-provider-solution-type",
  "frame-election-provider-support",
  "frame-support",
@@ -5966,7 +5967,7 @@ dependencies = [
 name = "frame-omni-bencher"
 version = "0.1.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "cumulus-primitives-proof-size-hostfunction",
  "env_logger 0.11.3",
  "frame-benchmarking-cli",
@@ -7363,9 +7364,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
 
 [[package]]
 name = "jobserver"
-version = "0.1.31"
+version = "0.1.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
 dependencies = [
  "libc",
 ]
@@ -8728,7 +8729,7 @@ dependencies = [
 name = "minimal-template-node"
 version = "0.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "docify",
  "futures",
  "futures-timer",
@@ -9221,7 +9222,7 @@ name = "node-bench"
 version = "0.9.0-dev"
 dependencies = [
  "array-bytes",
- "clap 4.5.10",
+ "clap 4.5.11",
  "derive_more",
  "fs_extra",
  "futures",
@@ -9300,7 +9301,7 @@ dependencies = [
 name = "node-runtime-generate-bags"
 version = "3.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "generate-bags",
  "kitchensink-runtime",
 ]
@@ -9309,7 +9310,7 @@ dependencies = [
 name = "node-template-release"
 version = "3.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "flate2",
  "fs_extra",
  "glob",
@@ -9611,9 +9612,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
 name = "openssl"
-version = "0.10.66"
+version = "0.10.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
 dependencies = [
  "bitflags 2.6.0",
  "cfg-if",
@@ -9652,9 +9653,9 @@ dependencies = [
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.103"
+version = "0.9.102"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
+checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
 dependencies = [
  "cc",
  "libc",
@@ -12031,7 +12032,7 @@ dependencies = [
 name = "parachain-template-node"
 version = "0.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "color-print",
  "cumulus-client-cli",
  "cumulus-client-collator",
@@ -12991,7 +12992,7 @@ name = "polkadot-cli"
 version = "7.0.0"
 dependencies = [
  "cfg-if",
- "clap 4.5.10",
+ "clap 4.5.11",
  "frame-benchmarking-cli",
  "futures",
  "log",
@@ -13851,7 +13852,7 @@ dependencies = [
  "async-trait",
  "bridge-hub-rococo-runtime",
  "bridge-hub-westend-runtime",
- "clap 4.5.10",
+ "clap 4.5.11",
  "collectives-westend-runtime",
  "color-print",
  "contracts-rococo-runtime",
@@ -14835,7 +14836,7 @@ dependencies = [
  "async-trait",
  "bincode",
  "bitvec",
- "clap 4.5.10",
+ "clap 4.5.11",
  "clap-num",
  "color-eyre",
  "colored",
@@ -14933,7 +14934,7 @@ version = "1.0.0"
 dependencies = [
  "assert_matches",
  "async-trait",
- "clap 4.5.10",
+ "clap 4.5.11",
  "color-eyre",
  "futures",
  "futures-timer",
@@ -15075,7 +15076,7 @@ dependencies = [
 name = "polkadot-voter-bags"
 version = "7.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "generate-bags",
  "sp-io",
  "westend-runtime",
@@ -16230,7 +16231,7 @@ dependencies = [
 name = "remote-ext-tests-bags-list"
 version = "1.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "frame-system",
  "log",
  "pallet-bags-list-remote-tests",
@@ -17080,7 +17081,7 @@ name = "sc-chain-spec"
 version = "28.0.0"
 dependencies = [
  "array-bytes",
- "clap 4.5.10",
+ "clap 4.5.11",
  "docify",
  "log",
  "memmap2 0.9.3",
@@ -17123,7 +17124,7 @@ version = "0.36.0"
 dependencies = [
  "array-bytes",
  "chrono",
- "clap 4.5.10",
+ "clap 4.5.11",
  "fdlimit",
  "futures",
  "futures-timer",
@@ -18307,7 +18308,7 @@ dependencies = [
 name = "sc-storage-monitor"
 version = "0.16.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "fs4",
  "log",
  "sp-core",
@@ -18681,18 +18682,18 @@ dependencies = [
 
 [[package]]
 name = "secp256k1"
-version = "0.28.0"
+version = "0.28.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5"
+checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10"
 dependencies = [
  "secp256k1-sys",
 ]
 
 [[package]]
 name = "secp256k1-sys"
-version = "0.9.0"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7"
+checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb"
 dependencies = [
  "cc",
 ]
@@ -19191,9 +19192,9 @@ dependencies = [
 
 [[package]]
 name = "slotmap"
-version = "1.0.7"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
 dependencies = [
  "version_check",
 ]
@@ -19729,7 +19730,7 @@ dependencies = [
 name = "solochain-template-node"
 version = "0.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "frame-benchmarking-cli",
  "frame-metadata-hash-extension",
  "frame-system",
@@ -20405,7 +20406,7 @@ dependencies = [
 name = "sp-npos-elections-fuzzer"
 version = "2.0.0-alpha.5"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "honggfuzz",
  "rand",
  "sp-npos-elections",
@@ -20918,7 +20919,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 name = "staging-chain-spec-builder"
 version = "1.6.1"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "log",
  "sc-chain-spec",
  "serde_json",
@@ -20931,7 +20932,7 @@ version = "3.0.0-dev"
 dependencies = [
  "array-bytes",
  "assert_cmd",
- "clap 4.5.10",
+ "clap 4.5.11",
  "clap_complete",
  "criterion",
  "futures",
@@ -20966,7 +20967,7 @@ dependencies = [
 name = "staging-node-inspect"
 version = "0.12.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "parity-scale-codec",
  "sc-cli",
  "sc-client-api",
@@ -21239,7 +21240,7 @@ dependencies = [
 name = "subkey"
 version = "9.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "sc-cli",
 ]
 
@@ -21865,7 +21866,7 @@ dependencies = [
 name = "test-parachain-adder-collator"
 version = "1.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "futures",
  "futures-timer",
  "log",
@@ -21912,7 +21913,7 @@ dependencies = [
 name = "test-parachain-undying-collator"
 version = "1.0.0"
 dependencies = [
- "clap 4.5.10",
+ "clap 4.5.11",
  "futures",
  "futures-timer",
  "log",
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs
index 25e1cffad543c0aad6d955315721b63c6d509ca1..76179c6d82c6e904b122a42bdc76eec86aa74a09 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs
@@ -69,3 +69,11 @@ pub fn non_fee_asset(assets: &Assets, fee_idx: usize) -> Option<(Location, u128)
 	};
 	Some((asset.id.0, asset_amount))
 }
+
+pub fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 {
+	let latest_assets: Assets = assets.try_into().unwrap();
+	let Fungible(amount) = latest_assets.inner()[0].fun else {
+		unreachable!("asset is non-fungible");
+	};
+	amount
+}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml
index b4579da94cbf6fc6fbb5f5d0e47fdfabdb5134da..f66a5f1d5fe7e0dc0aaee8325a9717e25e9f1068 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml
@@ -28,6 +28,7 @@ pallet-utility = { workspace = true }
 xcm = { workspace = true }
 pallet-xcm = { workspace = true }
 xcm-executor = { workspace = true }
+xcm-runtime-apis = { workspace = true, default-features = true }
 polkadot-runtime-common = { workspace = true, default-features = true }
 rococo-runtime-constants = { workspace = true, default-features = true }
 
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs
index eca35831705493c1ab7961b62f75ed26cde38c6c..6309c05841078bca03969da2642588f344f5430d 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs
@@ -40,7 +40,9 @@ mod imports {
 			assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test,
 			TestArgs, TestContext, TestExt,
 		},
-		xcm_helpers::{non_fee_asset, xcm_transact_paid_execution},
+		xcm_helpers::{
+			get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution,
+		},
 		ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3,
 	};
 	pub use parachains_common::Balance;
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs
index 8fffec23d73a58efc51a433ae2eb66c4028aca95..88fa379c4072b97e867f3eccb68bb23b0eeedff2 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs
@@ -21,3 +21,4 @@ mod set_xcm_versions;
 mod swap;
 mod teleport;
 mod treasury;
+mod xcm_fee_estimation;
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs
new file mode 100644
index 0000000000000000000000000000000000000000..aa0e183ecddaf1a07c1f91d04253138b2625df46
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs
@@ -0,0 +1,286 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests for XCM fee estimation in the runtime.
+
+use crate::imports::*;
+use frame_support::{
+	dispatch::RawOrigin,
+	sp_runtime::{traits::Dispatchable, DispatchResult},
+};
+use xcm_runtime_apis::{
+	dry_run::runtime_decl_for_dry_run_api::DryRunApiV1,
+	fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1,
+};
+
+fn sender_assertions(test: ParaToParaThroughAHTest) {
+	type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
+	PenpalA::assert_xcm_pallet_attempted_complete(None);
+
+	assert_expected_events!(
+		PenpalA,
+		vec![
+			RuntimeEvent::ForeignAssets(
+				pallet_assets::Event::Burned { asset_id, owner, balance }
+			) => {
+				asset_id: *asset_id == Location::new(1, []),
+				owner: *owner == test.sender.account_id,
+				balance: *balance == test.args.amount,
+			},
+		]
+	);
+}
+
+fn hop_assertions(test: ParaToParaThroughAHTest) {
+	type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
+	AssetHubRococo::assert_xcmp_queue_success(None);
+
+	assert_expected_events!(
+		AssetHubRococo,
+		vec![
+			RuntimeEvent::Balances(
+				pallet_balances::Event::Burned { amount, .. }
+			) => {
+				amount: *amount == test.args.amount,
+			},
+		]
+	);
+}
+
+fn receiver_assertions(test: ParaToParaThroughAHTest) {
+	type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
+	PenpalB::assert_xcmp_queue_success(None);
+
+	assert_expected_events!(
+		PenpalB,
+		vec![
+			RuntimeEvent::ForeignAssets(
+				pallet_assets::Event::Issued { asset_id, owner, .. }
+			) => {
+				asset_id: *asset_id == Location::new(1, []),
+				owner: *owner == test.receiver.account_id,
+			},
+		]
+	);
+}
+
+fn transfer_assets_para_to_para_through_ah_dispatchable(
+	test: ParaToParaThroughAHTest,
+) -> DispatchResult {
+	let call = transfer_assets_para_to_para_through_ah_call(test.clone());
+	match call.dispatch(test.signed_origin) {
+		Ok(_) => Ok(()),
+		Err(error_with_post_info) => Err(error_with_post_info.error),
+	}
+}
+
+fn transfer_assets_para_to_para_through_ah_call(
+	test: ParaToParaThroughAHTest,
+) -> <PenpalA as Chain>::RuntimeCall {
+	type RuntimeCall = <PenpalA as Chain>::RuntimeCall;
+
+	let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubRococo::para_id());
+	let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
+		assets: Wild(AllCounted(test.args.assets.len() as u32)),
+		beneficiary: test.args.beneficiary,
+	}]);
+	RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets_using_type_and_then {
+		dest: bx!(test.args.dest.into()),
+		assets: bx!(test.args.assets.clone().into()),
+		assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
+		remote_fees_id: bx!(VersionedAssetId::V4(AssetId(Location::new(1, [])))),
+		fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())),
+		custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)),
+		weight_limit: test.args.weight_limit,
+	})
+}
+
+/// We are able to dry-run and estimate the fees for a multi-hop XCM journey.
+/// Scenario: Alice on PenpalA has some DOTs and wants to send them to PenpalB.
+/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`.
+#[test]
+fn multi_hop_works() {
+	let destination = PenpalA::sibling_location_of(PenpalB::para_id());
+	let sender = PenpalASender::get();
+	let amount_to_send = 1_000_000_000_000;
+	let asset_owner = PenpalAssetOwner::get();
+	let assets: Assets = (Parent, amount_to_send).into();
+	let relay_native_asset_location = Location::parent();
+	let sender_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id());
+	let sov_of_sender_on_ah = AssetHubRococo::sovereign_account_id_of(sender_as_seen_by_ah.clone());
+
+	// fund Parachain's sender account
+	PenpalA::mint_foreign_asset(
+		<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
+		relay_native_asset_location.clone(),
+		sender.clone(),
+		amount_to_send * 2,
+	);
+
+	// fund the Parachain Origin's SA on AssetHub with the native tokens held in reserve.
+	AssetHubRococo::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]);
+
+	// Init values for Parachain Destination
+	let beneficiary_id = PenpalBReceiver::get();
+
+	let test_args = TestContext {
+		sender: PenpalASender::get(),     // Bob in PenpalB.
+		receiver: PenpalBReceiver::get(), // Alice.
+		args: TestArgs::new_para(
+			destination,
+			beneficiary_id.clone(),
+			amount_to_send,
+			assets,
+			None,
+			0,
+		),
+	};
+	let mut test = ParaToParaThroughAHTest::new(test_args);
+
+	// We get them from the PenpalA closure.
+	let mut delivery_fees_amount = 0;
+	let mut remote_message = VersionedXcm::V4(Xcm(Vec::new()));
+	<PenpalA as TestExt>::execute_with(|| {
+		type Runtime = <PenpalA as Chain>::Runtime;
+		type OriginCaller = <PenpalA as Chain>::OriginCaller;
+
+		let call = transfer_assets_para_to_para_through_ah_call(test.clone());
+		let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
+		let result = Runtime::dry_run_call(origin, call).unwrap();
+		// We filter the result to get only the messages we are interested in.
+		let (destination_to_query, messages_to_query) = &result
+			.forwarded_xcms
+			.iter()
+			.find(|(destination, _)| {
+				*destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)]))
+			})
+			.unwrap();
+		assert_eq!(messages_to_query.len(), 1);
+		remote_message = messages_to_query[0].clone();
+		let delivery_fees =
+			Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone())
+				.unwrap();
+		delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees);
+	});
+
+	// These are set in the AssetHub closure.
+	let mut intermediate_execution_fees = 0;
+	let mut intermediate_delivery_fees_amount = 0;
+	let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new()));
+	<AssetHubRococo as TestExt>::execute_with(|| {
+		type Runtime = <AssetHubRococo as Chain>::Runtime;
+		type RuntimeCall = <AssetHubRococo as Chain>::RuntimeCall;
+
+		// First we get the execution fees.
+		let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap();
+		intermediate_execution_fees = Runtime::query_weight_to_asset_fee(
+			weight,
+			VersionedAssetId::V4(Location::new(1, []).into()),
+		)
+		.unwrap();
+
+		// We have to do this to turn `VersionedXcm<()>` into `VersionedXcm<RuntimeCall>`.
+		let xcm_program =
+			VersionedXcm::V4(Xcm::<RuntimeCall>::from(remote_message.clone().try_into().unwrap()));
+
+		// Now we get the delivery fees to the final destination.
+		let result =
+			Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap();
+		let (destination_to_query, messages_to_query) = &result
+			.forwarded_xcms
+			.iter()
+			.find(|(destination, _)| {
+				*destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)]))
+			})
+			.unwrap();
+		// There's actually two messages here.
+		// One created when the message we sent from PenpalA arrived and was executed.
+		// The second one when we dry-run the xcm.
+		// We could've gotten the message from the queue without having to dry-run, but
+		// offchain applications would have to dry-run, so we do it here as well.
+		intermediate_remote_message = messages_to_query[0].clone();
+		let delivery_fees = Runtime::query_delivery_fees(
+			destination_to_query.clone(),
+			intermediate_remote_message.clone(),
+		)
+		.unwrap();
+		intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees);
+	});
+
+	// Get the final execution fees in the destination.
+	let mut final_execution_fees = 0;
+	<PenpalB as TestExt>::execute_with(|| {
+		type Runtime = <PenpalA as Chain>::Runtime;
+
+		let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap();
+		final_execution_fees =
+			Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into()))
+				.unwrap();
+	});
+
+	// Dry-running is done.
+	PenpalA::reset_ext();
+	AssetHubRococo::reset_ext();
+	PenpalB::reset_ext();
+
+	// Fund accounts again.
+	PenpalA::mint_foreign_asset(
+		<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner),
+		relay_native_asset_location.clone(),
+		sender.clone(),
+		amount_to_send * 2,
+	);
+	AssetHubRococo::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]);
+
+	// Actually run the extrinsic.
+	let sender_assets_before = PenpalA::execute_with(|| {
+		type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
+		<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
+	});
+	let receiver_assets_before = PenpalB::execute_with(|| {
+		type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
+		<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &beneficiary_id)
+	});
+
+	test.set_assertion::<PenpalA>(sender_assertions);
+	test.set_assertion::<AssetHubRococo>(hop_assertions);
+	test.set_assertion::<PenpalB>(receiver_assertions);
+	test.set_dispatchable::<PenpalA>(transfer_assets_para_to_para_through_ah_dispatchable);
+	test.assert();
+
+	let sender_assets_after = PenpalA::execute_with(|| {
+		type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
+		<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
+	});
+	let receiver_assets_after = PenpalB::execute_with(|| {
+		type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
+		<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &beneficiary_id)
+	});
+
+	// We know the exact fees on every hop.
+	assert_eq!(
+		sender_assets_after,
+		sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly
+		                                                              * from the sender's
+		                                                              * account. */
+	);
+	assert_eq!(
+		receiver_assets_after,
+		receiver_assets_before + amount_to_send -
+			intermediate_execution_fees -
+			intermediate_delivery_fees_amount -
+			final_execution_fees
+	);
+}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
index 9401621c5ba371b516cbda874d9a729f6f7d1808..060c3fb392544b5135eabdbb4f962b82f1f704a7 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
@@ -40,7 +40,9 @@ mod imports {
 			assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test,
 			TestArgs, TestContext, TestExt,
 		},
-		xcm_helpers::{non_fee_asset, xcm_transact_paid_execution},
+		xcm_helpers::{
+			get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution,
+		},
 		ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3,
 	};
 	pub use parachains_common::{AccountId, Balance};
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs
index c01aa7825336348f5077c89a73c61e7b128a6200..037d6604ea4d5e665928b5629dce3a52abb30a8a 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs
@@ -17,89 +17,95 @@
 
 use crate::imports::*;
 
-use frame_system::RawOrigin;
+use frame_support::{
+	dispatch::RawOrigin,
+	sp_runtime::{traits::Dispatchable, DispatchResult},
+};
 use xcm_runtime_apis::{
 	dry_run::runtime_decl_for_dry_run_api::DryRunApiV1,
 	fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1,
 };
 
-/// We are able to dry-run and estimate the fees for a teleport between relay and system para.
-/// Scenario: Alice on Westend relay chain wants to teleport WND to Asset Hub.
-/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`.
-#[test]
-fn teleport_relay_system_para_works() {
-	let destination: Location = Parachain(1000).into(); // Asset Hub.
-	let beneficiary_id = AssetHubWestendReceiver::get();
-	let beneficiary: Location = AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here.
-		.into(); // Beneficiary in Asset Hub.
-	let teleport_amount = 1_000_000_000_000; // One WND (12 decimals).
-	let assets: Assets = vec![(Here, teleport_amount).into()].into();
-
-	// We get them from the Westend closure.
-	let mut delivery_fees_amount = 0;
-	let mut remote_message = VersionedXcm::V4(Xcm(Vec::new()));
-	<Westend as TestExt>::new_ext().execute_with(|| {
-		type Runtime = <Westend as Chain>::Runtime;
-		type RuntimeCall = <Westend as Chain>::RuntimeCall;
-		type OriginCaller = <Westend as Chain>::OriginCaller;
-
-		let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets {
-			dest: Box::new(VersionedLocation::V4(destination.clone())),
-			beneficiary: Box::new(VersionedLocation::V4(beneficiary)),
-			assets: Box::new(VersionedAssets::V4(assets)),
-			fee_asset_item: 0,
-			weight_limit: Unlimited,
-		});
-		let origin = OriginCaller::system(RawOrigin::Signed(WestendSender::get()));
-		let result = Runtime::dry_run_call(origin, call).unwrap();
-		assert_eq!(result.forwarded_xcms.len(), 1);
-		let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0];
-		assert_eq!(messages_to_query.len(), 1);
-		remote_message = messages_to_query[0].clone();
-		let delivery_fees =
-			Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone())
-				.unwrap();
-		delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees);
-	});
-
-	// This is set in the AssetHubWestend closure.
-	let mut remote_execution_fees = 0;
-	<AssetHubWestend as TestExt>::execute_with(|| {
-		type Runtime = <AssetHubWestend as Chain>::Runtime;
-
-		let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap();
-		remote_execution_fees =
-			Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into()))
-				.unwrap();
-	});
-
-	let test_args = TestContext {
-		sender: WestendSender::get(),             // Alice.
-		receiver: AssetHubWestendReceiver::get(), // Bob in Asset Hub.
-		args: TestArgs::new_relay(destination, beneficiary_id, teleport_amount),
-	};
-	let mut test = RelayToSystemParaTest::new(test_args);
+fn sender_assertions(test: ParaToParaThroughAHTest) {
+	type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
+	PenpalA::assert_xcm_pallet_attempted_complete(None);
+
+	assert_expected_events!(
+		PenpalA,
+		vec![
+			RuntimeEvent::ForeignAssets(
+				pallet_assets::Event::Burned { asset_id, owner, balance }
+			) => {
+				asset_id: *asset_id == Location::new(1, []),
+				owner: *owner == test.sender.account_id,
+				balance: *balance == test.args.amount,
+			},
+		]
+	);
+}
 
-	let sender_balance_before = test.sender.balance;
-	let receiver_balance_before = test.receiver.balance;
-	assert_eq!(sender_balance_before, 1_000_000_000_000_000_000);
-	assert_eq!(receiver_balance_before, 4_096_000_000_000);
+fn hop_assertions(test: ParaToParaThroughAHTest) {
+	type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+	AssetHubWestend::assert_xcmp_queue_success(None);
+
+	assert_expected_events!(
+		AssetHubWestend,
+		vec![
+			RuntimeEvent::Balances(
+				pallet_balances::Event::Burned { amount, .. }
+			) => {
+				amount: *amount == test.args.amount,
+			},
+		]
+	);
+}
 
-	test.set_dispatchable::<Westend>(transfer_assets);
-	test.assert();
+fn receiver_assertions(test: ParaToParaThroughAHTest) {
+	type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
+	PenpalB::assert_xcmp_queue_success(None);
+
+	assert_expected_events!(
+		PenpalB,
+		vec![
+			RuntimeEvent::ForeignAssets(
+				pallet_assets::Event::Issued { asset_id, owner, .. }
+			) => {
+				asset_id: *asset_id == Location::new(1, []),
+				owner: *owner == test.receiver.account_id,
+			},
+		]
+	);
+}
 
-	let sender_balance_after = test.sender.balance;
-	let receiver_balance_after = test.receiver.balance;
+fn transfer_assets_para_to_para_through_ah_dispatchable(
+	test: ParaToParaThroughAHTest,
+) -> DispatchResult {
+	let call = transfer_assets_para_to_para_through_ah_call(test.clone());
+	match call.dispatch(test.signed_origin) {
+		Ok(_) => Ok(()),
+		Err(error_with_post_info) => Err(error_with_post_info.error),
+	}
+}
 
-	// We now know the exact fees.
-	assert_eq!(
-		sender_balance_after,
-		sender_balance_before - delivery_fees_amount - teleport_amount
-	);
-	assert_eq!(
-		receiver_balance_after,
-		receiver_balance_before + teleport_amount - remote_execution_fees
-	);
+fn transfer_assets_para_to_para_through_ah_call(
+	test: ParaToParaThroughAHTest,
+) -> <PenpalA as Chain>::RuntimeCall {
+	type RuntimeCall = <PenpalA as Chain>::RuntimeCall;
+
+	let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubWestend::para_id());
+	let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
+		assets: Wild(AllCounted(test.args.assets.len() as u32)),
+		beneficiary: test.args.beneficiary,
+	}]);
+	RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets_using_type_and_then {
+		dest: bx!(test.args.dest.into()),
+		assets: bx!(test.args.assets.clone().into()),
+		assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
+		remote_fees_id: bx!(VersionedAssetId::V4(AssetId(Location::new(1, [])))),
+		fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())),
+		custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)),
+		weight_limit: test.args.weight_limit,
+	})
 }
 
 /// We are able to dry-run and estimate the fees for a multi-hop XCM journey.
@@ -109,12 +115,13 @@ fn teleport_relay_system_para_works() {
 fn multi_hop_works() {
 	let destination = PenpalA::sibling_location_of(PenpalB::para_id());
 	let sender = PenpalASender::get();
-	let amount_to_send = 1_000_000_000_000; // One WND (12 decimals).
+	let amount_to_send = 1_000_000_000_000;
 	let asset_owner = PenpalAssetOwner::get();
 	let assets: Assets = (Parent, amount_to_send).into();
-	let relay_native_asset_location = RelayLocation::get();
-	let sender_as_seen_by_relay = Westend::child_location_of(PenpalA::para_id());
-	let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay.clone());
+	let relay_native_asset_location = Location::parent();
+	let sender_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id());
+	let sov_of_sender_on_ah =
+		AssetHubWestend::sovereign_account_id_of(sender_as_seen_by_ah.clone());
 
 	// fund Parachain's sender account
 	PenpalA::mint_foreign_asset(
@@ -124,36 +131,44 @@ fn multi_hop_works() {
 		amount_to_send * 2,
 	);
 
-	// fund the Parachain Origin's SA on Relay Chain with the native tokens held in reserve
-	Westend::fund_accounts(vec![(sov_of_sender_on_relay.clone().into(), amount_to_send * 2)]);
+	// fund the Parachain Origin's SA on AssetHub with the native tokens held in reserve.
+	AssetHubWestend::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]);
 
 	// Init values for Parachain Destination
 	let beneficiary_id = PenpalBReceiver::get();
-	let beneficiary: Location = AccountId32 {
-		id: beneficiary_id.clone().into(),
-		network: None, // Test doesn't allow specifying a network here.
-	}
-	.into();
+
+	let test_args = TestContext {
+		sender: PenpalASender::get(),     // Bob in PenpalB.
+		receiver: PenpalBReceiver::get(), // Alice.
+		args: TestArgs::new_para(
+			destination,
+			beneficiary_id.clone(),
+			amount_to_send,
+			assets,
+			None,
+			0,
+		),
+	};
+	let mut test = ParaToParaThroughAHTest::new(test_args);
 
 	// We get them from the PenpalA closure.
 	let mut delivery_fees_amount = 0;
 	let mut remote_message = VersionedXcm::V4(Xcm(Vec::new()));
 	<PenpalA as TestExt>::execute_with(|| {
 		type Runtime = <PenpalA as Chain>::Runtime;
-		type RuntimeCall = <PenpalA as Chain>::RuntimeCall;
 		type OriginCaller = <PenpalA as Chain>::OriginCaller;
 
-		let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets {
-			dest: Box::new(VersionedLocation::V4(destination.clone())),
-			beneficiary: Box::new(VersionedLocation::V4(beneficiary)),
-			assets: Box::new(VersionedAssets::V4(assets.clone())),
-			fee_asset_item: 0,
-			weight_limit: Unlimited,
-		});
-		let origin = OriginCaller::system(RawOrigin::Signed(PenpalASender::get()));
+		let call = transfer_assets_para_to_para_through_ah_call(test.clone());
+		let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
 		let result = Runtime::dry_run_call(origin, call).unwrap();
-		assert_eq!(result.forwarded_xcms.len(), 1);
-		let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0];
+		// We filter the result to get only the messages we are interested in.
+		let (destination_to_query, messages_to_query) = &result
+			.forwarded_xcms
+			.iter()
+			.find(|(destination, _)| {
+				*destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)]))
+			})
+			.unwrap();
 		assert_eq!(messages_to_query.len(), 1);
 		remote_message = messages_to_query[0].clone();
 		let delivery_fees =
@@ -162,18 +177,21 @@ fn multi_hop_works() {
 		delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees);
 	});
 
-	// This is set in the Westend closure.
+	// These are set in the AssetHub closure.
 	let mut intermediate_execution_fees = 0;
 	let mut intermediate_delivery_fees_amount = 0;
 	let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new()));
-	<Westend as TestExt>::execute_with(|| {
-		type Runtime = <Westend as Chain>::Runtime;
-		type RuntimeCall = <Westend as Chain>::RuntimeCall;
+	<AssetHubWestend as TestExt>::execute_with(|| {
+		type Runtime = <AssetHubWestend as Chain>::Runtime;
+		type RuntimeCall = <AssetHubWestend as Chain>::RuntimeCall;
 
 		// First we get the execution fees.
 		let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap();
-		intermediate_execution_fees =
-			Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Here.into())).unwrap();
+		intermediate_execution_fees = Runtime::query_weight_to_asset_fee(
+			weight,
+			VersionedAssetId::V4(Location::new(1, []).into()),
+		)
+		.unwrap();
 
 		// We have to do this to turn `VersionedXcm<()>` into `VersionedXcm<RuntimeCall>`.
 		let xcm_program =
@@ -181,8 +199,14 @@ fn multi_hop_works() {
 
 		// Now we get the delivery fees to the final destination.
 		let result =
-			Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program).unwrap();
-		let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0];
+			Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap();
+		let (destination_to_query, messages_to_query) = &result
+			.forwarded_xcms
+			.iter()
+			.find(|(destination, _)| {
+				*destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)]))
+			})
+			.unwrap();
 		// There's actually two messages here.
 		// One created when the message we sent from PenpalA arrived and was executed.
 		// The second one when we dry-run the xcm.
@@ -200,7 +224,7 @@ fn multi_hop_works() {
 	// Get the final execution fees in the destination.
 	let mut final_execution_fees = 0;
 	<PenpalB as TestExt>::execute_with(|| {
-		type Runtime = <PenpalB as Chain>::Runtime;
+		type Runtime = <PenpalA as Chain>::Runtime;
 
 		let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap();
 		final_execution_fees =
@@ -210,7 +234,7 @@ fn multi_hop_works() {
 
 	// Dry-running is done.
 	PenpalA::reset_ext();
-	Westend::reset_ext();
+	AssetHubWestend::reset_ext();
 	PenpalB::reset_ext();
 
 	// Fund accounts again.
@@ -220,23 +244,9 @@ fn multi_hop_works() {
 		sender.clone(),
 		amount_to_send * 2,
 	);
-	Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]);
+	AssetHubWestend::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]);
 
 	// Actually run the extrinsic.
-	let test_args = TestContext {
-		sender: PenpalASender::get(),     // Alice.
-		receiver: PenpalBReceiver::get(), // Bob in PenpalB.
-		args: TestArgs::new_para(
-			destination,
-			beneficiary_id.clone(),
-			amount_to_send,
-			assets,
-			None,
-			0,
-		),
-	};
-	let mut test = ParaToParaThroughRelayTest::new(test_args);
-
 	let sender_assets_before = PenpalA::execute_with(|| {
 		type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
 		<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
@@ -246,7 +256,10 @@ fn multi_hop_works() {
 		<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &beneficiary_id)
 	});
 
-	test.set_dispatchable::<PenpalA>(transfer_assets_para_to_para);
+	test.set_assertion::<PenpalA>(sender_assertions);
+	test.set_assertion::<AssetHubWestend>(hop_assertions);
+	test.set_assertion::<PenpalB>(receiver_assertions);
+	test.set_dispatchable::<PenpalA>(transfer_assets_para_to_para_through_ah_dispatchable);
 	test.assert();
 
 	let sender_assets_after = PenpalA::execute_with(|| {
@@ -273,33 +286,3 @@ fn multi_hop_works() {
 			final_execution_fees
 	);
 }
-
-fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 {
-	let latest_assets: Assets = assets.try_into().unwrap();
-	let Fungible(amount) = latest_assets.inner()[0].fun else {
-		unreachable!("asset is fungible");
-	};
-	amount
-}
-
-fn transfer_assets(test: RelayToSystemParaTest) -> DispatchResult {
-	<Westend as WestendPallet>::XcmPallet::transfer_assets(
-		test.signed_origin,
-		bx!(test.args.dest.into()),
-		bx!(test.args.beneficiary.into()),
-		bx!(test.args.assets.into()),
-		test.args.fee_asset_item,
-		test.args.weight_limit,
-	)
-}
-
-fn transfer_assets_para_to_para(test: ParaToParaThroughRelayTest) -> DispatchResult {
-	<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets(
-		test.signed_origin,
-		bx!(test.args.dest.into()),
-		bx!(test.args.beneficiary.into()),
-		bx!(test.args.assets.into()),
-		test.args.fee_asset_item,
-		test.args.weight_limit,
-	)
-}