emulated_integration_tests_common/
macros.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16pub use paste;
17
18// Substrate
19pub use frame_support::{pallet_prelude::Weight, weights::WeightToFee};
20pub use pallet_assets;
21pub use pallet_balances;
22pub use pallet_message_queue;
23pub use pallet_xcm;
24
25// Polkadot
26pub use xcm::{
27	prelude::{
28		AccountId32, All, Asset, AssetId, BuyExecution, DepositAsset, ExpectTransactStatus,
29		Fungible, Here, Location, MaybeErrorCode, OriginKind, RefundSurplus, Transact, Unlimited,
30		VersionedAssets, VersionedXcm, WeightLimit, WithdrawAsset, Xcm,
31	},
32	v3::Location as V3Location,
33};
34
35// Cumulus
36pub use asset_test_utils;
37pub use cumulus_pallet_xcmp_queue;
38pub use parachains_common::AccountId;
39pub use xcm_emulator::Chain;
40
41#[macro_export]
42macro_rules! test_parachain_is_trusted_teleporter {
43	( $sender_para:ty, $sender_xcm_config:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr) ) => {
44		$crate::macros::paste::paste! {
45			// init Origin variables
46			let sender = [<$sender_para Sender>]::get();
47			let mut para_sender_balance_before =
48				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
49			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
50			let fee_asset_item = 0;
51			let weight_limit = $crate::macros::WeightLimit::Unlimited;
52
53			$(
54				{
55					// init Destination variables
56					let receiver = [<$receiver_para Receiver>]::get();
57					let para_receiver_balance_before =
58						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
59					let para_destination =
60						<$sender_para>::sibling_location_of(<$receiver_para>::para_id());
61					let beneficiary: Location =
62						$crate::macros::AccountId32 { network: None, id: receiver.clone().into() }.into();
63
64					// Send XCM message from Origin Parachain
65					// We are only testing the limited teleport version, which should be ok since success will
66					// depend only on a proper `XcmConfig` at destination.
67					<$sender_para>::execute_with(|| {
68						assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::limited_teleport_assets(
69							origin.clone(),
70							bx!(para_destination.clone().into()),
71							bx!(beneficiary.clone().into()),
72							bx!($assets.clone().into()),
73							fee_asset_item,
74							weight_limit.clone(),
75						));
76
77						type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
78
79						assert_expected_events!(
80							$sender_para,
81							vec![
82								RuntimeEvent::PolkadotXcm(
83									$crate::macros::pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
84								) => {},
85								RuntimeEvent::XcmpQueue(
86									$crate::macros::cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
87								) => {},
88								RuntimeEvent::Balances(
89									$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
90								) => {},
91							]
92						);
93					});
94
95					// Receive XCM message in Destination Parachain
96					<$receiver_para>::execute_with(|| {
97						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
98
99						assert_expected_events!(
100							$receiver_para,
101							vec![
102								RuntimeEvent::Balances(
103									$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
104								) => {},
105								RuntimeEvent::MessageQueue(
106									$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
107								) => {},
108							]
109						);
110					});
111
112					// Check if balances are updated accordingly in Origin and Destination Parachains
113					let para_sender_balance_after =
114						<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
115					let para_receiver_balance_after =
116						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
117					let delivery_fees = <$sender_para>::execute_with(|| {
118						$crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::<
119							<$sender_xcm_config as xcm_executor::Config>::XcmSender,
120						>($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination)
121					});
122
123					assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after);
124					assert!(para_receiver_balance_after > para_receiver_balance_before);
125
126					// Update sender balance
127					para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
128				}
129			)+
130		}
131	};
132}
133
134#[macro_export]
135macro_rules! test_relay_is_trusted_teleporter {
136	( $sender_relay:ty, $sender_xcm_config:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr) ) => {
137		$crate::macros::paste::paste! {
138			// init Origin variables
139			let sender = [<$sender_relay Sender>]::get();
140			let mut relay_sender_balance_before =
141				<$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
142			let origin = <$sender_relay as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
143			let fee_asset_item = 0;
144			let weight_limit = $crate::macros::WeightLimit::Unlimited;
145
146			$(
147				{
148					// init Destination variables
149					let receiver = [<$receiver_para Receiver>]::get();
150					let para_receiver_balance_before =
151						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
152					let para_destination =
153						<$sender_relay>::child_location_of(<$receiver_para>::para_id());
154					let beneficiary: Location =
155						$crate::macros::AccountId32 { network: None, id: receiver.clone().into() }.into();
156
157					// Send XCM message from Relay
158					<$sender_relay>::execute_with(|| {
159						assert_ok!(<$sender_relay as [<$sender_relay Pallet>]>::XcmPallet::limited_teleport_assets(
160							origin.clone(),
161							bx!(para_destination.clone().into()),
162							bx!(beneficiary.clone().into()),
163							bx!($assets.clone().into()),
164							fee_asset_item,
165							weight_limit.clone(),
166						));
167
168						type RuntimeEvent = <$sender_relay as $crate::macros::Chain>::RuntimeEvent;
169
170						assert_expected_events!(
171							$sender_relay,
172							vec![
173								RuntimeEvent::XcmPallet(
174									$crate::macros::pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
175								) => {},
176								RuntimeEvent::Balances(
177									$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
178								) => {},
179								RuntimeEvent::XcmPallet(
180									$crate::macros::pallet_xcm::Event::Sent { .. }
181								) => {},
182							]
183						);
184					});
185
186					// Receive XCM message in Destination Parachain
187					<$receiver_para>::execute_with(|| {
188						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
189
190						assert_expected_events!(
191							$receiver_para,
192							vec![
193								RuntimeEvent::Balances(
194									$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
195								) => {},
196								RuntimeEvent::MessageQueue(
197									$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
198								) => {},
199							]
200						);
201					});
202
203					// Check if balances are updated accordingly in Origin and Parachain
204					let relay_sender_balance_after =
205						<$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
206					let para_receiver_balance_after =
207						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
208					let delivery_fees = <$sender_relay>::execute_with(|| {
209						$crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::<
210							<$sender_xcm_config as xcm_executor::Config>::XcmSender,
211						>($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination)
212					});
213
214					assert_eq!(relay_sender_balance_before - $amount - delivery_fees, relay_sender_balance_after);
215					assert!(para_receiver_balance_after > para_receiver_balance_before);
216
217					// Update sender balance
218					relay_sender_balance_before = <$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
219				}
220			)+
221		}
222	};
223}
224
225#[macro_export]
226macro_rules! test_parachain_is_trusted_teleporter_for_relay {
227	( $sender_para:ty, $sender_xcm_config:ty, $receiver_relay:ty, $amount:expr ) => {
228		$crate::macros::paste::paste! {
229			// init Origin variables
230			let sender = [<$sender_para Sender>]::get();
231			let para_sender_balance_before =
232				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
233			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
234			let assets: Assets = (Parent, $amount).into();
235			let fee_asset_item = 0;
236			let weight_limit = $crate::macros::WeightLimit::Unlimited;
237
238			// init Destination variables
239			let receiver = [<$receiver_relay Receiver>]::get();
240			let relay_receiver_balance_before =
241				<$receiver_relay as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
242			let relay_destination: Location = Parent.into();
243			let beneficiary: Location =
244				$crate::macros::AccountId32 { network: None, id: receiver.clone().into() }.into();
245
246			// Send XCM message from Parachain
247			<$sender_para>::execute_with(|| {
248				assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::limited_teleport_assets(
249					origin.clone(),
250					bx!(relay_destination.clone().into()),
251					bx!(beneficiary.clone().into()),
252					bx!(assets.clone().into()),
253					fee_asset_item,
254					weight_limit.clone(),
255				));
256
257				type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
258
259				assert_expected_events!(
260					$sender_para,
261					vec![
262						RuntimeEvent::PolkadotXcm(
263							$crate::macros::pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
264						) => {},
265						RuntimeEvent::Balances(
266							$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
267						) => {},
268						RuntimeEvent::PolkadotXcm(
269							$crate::macros::pallet_xcm::Event::Sent { .. }
270						) => {},
271					]
272				);
273			});
274
275			// Receive XCM message in Destination Parachain
276			<$receiver_relay>::execute_with(|| {
277				type RuntimeEvent = <$receiver_relay as $crate::macros::Chain>::RuntimeEvent;
278
279				assert_expected_events!(
280					$receiver_relay,
281					vec![
282						RuntimeEvent::Balances(
283							$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
284						) => {},
285						RuntimeEvent::MessageQueue(
286							$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
287						) => {},
288					]
289				);
290			});
291
292			// Check if balances are updated accordingly in Origin and Relay Chain
293			let para_sender_balance_after =
294				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
295			let relay_receiver_balance_after =
296				<$receiver_relay as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
297			let delivery_fees = <$sender_para>::execute_with(|| {
298				$crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::<
299					<$sender_xcm_config as xcm_executor::Config>::XcmSender,
300				>(assets, fee_asset_item, weight_limit.clone(), beneficiary, relay_destination)
301			});
302
303			assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after);
304			assert!(relay_receiver_balance_after > relay_receiver_balance_before);
305		}
306	};
307}
308
309#[macro_export]
310macro_rules! test_chain_can_claim_assets {
311	( $sender_para:ty, $runtime_call:ty, $network_id:expr, $assets:expr, $amount:expr ) => {
312		$crate::macros::paste::paste! {
313			let sender = [<$sender_para Sender>]::get();
314			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
315			// Receiver is the same as sender
316			let beneficiary: Location =
317				$crate::macros::AccountId32 { network: Some($network_id), id: sender.clone().into() }.into();
318			let versioned_assets: $crate::macros::VersionedAssets = $assets.clone().into();
319
320			<$sender_para>::execute_with(|| {
321				// Assets are trapped for whatever reason.
322				// The possible reasons for this might differ from runtime to runtime, so here we just drop them directly.
323				<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::drop_assets(
324					&beneficiary,
325					$assets.clone().into(),
326					&XcmContext { origin: None, message_id: [0u8; 32], topic: None },
327				);
328
329				type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
330				assert_expected_events!(
331					$sender_para,
332					vec![
333						RuntimeEvent::PolkadotXcm(
334							$crate::macros::pallet_xcm::Event::AssetsTrapped { origin: beneficiary, assets: versioned_assets, .. }
335						) => {},
336					]
337				);
338
339				let balance_before = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&sender);
340
341				// Different origin or different assets won't work.
342				let other_origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed([<$sender_para Receiver>]::get());
343				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
344					other_origin,
345					bx!(versioned_assets.clone().into()),
346					bx!(beneficiary.clone().into()),
347				).is_err());
348				let other_versioned_assets: $crate::macros::VersionedAssets = Assets::new().into();
349				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
350					origin.clone(),
351					bx!(other_versioned_assets.into()),
352					bx!(beneficiary.clone().into()),
353				).is_err());
354
355				// Assets will be claimed to `beneficiary`, which is the same as `sender`.
356				assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
357					origin.clone(),
358					bx!(versioned_assets.clone().into()),
359					bx!(beneficiary.clone().into()),
360				));
361
362				assert_expected_events!(
363					$sender_para,
364					vec![
365						RuntimeEvent::PolkadotXcm(
366							$crate::macros::pallet_xcm::Event::AssetsClaimed { origin: beneficiary, assets: versioned_assets, .. }
367						) => {},
368					]
369				);
370
371				// After claiming the assets, the balance has increased.
372				let balance_after = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&sender);
373				assert_eq!(balance_after, balance_before + $amount);
374
375				// Claiming the assets again doesn't work.
376				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
377					origin.clone(),
378					bx!(versioned_assets.clone().into()),
379					bx!(beneficiary.clone().into()),
380				).is_err());
381
382				let balance = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&sender);
383				assert_eq!(balance, balance_after);
384
385				// You can also claim assets and send them to a different account.
386				<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::drop_assets(
387					&beneficiary,
388					$assets.clone().into(),
389					&XcmContext { origin: None, message_id: [0u8; 32], topic: None },
390				);
391				let receiver = [<$sender_para Receiver>]::get();
392				let other_beneficiary: Location =
393					$crate::macros::AccountId32 { network: Some($network_id), id: receiver.clone().into() }.into();
394				let balance_before = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&receiver);
395				assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
396					origin.clone(),
397					bx!(versioned_assets.clone().into()),
398					bx!(other_beneficiary.clone().into()),
399				));
400				let balance_after = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&receiver);
401				assert_eq!(balance_after, balance_before + $amount);
402			});
403		}
404	};
405}
406
407#[macro_export]
408macro_rules! test_can_estimate_and_pay_exact_fees {
409	( $sender_para:ty, $asset_hub:ty, $receiver_para:ty, ($asset_id:expr, $amount:expr), $owner_prefix:ty ) => {
410		$crate::macros::paste::paste! {
411			// We first define the call we'll use throughout the test.
412			fn get_call(
413				estimated_local_fees: impl Into<Asset>,
414				estimated_intermediate_fees: impl Into<Asset>,
415				estimated_remote_fees: impl Into<Asset>,
416			) -> <$sender_para as Chain>::RuntimeCall {
417				type RuntimeCall = <$sender_para as Chain>::RuntimeCall;
418
419				let beneficiary = [<$receiver_para Receiver>]::get();
420				let xcm_in_destination = Xcm::<()>::builder_unsafe()
421					.pay_fees(estimated_remote_fees)
422					.deposit_asset(AllCounted(1), beneficiary)
423					.build();
424				let ah_to_receiver = $asset_hub::sibling_location_of($receiver_para::para_id());
425				let xcm_in_reserve = Xcm::<()>::builder_unsafe()
426					.pay_fees(estimated_intermediate_fees)
427					.deposit_reserve_asset(
428						AllCounted(1),
429						ah_to_receiver,
430						xcm_in_destination,
431					)
432					.build();
433				let sender_to_ah = $sender_para::sibling_location_of($asset_hub::para_id());
434				let local_xcm = Xcm::<<$sender_para as Chain>::RuntimeCall>::builder()
435					.withdraw_asset(($asset_id, $amount))
436					.pay_fees(estimated_local_fees)
437					.initiate_reserve_withdraw(AllCounted(1), sender_to_ah, xcm_in_reserve)
438					.build();
439
440				RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute {
441					message: bx!(VersionedXcm::from(local_xcm)),
442					max_weight: Weight::from_parts(10_000_000_000, 500_000),
443				})
444			}
445
446			let destination = $sender_para::sibling_location_of($receiver_para::para_id());
447			let sender = [<$sender_para Sender>]::get();
448			let sender_as_seen_by_ah = $asset_hub::sibling_location_of($sender_para::para_id());
449			let sov_of_sender_on_ah = $asset_hub::sovereign_account_id_of(sender_as_seen_by_ah.clone());
450			let asset_owner = [<$owner_prefix AssetOwner>]::get();
451
452			// Fund parachain's sender account.
453			$sender_para::mint_foreign_asset(
454				<$sender_para as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
455				$asset_id.clone().into(),
456				sender.clone(),
457				$amount * 2,
458			);
459
460			// Fund the parachain origin's SA on Asset Hub with the native tokens.
461			$asset_hub::fund_accounts(vec![(sov_of_sender_on_ah.clone(), $amount * 2)]);
462
463			let beneficiary_id = [<$receiver_para Receiver>]::get();
464
465			let test_args = TestContext {
466				sender: sender.clone(),
467				receiver: beneficiary_id.clone(),
468				args: TestArgs::new_para(
469					destination,
470					beneficiary_id.clone(),
471					$amount,
472					($asset_id, $amount).into(),
473					None,
474					0,
475				),
476			};
477			let mut test = ParaToParaThroughAHTest::new(test_args);
478
479			// We get these from the closure.
480			let mut local_execution_fees = 0;
481			let mut local_delivery_fees = 0;
482			let mut remote_message = VersionedXcm::from(Xcm::<()>(Vec::new()));
483			<$sender_para as TestExt>::execute_with(|| {
484				type Runtime = <$sender_para as Chain>::Runtime;
485				type OriginCaller = <$sender_para as Chain>::OriginCaller;
486
487				let call = get_call(
488					(Parent, 100_000_000_000u128),
489					(Parent, 100_000_000_000u128),
490					(Parent, 100_000_000_000u128),
491				);
492				let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
493				let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap();
494				let local_xcm = result.local_xcm.unwrap().clone();
495				let local_xcm_weight = Runtime::query_xcm_weight(local_xcm).unwrap();
496				local_execution_fees = Runtime::query_weight_to_asset_fee(
497					local_xcm_weight,
498					VersionedAssetId::from(AssetId(Location::parent())),
499				)
500				.unwrap();
501				// We filter the result to get only the messages we are interested in.
502				let (destination_to_query, messages_to_query) = &result
503					.forwarded_xcms
504					.iter()
505					.find(|(destination, _)| {
506						*destination == VersionedLocation::from(Location::new(1, [Parachain(1000)]))
507					})
508					.unwrap();
509				assert_eq!(messages_to_query.len(), 1);
510				remote_message = messages_to_query[0].clone();
511				let delivery_fees =
512					Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone())
513						.unwrap();
514				local_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees);
515			});
516
517			// These are set in the AssetHub closure.
518			let mut intermediate_execution_fees = 0;
519			let mut intermediate_delivery_fees = 0;
520			let mut intermediate_remote_message = VersionedXcm::from(Xcm::<()>(Vec::new()));
521			<$asset_hub as TestExt>::execute_with(|| {
522				type Runtime = <$asset_hub as Chain>::Runtime;
523				type RuntimeCall = <$asset_hub as Chain>::RuntimeCall;
524
525				// First we get the execution fees.
526				let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap();
527				intermediate_execution_fees = Runtime::query_weight_to_asset_fee(
528					weight,
529					VersionedAssetId::from(AssetId(Location::new(1, []))),
530				)
531				.unwrap();
532
533				// We have to do this to turn `VersionedXcm<()>` into `VersionedXcm<RuntimeCall>`.
534				let xcm_program =
535					VersionedXcm::from(Xcm::<RuntimeCall>::from(remote_message.clone().try_into().unwrap()));
536
537				// Now we get the delivery fees to the final destination.
538				let result =
539					Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap();
540				let (destination_to_query, messages_to_query) = &result
541					.forwarded_xcms
542					.iter()
543					.find(|(destination, _)| {
544						*destination == VersionedLocation::from(Location::new(1, [Parachain(2001)]))
545					})
546					.unwrap();
547				// There's actually two messages here.
548				// One created when the message we sent from `$sender_para` arrived and was executed.
549				// The second one when we dry-run the xcm.
550				// We could've gotten the message from the queue without having to dry-run, but
551				// offchain applications would have to dry-run, so we do it here as well.
552				intermediate_remote_message = messages_to_query[0].clone();
553				let delivery_fees = Runtime::query_delivery_fees(
554					destination_to_query.clone(),
555					intermediate_remote_message.clone(),
556				)
557				.unwrap();
558				intermediate_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees);
559			});
560
561			// Get the final execution fees in the destination.
562			let mut final_execution_fees = 0;
563			<$receiver_para as TestExt>::execute_with(|| {
564				type Runtime = <$sender_para as Chain>::Runtime;
565
566				let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap();
567				final_execution_fees =
568					Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::parent())))
569						.unwrap();
570			});
571
572			// Dry-running is done.
573			$sender_para::reset_ext();
574			$asset_hub::reset_ext();
575			$receiver_para::reset_ext();
576
577			// Fund accounts again.
578			$sender_para::mint_foreign_asset(
579				<$sender_para as Chain>::RuntimeOrigin::signed(asset_owner),
580				$asset_id.clone().into(),
581				sender.clone(),
582				$amount * 2,
583			);
584			$asset_hub::fund_accounts(vec![(sov_of_sender_on_ah, $amount * 2)]);
585
586			// Actually run the extrinsic.
587			let sender_assets_before = $sender_para::execute_with(|| {
588				type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets;
589				<ForeignAssets as Inspect<_>>::balance($asset_id.clone().into(), &sender)
590			});
591			let receiver_assets_before = $receiver_para::execute_with(|| {
592				type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets;
593				<ForeignAssets as Inspect<_>>::balance($asset_id.clone().into(), &beneficiary_id)
594			});
595
596			test.set_assertion::<$sender_para>(sender_assertions);
597			test.set_assertion::<$asset_hub>(hop_assertions);
598			test.set_assertion::<$receiver_para>(receiver_assertions);
599			let call = get_call(
600				(Parent, local_execution_fees + local_delivery_fees),
601				(Parent, intermediate_execution_fees + intermediate_delivery_fees),
602				(Parent, final_execution_fees),
603			);
604			test.set_call(call);
605			test.assert();
606
607			let sender_assets_after = $sender_para::execute_with(|| {
608				type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets;
609				<ForeignAssets as Inspect<_>>::balance($asset_id.clone().into(), &sender)
610			});
611			let receiver_assets_after = $receiver_para::execute_with(|| {
612				type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets;
613				<ForeignAssets as Inspect<_>>::balance($asset_id.into(), &beneficiary_id)
614			});
615
616			// We know the exact fees on every hop.
617			assert_eq!(sender_assets_after, sender_assets_before - $amount);
618			assert_eq!(
619				receiver_assets_after,
620				receiver_assets_before + $amount -
621					local_execution_fees -
622					local_delivery_fees -
623					intermediate_execution_fees -
624					intermediate_delivery_fees -
625					final_execution_fees
626			);
627		}
628	};
629}
630
631#[macro_export]
632macro_rules! test_dry_run_transfer_across_pk_bridge {
633	( $sender_asset_hub:ty, $sender_bridge_hub:ty, $destination:expr ) => {
634		$crate::macros::paste::paste! {
635			use frame_support::{dispatch::RawOrigin, traits::fungible};
636			use sp_runtime::AccountId32;
637			use xcm::prelude::*;
638			use xcm_runtime_apis::dry_run::runtime_decl_for_dry_run_api::DryRunApiV2;
639
640			let who = AccountId32::new([1u8; 32]);
641			let transfer_amount = 10_000_000_000_000u128;
642			let initial_balance = transfer_amount * 10;
643
644			// AssetHub setup.
645			$sender_asset_hub::force_xcm_version($destination, XCM_VERSION);
646
647			<$sender_asset_hub as TestExt>::execute_with(|| {
648				type Runtime = <$sender_asset_hub as Chain>::Runtime;
649				type RuntimeCall = <$sender_asset_hub as Chain>::RuntimeCall;
650				type OriginCaller = <$sender_asset_hub as Chain>::OriginCaller;
651				type Balances = <$sender_asset_hub as [<$sender_asset_hub Pallet>]>::Balances;
652
653				// Give some initial funds.
654				<Balances as fungible::Mutate<_>>::set_balance(&who, initial_balance);
655
656				let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets {
657					dest: Box::new(VersionedLocation::from($destination)),
658					beneficiary: Box::new(VersionedLocation::from(Junction::AccountId32 {
659						id: who.clone().into(),
660						network: None,
661					})),
662					assets: Box::new(VersionedAssets::from(vec![
663						(Parent, transfer_amount).into(),
664					])),
665					fee_asset_item: 0,
666					weight_limit: Unlimited,
667				});
668				let result = Runtime::dry_run_call(OriginCaller::system(RawOrigin::Signed(who)), call, XCM_VERSION).unwrap();
669				// We assert the dry run succeeds and sends only one message to the local bridge hub.
670				assert!(result.execution_result.is_ok());
671				assert_eq!(result.forwarded_xcms.len(), 1);
672				assert_eq!(result.forwarded_xcms[0].0, VersionedLocation::from(Location::new(1, [Parachain($sender_bridge_hub::para_id().into())])));
673			});
674		}
675	};
676}
677
678#[macro_export]
679macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub {
680	( $asset_hub:ty ) => {
681		$crate::macros::paste::paste! {
682			use emulated_integration_tests_common::USDT_ID;
683			use xcm_runtime_apis::fees::{Error as XcmPaymentApiError, runtime_decl_for_xcm_payment_api::XcmPaymentApiV1};
684
685			$asset_hub::execute_with(|| {
686				// Setup a pool between USDT and WND.
687				type RuntimeOrigin = <$asset_hub as Chain>::RuntimeOrigin;
688				type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets;
689				type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion;
690				let wnd = Location::new(1, []);
691				let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
692				let sender = [<$asset_hub Sender>]::get();
693				assert_ok!(AssetConversion::create_pool(
694					RuntimeOrigin::signed(sender.clone()),
695					Box::new(wnd.clone()),
696					Box::new(usdt.clone()),
697				));
698
699				type Runtime = <$asset_hub as Chain>::Runtime;
700				let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(XCM_VERSION).unwrap();
701				assert_eq!(acceptable_payment_assets, vec![
702					VersionedAssetId::from(AssetId(wnd.clone())),
703					VersionedAssetId::from(AssetId(usdt.clone())),
704				]);
705
706				let program = Xcm::<()>::builder()
707					.withdraw_asset((Parent, 100u128))
708					.buy_execution((Parent, 10u128), Unlimited)
709					.deposit_asset(All, [0u8; 32])
710					.build();
711				let weight = Runtime::query_xcm_weight(VersionedXcm::from(program)).unwrap();
712				let fee_in_wnd = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(wnd.clone()))).unwrap();
713				// Assets not in a pool don't work.
714				assert!(Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1)])))).is_err());
715				let fee_in_usdt_fail = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt.clone())));
716				// Weight to asset fee fails because there's not enough asset in the pool.
717				// We just created it, there's none.
718				assert_eq!(fee_in_usdt_fail, Err(XcmPaymentApiError::AssetNotFound));
719				// We add some.
720				assert_ok!(Assets::mint(
721					RuntimeOrigin::signed(sender.clone()),
722					USDT_ID.into(),
723					sender.clone().into(),
724					5_000_000_000_000
725				));
726				// We make 1 WND = 4 USDT.
727				assert_ok!(AssetConversion::add_liquidity(
728					RuntimeOrigin::signed(sender.clone()),
729					Box::new(wnd),
730					Box::new(usdt.clone()),
731					1_000_000_000_000,
732					4_000_000_000_000,
733					0,
734					0,
735					sender.into()
736				));
737				// Now it works.
738				let fee_in_usdt = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt)));
739				assert_ok!(fee_in_usdt);
740				assert!(fee_in_usdt.unwrap() > fee_in_wnd);
741			});
742		}
743	};
744}