referrerpolicy=no-referrer-when-downgrade

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_asset_conversion;
21pub use pallet_assets;
22pub use pallet_balances;
23pub use pallet_message_queue;
24pub use pallet_whitelist;
25pub use pallet_xcm;
26
27pub use frame_support::assert_ok;
28
29// Polkadot
30pub use polkadot_runtime_parachains::dmp::Pallet as Dmp;
31pub use xcm::{
32	latest::AssetTransferFilter,
33	prelude::{
34		AliasOrigin, All, AllCounted, Asset, AssetId, Assets, BuyExecution, DepositAsset,
35		ExpectTransactStatus, Fungible, GeneralIndex, Here, InitiateTransfer, Junction, Location,
36		MaybeErrorCode, OriginKind, Outcome, PalletInstance, Parachain, Parent, PayFees,
37		RefundSurplus, Transact, Unlimited, VersionedAssetId, VersionedAssets, VersionedLocation,
38		VersionedXcm, WeightLimit, Wild, WithdrawAsset, Xcm, XcmContext, XCM_VERSION,
39	},
40};
41
42pub use xcm_executor::traits::{DropAssets, TransferType};
43
44// Cumulus
45pub use asset_test_utils;
46pub use cumulus_pallet_xcmp_queue;
47pub use parachains_common::AccountId;
48pub use xcm_emulator::{
49	assert_expected_events, Chain, Parachain as Para, RelayChain, TestArgs, TestContext, TestExt,
50};
51
52pub use frame_support::{
53	dispatch::{GetDispatchInfo, RawOrigin},
54	BoundedVec,
55};
56pub use xcm_runtime_apis::{
57	dry_run::runtime_decl_for_dry_run_api::DryRunApiV2,
58	fees::{runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, Error as XcmPaymentApiError},
59};
60
61pub use frame_support::traits::{fungible::Mutate, fungibles::Inspect, Currency};
62pub use sp_runtime::{traits::Dispatchable, AccountId32};
63
64pub use crate::{ASSETS_PALLET_ID, USDT_ID};
65
66#[macro_export]
67macro_rules! test_parachain_is_trusted_teleporter {
68	( $sender_para:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr), $xcm_call:ident ) => {
69		$crate::macros::paste::paste! {
70			// init Origin variables
71			let sender = [<$sender_para Sender>]::get();
72			let mut para_sender_balance_before =
73				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
74			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
75			let fee_asset_item = 0;
76			let weight_limit = $crate::macros::WeightLimit::Unlimited;
77
78			$(
79				{
80					// init Destination variables
81					let receiver = [<$receiver_para Receiver>]::get();
82					let para_receiver_balance_before =
83						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
84					let para_destination =
85						<$sender_para as $crate::macros::Para>::sibling_location_of(
86							<$receiver_para as $crate::macros::Para>::para_id());
87					let beneficiary: $crate::macros::Location =
88						$crate::macros::Junction::AccountId32 { network: None, id: receiver.clone().into() }.into();
89
90					// Dry-run first.
91					let call = <$sender_para as $crate::macros::Chain>::RuntimeCall::PolkadotXcm(
92						$crate::macros::pallet_xcm::Call::$xcm_call {
93						dest: Box::new(para_destination.clone().into()),
94						beneficiary: Box::new(beneficiary.clone().into()),
95						assets: Box::new($assets.clone().into()),
96						fee_asset_item: fee_asset_item,
97						weight_limit: weight_limit.clone(),
98					});
99
100					// assume up to 90% of max weight
101					let max_weight_with_margin_for_error = ($crate::macros::Weight::MAX.ref_time() / 100) * 90;
102					assert!(<_ as $crate::macros::GetDispatchInfo>::get_dispatch_info(&call)
103						.call_weight.ref_time() < max_weight_with_margin_for_error);
104
105					let mut delivery_fees_amount = 0;
106					let mut remote_message = $crate::macros::VersionedXcm::from($crate::macros::Xcm(Vec::new()));
107					<$sender_para as $crate::macros::TestExt>::execute_with(|| {
108						type Runtime = <$sender_para as $crate::macros::Chain>::Runtime;
109						type OriginCaller = <$sender_para as $crate::macros::Chain>::OriginCaller;
110
111						let origin = OriginCaller::system($crate::macros::RawOrigin::Signed(sender.clone()));
112						let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_call(origin, call.clone(),
113							$crate::macros::XCM_VERSION).unwrap();
114
115
116						// We filter the result to get only the messages we are interested in.
117						let (destination_to_query, messages_to_query) = &result
118							.forwarded_xcms
119							.iter()
120							.find(|(destination, _)| {
121								*destination == $crate::macros::VersionedLocation::from($crate::macros::Location::new(1,
122									[$crate::macros::Parachain(<$receiver_para as $crate::macros::Para>::para_id().into())]))
123							})
124							.unwrap();
125						assert_eq!(messages_to_query.len(), 1);
126						remote_message = messages_to_query[0].clone();
127						let delivery_fees =
128							<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_delivery_fees(destination_to_query.clone(),
129							remote_message.clone()).unwrap();
130						let latest_delivery_fees: $crate::macros::Assets = delivery_fees.clone().try_into().unwrap();
131						let $crate::macros::Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else {
132							unreachable!("asset is non-fungible");
133						};
134						delivery_fees_amount = inner_delivery_fees_amount;
135					});
136
137					// Reset to send actual message.
138					<$sender_para as $crate::macros::TestExt>::reset_ext();
139					<$receiver_para as $crate::macros::TestExt>::reset_ext();
140
141					// TODO: The test fails without the line below, seems like no horizontal message passing is being done
142					//       when also using dry_run_call above (it works if there is no dry_run_call)
143					//       So this is just workaround, must be investigated
144					<$sender_para as $crate::macros::TestExt>::execute_with(|| { });
145
146					// Send XCM message from Origin Parachain
147					<$sender_para as $crate::macros::TestExt>::execute_with(|| {
148						let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
149						$crate::macros::assert_ok!(<_ as $crate::macros::Dispatchable>::dispatch(call, origin));
150
151						type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
152
153						$crate::macros::assert_expected_events!(
154							$sender_para,
155							vec![
156								RuntimeEvent::PolkadotXcm(
157									$crate::macros::pallet_xcm::Event::Attempted { outcome: $crate::macros::Outcome::Complete { .. } }
158								) => {},
159								RuntimeEvent::XcmpQueue(
160									$crate::macros::cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
161								) => {},
162								RuntimeEvent::Balances(
163									$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
164								) => {},
165							]
166						);
167					});
168
169					// Receive XCM message in Destination Parachain
170					<$receiver_para as $crate::macros::TestExt>::execute_with(|| {
171						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
172
173						$crate::macros::assert_expected_events!(
174							$receiver_para,
175							vec![
176								RuntimeEvent::Balances(
177									$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
178								) => {},
179								RuntimeEvent::MessageQueue(
180									$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
181								) => {},
182							]
183						);
184					});
185
186					// Check if balances are updated accordingly in Origin and Destination Parachains
187					let para_sender_balance_after =
188						<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
189					let para_receiver_balance_after =
190						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
191
192					assert_eq!(para_sender_balance_before - $amount - delivery_fees_amount, para_sender_balance_after);
193					assert!(para_receiver_balance_after > para_receiver_balance_before);
194
195					// Update sender balance
196					para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
197				}
198			)+
199		}
200	};
201}
202
203#[macro_export]
204macro_rules! test_relay_is_trusted_teleporter {
205	( $sender_relay:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr), $xcm_call:ident ) => {
206		$crate::macros::paste::paste! {
207			// init Origin variables
208			let sender = [<$sender_relay Sender>]::get();
209			let mut relay_sender_balance_before =
210				<$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
211			let fee_asset_item = 0;
212			let weight_limit = $crate::macros::WeightLimit::Unlimited;
213
214			$(
215				{
216					// init Destination variables
217					let receiver = [<$receiver_para Receiver>]::get();
218					let para_receiver_balance_before =
219						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
220					let para_destination =
221						<$sender_relay as $crate::macros::RelayChain>::child_location_of(
222							<$receiver_para as $crate::macros::Para>::para_id());
223					let beneficiary: $crate::macros::Location =
224						$crate::macros::Junction::AccountId32 { network: None, id: receiver.clone().into() }.into();
225
226					// Dry-run first.
227					let call = <$sender_relay as $crate::macros::Chain>::RuntimeCall::XcmPallet(
228						$crate::macros::pallet_xcm::Call::$xcm_call {
229						dest: Box::new(para_destination.clone().into()),
230						beneficiary: Box::new(beneficiary.clone().into()),
231						assets: Box::new($assets.clone().into()),
232						fee_asset_item: fee_asset_item,
233						weight_limit: weight_limit.clone(),
234					});
235
236					// verify sane weight for a call
237					// assume up to 90% of max weight
238					let max_weight_with_margin_for_error = ($crate::macros::Weight::MAX.ref_time() / 100) * 90;
239					assert!(<_ as $crate::macros::GetDispatchInfo>::get_dispatch_info(&call)
240						.call_weight.ref_time() < max_weight_with_margin_for_error);
241
242					let mut delivery_fees_amount = 0;
243					let mut remote_message = $crate::macros::VersionedXcm::from($crate::macros::Xcm(Vec::new()));
244					<$sender_relay as $crate::macros::TestExt>::execute_with(|| {
245						$crate::macros::Dmp::<<$sender_relay as $crate::macros::Chain>::Runtime>::make_parachain_reachable(
246							<$receiver_para as $crate::macros::Para>::para_id());
247						type Runtime = <$sender_relay as $crate::macros::Chain>::Runtime;
248						type OriginCaller = <$sender_relay as $crate::macros::Chain>::OriginCaller;
249
250						let origin = OriginCaller::system($crate::macros::RawOrigin::Signed(sender.clone()));
251						let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_call(origin, call.clone(),
252							$crate::macros::XCM_VERSION).unwrap();
253						// We filter the result to get only the messages we are interested in.
254						let (destination_to_query, messages_to_query) = &result
255							.forwarded_xcms
256							.iter()
257							.find(|(destination, _)| {
258								*destination == $crate::macros::VersionedLocation::from($crate::macros::Location::new(0,
259									[$crate::macros::Parachain(<$receiver_para as $crate::macros::Para>::para_id().into())]))
260							})
261							.unwrap();
262						assert_eq!(messages_to_query.len(), 1);
263						remote_message = messages_to_query[0].clone();
264						let delivery_fees =
265							<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_delivery_fees(destination_to_query.clone(),
266								remote_message.clone()).unwrap();
267						let latest_delivery_fees: $crate::macros::Assets = delivery_fees.clone().try_into().unwrap();
268						let $crate::macros::Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else {
269							unreachable!("asset is non-fungible");
270						};
271						delivery_fees_amount = inner_delivery_fees_amount;
272					});
273
274					// Reset to send actual message.
275					<$sender_relay as $crate::macros::TestExt>::reset_ext();
276					<$receiver_para as $crate::macros::TestExt>::reset_ext();
277
278					// Send XCM message from Relay.
279					<$sender_relay as $crate::macros::TestExt>::execute_with(|| {
280						$crate::macros::Dmp::<<$sender_relay as $crate::macros::Chain>::Runtime>::make_parachain_reachable(
281							<$receiver_para as $crate::macros::Para>::para_id());
282						let origin = <$sender_relay as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
283						$crate::macros::assert_ok!(<_ as $crate::macros::Dispatchable>::dispatch(call, origin));
284
285						type RuntimeEvent = <$sender_relay as $crate::macros::Chain>::RuntimeEvent;
286
287						$crate::macros::assert_expected_events!(
288							$sender_relay,
289							vec![
290								RuntimeEvent::XcmPallet(
291									$crate::macros::pallet_xcm::Event::Attempted { outcome: $crate::macros::Outcome::Complete { .. } }
292								) => {},
293								RuntimeEvent::Balances(
294									$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
295								) => {},
296								RuntimeEvent::XcmPallet(
297									$crate::macros::pallet_xcm::Event::Sent { .. }
298								) => {},
299							]
300						);
301					});
302
303					// Receive XCM message in Destination Parachain
304					<$receiver_para as $crate::macros::TestExt>::execute_with(|| {
305						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
306
307						$crate::macros::assert_expected_events!(
308							$receiver_para,
309							vec![
310								RuntimeEvent::Balances(
311									$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
312								) => {},
313								RuntimeEvent::MessageQueue(
314									$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
315								) => {},
316							]
317						);
318					});
319
320					// Check if balances are updated accordingly in Origin and Parachain
321					let relay_sender_balance_after =
322						<$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
323					let para_receiver_balance_after =
324						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
325
326					assert_eq!(relay_sender_balance_before - $amount - delivery_fees_amount, relay_sender_balance_after);
327					assert!(para_receiver_balance_after > para_receiver_balance_before);
328
329					// Update sender balance
330					relay_sender_balance_before = <$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
331				}
332			)+
333		}
334	};
335}
336
337#[macro_export]
338macro_rules! test_parachain_is_trusted_teleporter_for_relay {
339	( $sender_para:ty, $receiver_relay:ty, $amount:expr, $xcm_call:ident ) => {
340		$crate::macros::paste::paste! {
341			// init Origin variables
342			let sender = [<$sender_para Sender>]::get();
343			// Mint assets to `$sender_para` to succeed with teleport.
344			<$sender_para as $crate::macros::TestExt>::execute_with(|| {
345				$crate::macros::assert_ok!(<<$sender_para as [<$sender_para Pallet>]>::Balances
346					as $crate::macros::Mutate<_>>::mint_into(&sender, $amount + 10_000_000_000));
347
348			});
349			let mut para_sender_balance_before =
350				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
351			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
352			let assets: $crate::macros::Assets = ($crate::macros::Parent, $amount).into();
353			let fee_asset_item = 0;
354			let weight_limit = $crate::macros::WeightLimit::Unlimited;
355
356			// We need to mint funds into the checking account of `$receiver_relay`
357			// for it to accept a teleport from `$sender_para`.
358			// Else we'd get a `NotWithdrawable` error since it tries to reduce the check account balance, which
359			// would be 0.
360			<$receiver_relay as $crate::macros::TestExt>::execute_with(|| {
361				let check_account = <$receiver_relay as [<$receiver_relay Pallet>]>::XcmPallet::check_account();
362				$crate::macros::assert_ok!(<<$receiver_relay as [<$receiver_relay Pallet>]>::Balances
363					as $crate::macros::Mutate<_>>::mint_into(&check_account, $amount));
364			});
365
366			// Init destination variables.
367			let receiver = [<$receiver_relay Receiver>]::get();
368			let relay_receiver_balance_before =
369				<$receiver_relay as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
370			let relay_destination: $crate::macros::Location = $crate::macros::Parent.into();
371			let beneficiary: $crate::macros::Location =
372				$crate::macros::Junction::AccountId32 { network: None, id: receiver.clone().into() }.into();
373
374			// Dry-run first.
375			let call = <$sender_para as $crate::macros::Chain>::RuntimeCall::PolkadotXcm($crate::macros::pallet_xcm::Call::$xcm_call {
376				dest: Box::new(relay_destination.clone().into()),
377				beneficiary: Box::new(beneficiary.clone().into()),
378				assets: Box::new(assets.clone().into()),
379				fee_asset_item: fee_asset_item,
380				weight_limit: weight_limit.clone(),
381			});
382
383			// verify sane weight for a call
384			// assume up to 90% of max weight
385			let max_weight_with_margin_for_error = ($crate::macros::Weight::MAX.ref_time() / 100) * 90;
386			assert!(<_ as $crate::macros::GetDispatchInfo>::get_dispatch_info(&call)
387				.call_weight.ref_time() < max_weight_with_margin_for_error);
388
389			// These will be filled in the closure.
390			let mut delivery_fees_amount = 0;
391			let mut remote_message = $crate::macros::VersionedXcm::from($crate::macros::Xcm(Vec::new()));
392			<$sender_para as $crate::macros::TestExt>::execute_with(|| {
393				type Runtime = <$sender_para as $crate::macros::Chain>::Runtime;
394				type OriginCaller = <$sender_para as $crate::macros::Chain>::OriginCaller;
395
396				let origin = OriginCaller::system($crate::macros::RawOrigin::Signed(sender.clone()));
397				let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_call(origin, call.clone(),
398					$crate::macros::XCM_VERSION).unwrap();
399				// We filter the result to get only the messages we are interested in.
400				let (destination_to_query, messages_to_query) = &result
401					.forwarded_xcms
402					.iter()
403					.find(|(destination, _)| {
404						*destination == $crate::macros::VersionedLocation::from($crate::macros::Location::parent())
405					})
406					.unwrap();
407				assert_eq!(messages_to_query.len(), 1);
408				remote_message = messages_to_query[0].clone();
409				let delivery_fees =
410					<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_delivery_fees(destination_to_query.clone(),
411					remote_message.clone()).unwrap();
412				let latest_delivery_fees: $crate::macros::Assets = delivery_fees.clone().try_into().unwrap();
413				delivery_fees_amount = if let Some(first_asset) = latest_delivery_fees.inner().first() {
414					let $crate::macros::Fungible(inner_delivery_fees_amount) = first_asset.fun else {
415						unreachable!("asset is non-fungible");
416					};
417					inner_delivery_fees_amount
418				} else {
419					0
420				}
421			});
422
423			// Reset to send actual message.
424			<$sender_para as $crate::macros::TestExt>::reset_ext();
425			<$receiver_relay as $crate::macros::TestExt>::reset_ext();
426			// Mint assets to `$sender_para` to succeed with teleport.
427			<$sender_para as $crate::macros::TestExt>::execute_with(|| {
428				$crate::macros::assert_ok!(<<$sender_para as [<$sender_para Pallet>]>::Balances
429					as $crate::macros::Mutate<_>>::mint_into(&sender, $amount + 10_000_000_000));
430			});
431
432			// Since we reset everything, we need to mint funds into the checking account again.
433			<$receiver_relay as $crate::macros::TestExt>::execute_with(|| {
434				let check_account = <$receiver_relay as [<$receiver_relay Pallet>]>::XcmPallet::check_account();
435				$crate::macros::assert_ok!(<<$receiver_relay as [<$receiver_relay Pallet>]>::Balances
436					as $crate::macros::Mutate<_>>::mint_into(&check_account, $amount));
437			});
438
439			// Send XCM message from Parachain.
440			<$sender_para as $crate::macros::TestExt>::execute_with(|| {
441				let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
442				$crate::macros::assert_ok!(<_ as $crate::macros::Dispatchable>::dispatch(call, origin));
443
444				type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
445
446				$crate::macros::assert_expected_events!(
447					$sender_para,
448					vec![
449						RuntimeEvent::PolkadotXcm(
450							$crate::macros::pallet_xcm::Event::Attempted { outcome: $crate::macros::Outcome::Complete { .. } }
451						) => {},
452						RuntimeEvent::Balances(
453							$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
454						) => {},
455						RuntimeEvent::PolkadotXcm(
456							$crate::macros::pallet_xcm::Event::Sent { .. }
457						) => {},
458					]
459				);
460			});
461
462			// Receive XCM message in Destination Parachain
463			<$receiver_relay as $crate::macros::TestExt>::execute_with(|| {
464				type RuntimeEvent = <$receiver_relay as $crate::macros::Chain>::RuntimeEvent;
465
466				$crate::macros::assert_expected_events!(
467					$receiver_relay,
468					vec![
469						RuntimeEvent::Balances(
470							$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
471						) => {},
472						RuntimeEvent::MessageQueue(
473							$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
474						) => {},
475					]
476				);
477			});
478
479			// Check if balances are updated accordingly in Origin and Relay Chain
480			let para_sender_balance_after =
481				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
482			let relay_receiver_balance_after =
483				<$receiver_relay as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
484
485			assert_eq!(para_sender_balance_before - $amount - delivery_fees_amount, para_sender_balance_after);
486			assert!(relay_receiver_balance_after > relay_receiver_balance_before);
487
488			// Update sender balance
489			para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
490		}
491	};
492}
493
494#[macro_export]
495macro_rules! test_chain_can_claim_assets {
496	( $sender_para:ty, $runtime_call:ty, $network_id:expr, $assets:expr, $amount:expr ) => {
497		$crate::macros::paste::paste! {
498			let sender = [<$sender_para Sender>]::get();
499			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
500			// Receiver is the same as sender
501			let beneficiary: $crate::macros::Location =
502				$crate::macros::Junction::AccountId32 { network: Some($network_id), id: sender.clone().into() }.into();
503			let versioned_assets: $crate::macros::VersionedAssets = $assets.clone().into();
504
505			<$sender_para as $crate::macros::TestExt>::execute_with(|| {
506				// Assets are trapped for whatever reason.
507				// The possible reasons for this might differ from runtime to runtime, so here we just drop them directly.
508				<<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm as $crate::macros::DropAssets>::drop_assets(
509					&beneficiary,
510					$assets.clone().into(),
511					&$crate::macros::XcmContext { origin: None, message_id: [0u8; 32], topic: None },
512				);
513
514				type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
515				$crate::macros::assert_expected_events!(
516					$sender_para,
517					vec![
518						RuntimeEvent::PolkadotXcm(
519							$crate::macros::pallet_xcm::Event::AssetsTrapped { origin: beneficiary, assets: versioned_assets, .. }
520						) => {},
521					]
522				);
523
524				let balance_before = <<$sender_para as [<$sender_para Pallet>]>::Balances
525					as $crate::macros::Currency<_>>::free_balance(&sender);
526
527				// Different origin or different assets won't work.
528				let other_origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed([<$sender_para Receiver>]::get());
529				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
530					other_origin,
531					Box::new(versioned_assets.clone().into()),
532					Box::new(beneficiary.clone().into()),
533				).is_err());
534				let other_versioned_assets: $crate::macros::VersionedAssets = $crate::macros::Assets::new().into();
535				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
536					origin.clone(),
537					Box::new(other_versioned_assets.into()),
538					Box::new(beneficiary.clone().into()),
539				).is_err());
540
541				// Assets will be claimed to `beneficiary`, which is the same as `sender`.
542				$crate::macros::assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
543					origin.clone(),
544					Box::new(versioned_assets.clone().into()),
545					Box::new(beneficiary.clone().into()),
546				));
547
548				$crate::macros::assert_expected_events!(
549					$sender_para,
550					vec![
551						RuntimeEvent::PolkadotXcm(
552							$crate::macros::pallet_xcm::Event::AssetsClaimed { origin: beneficiary, assets: versioned_assets, .. }
553						) => {},
554					]
555				);
556
557				// After claiming the assets, the balance has increased.
558				let balance_after = <<$sender_para as [<$sender_para Pallet>]>::Balances
559					as $crate::macros::Currency<_>>::free_balance(&sender);
560				assert_eq!(balance_after, balance_before + $amount);
561
562				// Claiming the assets again doesn't work.
563				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
564					origin.clone(),
565					Box::new(versioned_assets.clone().into()),
566					Box::new(beneficiary.clone().into()),
567				).is_err());
568
569				let balance = <<$sender_para as [<$sender_para Pallet>]>::Balances
570					as $crate::macros::Currency<_>>::free_balance(&sender);
571				assert_eq!(balance, balance_after);
572
573				// You can also claim assets and send them to a different account.
574				<<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm as $crate::macros::DropAssets>::drop_assets(
575					&beneficiary,
576					$assets.clone().into(),
577					&$crate::macros::XcmContext { origin: None, message_id: [0u8; 32], topic: None },
578				);
579				let receiver = [<$sender_para Receiver>]::get();
580				let other_beneficiary: $crate::macros::Location =
581					$crate::macros::Junction::AccountId32 { network: Some($network_id), id: receiver.clone().into() }.into();
582				let balance_before = <<$sender_para as [<$sender_para Pallet>]>::Balances
583					as $crate::macros::Currency<_>>::free_balance(&receiver);
584				$crate::macros::assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
585					origin.clone(),
586					Box::new(versioned_assets.clone().into()),
587					Box::new(other_beneficiary.clone().into()),
588				));
589				let balance_after = <<$sender_para as [<$sender_para Pallet>]>::Balances
590					as $crate::macros::Currency<_>>::free_balance(&receiver);
591				assert_eq!(balance_after, balance_before + $amount);
592			});
593		}
594	};
595}
596
597#[macro_export]
598macro_rules! test_can_estimate_and_pay_exact_fees {
599	( $sender_para:ty, $asset_hub:ty, $receiver_para:ty, ($asset_id:expr, $amount:expr), $owner_prefix:ty ) => {
600		$crate::macros::paste::paste! {
601			// We first define the call we'll use throughout the test.
602			fn get_call(
603				estimated_local_fees: impl Into<$crate::macros::Asset>,
604				estimated_intermediate_fees: impl Into<$crate::macros::Asset>,
605				estimated_remote_fees: impl Into<$crate::macros::Asset>,
606			) -> <$sender_para as $crate::macros::Chain>::RuntimeCall {
607				type RuntimeCall = <$sender_para as $crate::macros::Chain>::RuntimeCall;
608
609				let beneficiary = [<$receiver_para Receiver>]::get();
610				let xcm_in_destination = $crate::macros::Xcm::<()>::builder_unsafe()
611					.pay_fees(estimated_remote_fees)
612					.deposit_asset($crate::macros::AllCounted(1), beneficiary)
613					.build();
614				let ah_to_receiver = <$asset_hub as $crate::macros::Para>::sibling_location_of(
615					<$receiver_para as $crate::macros::Para>::para_id());
616				let xcm_in_reserve = $crate::macros::Xcm::<()>::builder_unsafe()
617					.pay_fees(estimated_intermediate_fees)
618					.deposit_reserve_asset(
619						$crate::macros::AllCounted(1),
620						ah_to_receiver,
621						xcm_in_destination,
622					)
623					.build();
624				let sender_to_ah = <$sender_para as $crate::macros::Para>::sibling_location_of(
625					<$asset_hub as $crate::macros::Para>::para_id());
626				let local_xcm = $crate::macros::Xcm::<<$sender_para as $crate::macros::Chain>::RuntimeCall>::builder()
627					.withdraw_asset(($asset_id, $amount))
628					.pay_fees(estimated_local_fees)
629					.initiate_reserve_withdraw($crate::macros::AllCounted(1), sender_to_ah, xcm_in_reserve)
630					.build();
631
632				RuntimeCall::PolkadotXcm($crate::macros::pallet_xcm::Call::execute {
633					message: Box::new($crate::macros::VersionedXcm::from(local_xcm)),
634					max_weight: $crate::macros::Weight::from_parts(10_000_000_000, 500_000),
635				})
636			}
637
638			let destination = <$sender_para as $crate::macros::Para>::sibling_location_of(
639				<$receiver_para as $crate::macros::Para>::para_id());
640			let sender = [<$sender_para Sender>]::get();
641			let sender_as_seen_by_ah = <$asset_hub as $crate::macros::Para>::sibling_location_of(
642				<$sender_para as $crate::macros::Para>::para_id());
643			let sov_of_sender_on_ah = <$asset_hub as $crate::macros::Para>::sovereign_account_id_of(sender_as_seen_by_ah.clone());
644			let asset_owner = [<$owner_prefix AssetOwner>]::get();
645
646			// Fund parachain's sender account.
647			// TODO: consider mint_foreign_asset to be part of xcm_emulator::Chain trait
648			$sender_para::mint_foreign_asset(
649				<$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(asset_owner.clone()),
650				$asset_id.clone().into(),
651				sender.clone(),
652				$amount * 2,
653			);
654
655			// Fund the parachain origin's SA on Asset Hub with the native tokens.
656			// TODO: consider fund_accounts to be part of xcm_emulator::Chain trait
657			$asset_hub::fund_accounts(vec![(sov_of_sender_on_ah.clone(), $amount * 2)]);
658
659			let beneficiary_id = [<$receiver_para Receiver>]::get();
660
661			let test_args = $crate::macros::TestContext {
662				sender: sender.clone(),
663				receiver: beneficiary_id.clone(),
664				args: $crate::macros::TestArgs::new_para(
665					destination,
666					beneficiary_id.clone(),
667					$amount,
668					($asset_id, $amount).into(),
669					None,
670					0,
671				),
672			};
673			let mut test = ParaToParaThroughAHTest::new(test_args);
674
675			// We get these from the closure.
676			let mut local_execution_fees = 0;
677			let mut local_delivery_fees = 0;
678			let mut remote_message = $crate::macros::VersionedXcm::from($crate::macros::Xcm::<()>(Vec::new()));
679			<$sender_para as $crate::macros::TestExt>::execute_with(|| {
680				type Runtime = <$sender_para as $crate::macros::Chain>::Runtime;
681				type OriginCaller = <$sender_para as $crate::macros::Chain>::OriginCaller;
682
683				let call = get_call(
684					($crate::macros::Parent, 100_000_000_000u128),
685					($crate::macros::Parent, 100_000_000_000u128),
686					($crate::macros::Parent, 100_000_000_000u128),
687				);
688				let origin = OriginCaller::system($crate::macros::RawOrigin::Signed(sender.clone()));
689				let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_call(origin, call.clone(),
690					$crate::macros::XCM_VERSION).unwrap();
691				let local_xcm = result.local_xcm.unwrap().clone();
692				let local_xcm_weight = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_xcm_weight(local_xcm).unwrap();
693				local_execution_fees = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(
694					local_xcm_weight,
695					$crate::macros::VersionedAssetId::from($crate::macros::AssetId($crate::macros::Location::parent())),
696				)
697				.unwrap();
698				// We filter the result to get only the messages we are interested in.
699				let (destination_to_query, messages_to_query) = &result
700					.forwarded_xcms
701					.iter()
702					.find(|(destination, _)| {
703						*destination == $crate::macros::VersionedLocation::from(
704							$crate::macros::Location::new(1, [$crate::macros::Parachain(1000)]))
705					})
706					.unwrap();
707				assert_eq!(messages_to_query.len(), 1);
708				remote_message = messages_to_query[0].clone();
709				let delivery_fees =
710					<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_delivery_fees(destination_to_query.clone(),
711						remote_message.clone()).unwrap();
712				local_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees);
713			});
714
715			// These are set in the AssetHub closure.
716			let mut intermediate_execution_fees = 0;
717			let mut intermediate_delivery_fees = 0;
718			let mut intermediate_remote_message = $crate::macros::VersionedXcm::from($crate::macros::Xcm::<()>(Vec::new()));
719			<$asset_hub as $crate::macros::TestExt>::execute_with(|| {
720				type Runtime = <$asset_hub as $crate::macros::Chain>::Runtime;
721				type RuntimeCall = <$asset_hub as $crate::macros::Chain>::RuntimeCall;
722
723				// First we get the execution fees.
724				let weight = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_xcm_weight(remote_message.clone()).unwrap();
725				intermediate_execution_fees = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(
726					weight,
727					$crate::macros::VersionedAssetId::from($crate::macros::AssetId($crate::macros::Location::new(1, []))),
728				)
729				.unwrap();
730
731				// We have to do this to turn `VersionedXcm<()>` into `VersionedXcm<RuntimeCall>`.
732				let xcm_program =
733					$crate::macros::VersionedXcm::from($crate::macros::Xcm::<RuntimeCall>::from(
734						remote_message.clone().try_into().unwrap()));
735
736				// Now we get the delivery fees to the final destination.
737				let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_xcm(
738					sender_as_seen_by_ah.clone().into(), xcm_program).unwrap();
739				let (destination_to_query, messages_to_query) = &result
740					.forwarded_xcms
741					.iter()
742					.find(|(destination, _)| {
743						*destination == $crate::macros::VersionedLocation::from($crate::macros::Location::new(1,
744							[$crate::macros::Parachain(2001)]))
745					})
746					.unwrap();
747				// There's actually two messages here.
748				// One created when the message we sent from `$sender_para` arrived and was executed.
749				// The second one when we dry-run the xcm.
750				// We could've gotten the message from the queue without having to dry-run, but
751				// offchain applications would have to dry-run, so we do it here as well.
752				intermediate_remote_message = messages_to_query[0].clone();
753				let delivery_fees = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_delivery_fees(
754					destination_to_query.clone(),
755					intermediate_remote_message.clone(),
756				)
757				.unwrap();
758				intermediate_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees);
759			});
760
761			// Get the final execution fees in the destination.
762			let mut final_execution_fees = 0;
763			<$receiver_para as $crate::macros::TestExt>::execute_with(|| {
764				type Runtime = <$sender_para as $crate::macros::Chain>::Runtime;
765
766				let weight = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_xcm_weight(
767					intermediate_remote_message.clone()).unwrap();
768				final_execution_fees =
769					<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
770						$crate::macros::VersionedAssetId::from($crate::macros::AssetId($crate::macros::Location::parent())))
771						.unwrap();
772			});
773
774			// Dry-running is done.
775			<$sender_para as $crate::macros::TestExt>::reset_ext();
776			<$asset_hub as $crate::macros::TestExt>::reset_ext();
777			<$receiver_para as $crate::macros::TestExt>::reset_ext();
778
779			// Fund accounts again.
780			$sender_para::mint_foreign_asset(
781				<$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(asset_owner),
782				$asset_id.clone().into(),
783				sender.clone(),
784				$amount * 2,
785			);
786			$asset_hub::fund_accounts(vec![(sov_of_sender_on_ah, $amount * 2)]);
787
788			// Actually run the extrinsic.
789			let sender_assets_before = <$sender_para as $crate::macros::TestExt>::execute_with(|| {
790				type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets;
791				<ForeignAssets as $crate::macros::Inspect<_>>::balance($asset_id.clone().into(), &sender)
792			});
793			let receiver_assets_before = <$receiver_para as $crate::macros::TestExt>::execute_with(|| {
794				type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets;
795				<ForeignAssets as $crate::macros::Inspect<_>>::balance($asset_id.clone().into(), &beneficiary_id)
796			});
797
798			test.set_assertion::<$sender_para>(sender_assertions);
799			test.set_assertion::<$asset_hub>(hop_assertions);
800			test.set_assertion::<$receiver_para>(receiver_assertions);
801			let call = get_call(
802				($crate::macros::Parent, local_execution_fees + local_delivery_fees),
803				($crate::macros::Parent, intermediate_execution_fees + intermediate_delivery_fees),
804				($crate::macros::Parent, final_execution_fees),
805			);
806			test.set_call(call);
807			test.assert();
808
809			let sender_assets_after = <$sender_para as $crate::macros::TestExt>::execute_with(|| {
810				type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets;
811				<ForeignAssets as $crate::macros::Inspect<_>>::balance($asset_id.clone().into(), &sender)
812			});
813			let receiver_assets_after = <$receiver_para as $crate::macros::TestExt>::execute_with(|| {
814				type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets;
815				<ForeignAssets as $crate::macros::Inspect<_>>::balance($asset_id.into(), &beneficiary_id)
816			});
817
818			// We know the exact fees on every hop.
819			assert_eq!(sender_assets_after, sender_assets_before - $amount);
820			assert_eq!(
821				receiver_assets_after,
822				receiver_assets_before + $amount -
823					local_execution_fees -
824					local_delivery_fees -
825					intermediate_execution_fees -
826					intermediate_delivery_fees -
827					final_execution_fees
828			);
829		}
830	};
831}
832
833#[macro_export]
834macro_rules! test_dry_run_transfer_across_pk_bridge {
835	( $sender_asset_hub:ty, $sender_bridge_hub:ty, $destination:expr ) => {
836		$crate::macros::paste::paste! {
837
838			let who = $crate::macros::AccountId32::new([1u8; 32]);
839			let transfer_amount = 10_000_000_000_000u128;
840			let initial_balance = transfer_amount * 10;
841
842			// AssetHub setup.
843			$sender_asset_hub::force_xcm_version($destination, $crate::macros::XCM_VERSION);
844
845			<$sender_asset_hub as $crate::macros::TestExt>::execute_with(|| {
846				type Runtime = <$sender_asset_hub as $crate::macros::Chain>::Runtime;
847				type RuntimeCall = <$sender_asset_hub as $crate::macros::Chain>::RuntimeCall;
848				type OriginCaller = <$sender_asset_hub as $crate::macros::Chain>::OriginCaller;
849				type Balances = <$sender_asset_hub as [<$sender_asset_hub Pallet>]>::Balances;
850
851				// Give some initial funds.
852				<Balances as $crate::macros::Mutate<_>>::set_balance(&who, initial_balance);
853
854				let beneficiary: $crate::macros::Location = $crate::macros::Junction::AccountId32 {
855						id: who.clone().into(),
856						network: None,
857					}.into();
858
859				let call = RuntimeCall::PolkadotXcm($crate::macros::pallet_xcm::Call::transfer_assets_using_type_and_then {
860					dest: Box::new($crate::macros::VersionedLocation::from($destination)),
861					assets: Box::new($crate::macros::VersionedAssets::from(vec![
862						($crate::macros::Parent, transfer_amount).into(),
863					])),
864					assets_transfer_type: Box::new($crate::macros::TransferType::LocalReserve),
865					remote_fees_id: Box::new($crate::macros::VersionedAssetId::from($crate::macros::Parent)),
866					fees_transfer_type: Box::new($crate::macros::TransferType::LocalReserve),
867					custom_xcm_on_dest: Box::new($crate::macros::VersionedXcm::<()>::from($crate::macros::Xcm::<()>::builder_unsafe().deposit_asset(AllCounted(1), beneficiary).build())),
868					weight_limit: $crate::macros::Unlimited,
869				});
870				let origin = OriginCaller::system($crate::macros::RawOrigin::Signed(who));
871				let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_call(origin, call.clone(),
872					$crate::macros::XCM_VERSION).unwrap();
873
874				// We assert the dry run succeeds and sends only one message to the local bridge hub.
875				assert!(result.execution_result.is_ok());
876				assert_eq!(result.forwarded_xcms.len(), 1);
877				assert_eq!(result.forwarded_xcms[0].0, $crate::macros::VersionedLocation::from(
878					$crate::macros::Location::new(1, [$crate::macros::Parachain(
879						<$sender_bridge_hub as $crate::macros::Para>::para_id().into())])));
880			});
881		}
882	};
883}
884
885#[macro_export]
886macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub {
887	( $asset_hub:ty ) => {
888		$crate::macros::paste::paste! {
889
890			<$asset_hub as $crate::macros::TestExt>::execute_with(|| {
891				// Setup a pool between USDT and WND.
892				type RuntimeOrigin = <$asset_hub as $crate::macros::Chain>::RuntimeOrigin;
893				type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets;
894				type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion;
895				let wnd = $crate::macros::Location::new(1, []);
896				let usdt = $crate::macros::Location::new(0, [$crate::macros::PalletInstance($crate::macros::ASSETS_PALLET_ID),
897					$crate::macros::GeneralIndex($crate::macros::USDT_ID.into())]);
898				let sender = [<$asset_hub Sender>]::get();
899				$crate::macros::assert_ok!(AssetConversion::create_pool(
900					RuntimeOrigin::signed(sender.clone()),
901					Box::new(wnd.clone()),
902					Box::new(usdt.clone()),
903				));
904
905				type Runtime = <$asset_hub as $crate::macros::Chain>::Runtime;
906				let acceptable_payment_assets = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_acceptable_payment_assets(
907					$crate::macros::XCM_VERSION).unwrap();
908				assert_eq!(acceptable_payment_assets, vec![
909					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(wnd.clone())),
910					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt.clone())),
911				]);
912
913				let program = $crate::macros::Xcm::<()>::builder()
914					.withdraw_asset(($crate::macros::Parent, 100u128))
915					.buy_execution(($crate::macros::Parent, 10u128), $crate::macros::Unlimited)
916					.deposit_asset($crate::macros::All, [0u8; 32])
917					.build();
918				let weight = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_xcm_weight(
919					$crate::macros::VersionedXcm::from(program)).unwrap();
920				let fee_in_wnd = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
921					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(wnd.clone()))).unwrap();
922				// Assets not in a pool don't work.
923				assert!(<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
924					$crate::macros::VersionedAssetId::from(
925						$crate::macros::AssetId($crate::macros::Location::new(0,
926							[$crate::macros::PalletInstance($crate::macros::ASSETS_PALLET_ID),
927								$crate::macros::GeneralIndex(1)]
928							)
929						)
930					)
931				).is_err());
932				let fee_in_usdt_fail = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
933					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt.clone())));
934				// Weight to asset fee fails because there's not enough asset in the pool.
935				// We just created it, there's none.
936				assert_eq!(fee_in_usdt_fail, Err($crate::macros::XcmPaymentApiError::AssetNotFound));
937				// We add some.
938				$crate::macros::assert_ok!(Assets::mint(
939					RuntimeOrigin::signed(sender.clone()),
940					$crate::macros::USDT_ID.into(),
941					sender.clone().into(),
942					5_000_000_000_000
943				));
944				// We make 1 WND = 4 USDT.
945				$crate::macros::assert_ok!(AssetConversion::add_liquidity(
946					RuntimeOrigin::signed(sender.clone()),
947					Box::new(wnd),
948					Box::new(usdt.clone()),
949					1_000_000_000_000,
950					4_000_000_000_000,
951					0,
952					0,
953					sender.into()
954				));
955				// Now it works.
956				let fee_in_usdt = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
957					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt))
958				);
959				$crate::macros::assert_ok!(fee_in_usdt);
960				assert!(fee_in_usdt.unwrap() > fee_in_wnd);
961			});
962		}
963	};
964}
965
966#[macro_export]
967macro_rules! test_cross_chain_alias {
968	( vec![$( ($sender_para:ty, $receiver_para:ty, $is_teleport:expr, $expected_success:expr) ),+], $origin:expr, $target:expr, $fees:expr ) => {
969		$crate::macros::paste::paste! {
970			$(
971				{
972					let para_destination = <$sender_para as $crate::macros::Para>::sibling_location_of(
973						<$receiver_para as $crate::macros::Para>::para_id());
974					let account: $crate::macros::AccountId = $origin.clone().into();
975					$sender_para::fund_accounts(vec![(account.clone(), $fees * 10)]);
976					let total_fees: $crate::macros::Asset = ($crate::macros::Location::parent(), $fees).into();
977					let fees: $crate::macros::Asset = ($crate::macros::Location::parent(), $fees / 2).into();
978
979					let remote_fees = if $is_teleport {
980						Some($crate::macros::AssetTransferFilter::Teleport(fees.clone().into()))
981					} else {
982						let source_para_sa = <$receiver_para as $crate::macros::Para>::sovereign_account_id_of(
983							<$receiver_para as $crate::macros::Para>::sibling_location_of(
984								<$sender_para as $crate::macros::Para>::para_id()),
985						);
986						$receiver_para::fund_accounts(vec![(source_para_sa, $fees * 10)]);
987						Some($crate::macros::AssetTransferFilter::ReserveWithdraw(fees.clone().into()))
988					};
989					<$sender_para as $crate::macros::TestExt>::execute_with(|| {
990						type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
991						let xcm_message = $crate::macros::Xcm::<()>(vec![
992							$crate::macros::WithdrawAsset(total_fees.into()),
993							$crate::macros::PayFees { asset: fees.clone() },
994							$crate::macros::InitiateTransfer {
995								destination: para_destination,
996								remote_fees,
997								preserve_origin: true,
998								assets: $crate::macros::BoundedVec::new(),
999								remote_xcm: $crate::macros::Xcm(vec![
1000									// try to alias into `account`
1001									$crate::macros::AliasOrigin($target.clone().into()),
1002									$crate::macros::RefundSurplus,
1003									$crate::macros::DepositAsset {
1004										assets: $crate::macros::Wild($crate::macros::AllCounted(1)),
1005										beneficiary: $target.clone().into(),
1006									},
1007								]),
1008							},
1009							$crate::macros::RefundSurplus,
1010							$crate::macros::DepositAsset { assets: $crate::macros::Wild($crate::macros::AllCounted(1)),
1011								beneficiary: account.clone().into() },
1012						]);
1013
1014						let signed_origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(account.into());
1015						$crate::macros::assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::execute(
1016							signed_origin,
1017							Box::new($crate::macros::VersionedXcm::from(xcm_message.into())),
1018							$crate::macros::Weight::MAX
1019						));
1020						$crate::macros::assert_expected_events!(
1021							$sender_para,
1022							vec![
1023								RuntimeEvent::PolkadotXcm($crate::macros::pallet_xcm::Event::Sent { .. }) => {},
1024							]
1025						);
1026					});
1027
1028					<$receiver_para as $crate::macros::TestExt>::execute_with(|| {
1029						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
1030						$crate::macros::assert_expected_events!(
1031							$receiver_para,
1032							vec![
1033								RuntimeEvent::MessageQueue($crate::macros::pallet_message_queue::Event::Processed {
1034									success, ..
1035								}) => { success: *success == $expected_success, },
1036							]
1037						);
1038					});
1039				}
1040			)+
1041		}
1042	};
1043}
1044
1045/// note: $asset needs to be prefunded outside this function
1046#[macro_export]
1047macro_rules! create_pool_with_native_on {
1048	( $chain:ident, $asset:expr, $is_foreign:expr, $asset_owner:expr ) => {
1049		$crate::create_pool_with_native_on!(
1050			$chain,
1051			$asset,
1052			$is_foreign,
1053			$asset_owner,
1054			1_000_000_000_000,
1055			2_000_000_000_000
1056		);
1057	};
1058
1059	( $chain:ident, $asset:expr, $is_foreign:expr, $asset_owner:expr, $native_amount:expr, $asset_amount:expr ) => {
1060		$crate::macros::paste::paste! {
1061			<$chain as $crate::macros::TestExt>::execute_with(|| {
1062				type RuntimeEvent = <$chain as $crate::macros::Chain>::RuntimeEvent;
1063				let owner = $asset_owner;
1064				let signed_owner = <$chain as $crate::macros::Chain>::RuntimeOrigin::signed(owner.clone());
1065				let native_asset: $crate::macros::Location = $crate::macros::Parent.into();
1066
1067				if $is_foreign {
1068					$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::ForeignAssets::mint(
1069						signed_owner.clone(),
1070						$asset.clone().into(),
1071						owner.clone().into(),
1072						10_000_000_000_000, // For it to have more than enough.
1073					));
1074				} else {
1075					let asset_id = match $asset.interior.last() {
1076						Some($crate::macros::GeneralIndex(id)) => *id as u32,
1077						_ => unreachable!(),
1078					};
1079					$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::Assets::mint(
1080						signed_owner.clone(),
1081						asset_id.into(),
1082						owner.clone().into(),
1083						10_000_000_000_000, // For it to have more than enough.
1084					));
1085				}
1086
1087				$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::create_pool(
1088					signed_owner.clone(),
1089					Box::new(native_asset.clone()),
1090					Box::new($asset.clone()),
1091				));
1092
1093				$crate::macros::assert_expected_events!(
1094					$chain,
1095					vec![
1096						RuntimeEvent::AssetConversion($crate::macros::pallet_asset_conversion::Event::PoolCreated { .. }) => {},
1097					]
1098				);
1099
1100				$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::add_liquidity(
1101					signed_owner,
1102					Box::new(native_asset),
1103					Box::new($asset),
1104					$native_amount,
1105					$asset_amount,
1106					0,
1107					0,
1108					owner.into()
1109				));
1110
1111				$crate::macros::assert_expected_events!(
1112					$chain,
1113					vec![
1114						RuntimeEvent::AssetConversion($crate::macros::pallet_asset_conversion::Event::LiquidityAdded { .. }) => {},
1115					]
1116				);
1117			});
1118		}
1119	};
1120}
1121
1122#[macro_export]
1123macro_rules! assert_whitelisted {
1124    ($chain:ident, $expected_call_hash:expr) => {
1125		type RuntimeEvent = <$chain as $crate::macros::Chain>::RuntimeEvent;
1126		$crate::macros::assert_expected_events!(
1127			$chain,
1128			vec![
1129				RuntimeEvent::Whitelist($crate::macros::pallet_whitelist::Event::CallWhitelisted { call_hash }) => {
1130						call_hash: *call_hash == $expected_call_hash,
1131				},
1132			]
1133		);
1134    };
1135}