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