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