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;
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 call = RuntimeCall::PolkadotXcm($crate::macros::pallet_xcm::Call::transfer_assets {
855					dest: Box::new($crate::macros::VersionedLocation::from($destination)),
856					beneficiary: Box::new($crate::macros::VersionedLocation::from($crate::macros::Junction::AccountId32 {
857						id: who.clone().into(),
858						network: None,
859					})),
860					assets: Box::new($crate::macros::VersionedAssets::from(vec![
861						($crate::macros::Parent, transfer_amount).into(),
862					])),
863					fee_asset_item: 0,
864					weight_limit: $crate::macros::Unlimited,
865				});
866				let origin = OriginCaller::system($crate::macros::RawOrigin::Signed(who));
867				let result = <Runtime as $crate::macros::DryRunApiV2<_,_,_,_>>::dry_run_call(origin, call.clone(),
868					$crate::macros::XCM_VERSION).unwrap();
869
870				// We assert the dry run succeeds and sends only one message to the local bridge hub.
871				assert!(result.execution_result.is_ok());
872				assert_eq!(result.forwarded_xcms.len(), 1);
873				assert_eq!(result.forwarded_xcms[0].0, $crate::macros::VersionedLocation::from(
874					$crate::macros::Location::new(1, [$crate::macros::Parachain(
875						<$sender_bridge_hub as $crate::macros::Para>::para_id().into())])));
876			});
877		}
878	};
879}
880
881#[macro_export]
882macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub {
883	( $asset_hub:ty ) => {
884		$crate::macros::paste::paste! {
885
886			<$asset_hub as $crate::macros::TestExt>::execute_with(|| {
887				// Setup a pool between USDT and WND.
888				type RuntimeOrigin = <$asset_hub as $crate::macros::Chain>::RuntimeOrigin;
889				type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets;
890				type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion;
891				let wnd = $crate::macros::Location::new(1, []);
892				let usdt = $crate::macros::Location::new(0, [$crate::macros::PalletInstance($crate::macros::ASSETS_PALLET_ID),
893					$crate::macros::GeneralIndex($crate::macros::USDT_ID.into())]);
894				let sender = [<$asset_hub Sender>]::get();
895				$crate::macros::assert_ok!(AssetConversion::create_pool(
896					RuntimeOrigin::signed(sender.clone()),
897					Box::new(wnd.clone()),
898					Box::new(usdt.clone()),
899				));
900
901				type Runtime = <$asset_hub as $crate::macros::Chain>::Runtime;
902				let acceptable_payment_assets = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_acceptable_payment_assets(
903					$crate::macros::XCM_VERSION).unwrap();
904				assert_eq!(acceptable_payment_assets, vec![
905					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(wnd.clone())),
906					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt.clone())),
907				]);
908
909				let program = $crate::macros::Xcm::<()>::builder()
910					.withdraw_asset(($crate::macros::Parent, 100u128))
911					.buy_execution(($crate::macros::Parent, 10u128), $crate::macros::Unlimited)
912					.deposit_asset($crate::macros::All, [0u8; 32])
913					.build();
914				let weight = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_xcm_weight(
915					$crate::macros::VersionedXcm::from(program)).unwrap();
916				let fee_in_wnd = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
917					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(wnd.clone()))).unwrap();
918				// Assets not in a pool don't work.
919				assert!(<Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
920					$crate::macros::VersionedAssetId::from(
921						$crate::macros::AssetId($crate::macros::Location::new(0,
922							[$crate::macros::PalletInstance($crate::macros::ASSETS_PALLET_ID),
923								$crate::macros::GeneralIndex(1)]
924							)
925						)
926					)
927				).is_err());
928				let fee_in_usdt_fail = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
929					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt.clone())));
930				// Weight to asset fee fails because there's not enough asset in the pool.
931				// We just created it, there's none.
932				assert_eq!(fee_in_usdt_fail, Err($crate::macros::XcmPaymentApiError::AssetNotFound));
933				// We add some.
934				$crate::macros::assert_ok!(Assets::mint(
935					RuntimeOrigin::signed(sender.clone()),
936					$crate::macros::USDT_ID.into(),
937					sender.clone().into(),
938					5_000_000_000_000
939				));
940				// We make 1 WND = 4 USDT.
941				$crate::macros::assert_ok!(AssetConversion::add_liquidity(
942					RuntimeOrigin::signed(sender.clone()),
943					Box::new(wnd),
944					Box::new(usdt.clone()),
945					1_000_000_000_000,
946					4_000_000_000_000,
947					0,
948					0,
949					sender.into()
950				));
951				// Now it works.
952				let fee_in_usdt = <Runtime as $crate::macros::XcmPaymentApiV1<_>>::query_weight_to_asset_fee(weight,
953					$crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt))
954				);
955				$crate::macros::assert_ok!(fee_in_usdt);
956				assert!(fee_in_usdt.unwrap() > fee_in_wnd);
957			});
958		}
959	};
960}
961
962#[macro_export]
963macro_rules! test_cross_chain_alias {
964	( vec![$( ($sender_para:ty, $receiver_para:ty, $is_teleport:expr, $expected_success:expr) ),+], $origin:expr, $target:expr, $fees:expr ) => {
965		$crate::macros::paste::paste! {
966			$(
967				{
968					let para_destination = <$sender_para as $crate::macros::Para>::sibling_location_of(
969						<$receiver_para as $crate::macros::Para>::para_id());
970					let account: $crate::macros::AccountId = $origin.clone().into();
971					$sender_para::fund_accounts(vec![(account.clone(), $fees * 10)]);
972					let total_fees: $crate::macros::Asset = ($crate::macros::Location::parent(), $fees).into();
973					let fees: $crate::macros::Asset = ($crate::macros::Location::parent(), $fees / 2).into();
974
975					let remote_fees = if $is_teleport {
976						Some($crate::macros::AssetTransferFilter::Teleport(fees.clone().into()))
977					} else {
978						let source_para_sa = <$receiver_para as $crate::macros::Para>::sovereign_account_id_of(
979							<$receiver_para as $crate::macros::Para>::sibling_location_of(
980								<$sender_para as $crate::macros::Para>::para_id()),
981						);
982						$receiver_para::fund_accounts(vec![(source_para_sa, $fees * 10)]);
983						Some($crate::macros::AssetTransferFilter::ReserveWithdraw(fees.clone().into()))
984					};
985					<$sender_para as $crate::macros::TestExt>::execute_with(|| {
986						type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
987						let xcm_message = $crate::macros::Xcm::<()>(vec![
988							$crate::macros::WithdrawAsset(total_fees.into()),
989							$crate::macros::PayFees { asset: fees.clone() },
990							$crate::macros::InitiateTransfer {
991								destination: para_destination,
992								remote_fees,
993								preserve_origin: true,
994								assets: $crate::macros::BoundedVec::new(),
995								remote_xcm: $crate::macros::Xcm(vec![
996									// try to alias into `account`
997									$crate::macros::AliasOrigin($target.clone().into()),
998									$crate::macros::RefundSurplus,
999									$crate::macros::DepositAsset {
1000										assets: $crate::macros::Wild($crate::macros::AllCounted(1)),
1001										beneficiary: $target.clone().into(),
1002									},
1003								]),
1004							},
1005							$crate::macros::RefundSurplus,
1006							$crate::macros::DepositAsset { assets: $crate::macros::Wild($crate::macros::AllCounted(1)),
1007								beneficiary: account.clone().into() },
1008						]);
1009
1010						let signed_origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(account.into());
1011						$crate::macros::assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::execute(
1012							signed_origin,
1013							Box::new($crate::macros::VersionedXcm::from(xcm_message.into())),
1014							$crate::macros::Weight::MAX
1015						));
1016						$crate::macros::assert_expected_events!(
1017							$sender_para,
1018							vec![
1019								RuntimeEvent::PolkadotXcm($crate::macros::pallet_xcm::Event::Sent { .. }) => {},
1020							]
1021						);
1022					});
1023
1024					<$receiver_para as $crate::macros::TestExt>::execute_with(|| {
1025						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
1026						$crate::macros::assert_expected_events!(
1027							$receiver_para,
1028							vec![
1029								RuntimeEvent::MessageQueue($crate::macros::pallet_message_queue::Event::Processed {
1030									success, ..
1031								}) => { success: *success == $expected_success, },
1032							]
1033						);
1034					});
1035				}
1036			)+
1037		}
1038	};
1039}
1040
1041/// note: $asset needs to be prefunded outside this function
1042#[macro_export]
1043macro_rules! create_pool_with_native_on {
1044	( $chain:ident, $asset:expr, $is_foreign:expr, $asset_owner:expr ) => {
1045		$crate::create_pool_with_native_on!(
1046			$chain,
1047			$asset,
1048			$is_foreign,
1049			$asset_owner,
1050			1_000_000_000_000,
1051			2_000_000_000_000
1052		);
1053	};
1054
1055	( $chain:ident, $asset:expr, $is_foreign:expr, $asset_owner:expr, $native_amount:expr, $asset_amount:expr ) => {
1056		$crate::macros::paste::paste! {
1057			<$chain as $crate::macros::TestExt>::execute_with(|| {
1058				type RuntimeEvent = <$chain as $crate::macros::Chain>::RuntimeEvent;
1059				let owner = $asset_owner;
1060				let signed_owner = <$chain as $crate::macros::Chain>::RuntimeOrigin::signed(owner.clone());
1061				let native_asset: $crate::macros::Location = $crate::macros::Parent.into();
1062
1063				if $is_foreign {
1064					$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::ForeignAssets::mint(
1065						signed_owner.clone(),
1066						$asset.clone().into(),
1067						owner.clone().into(),
1068						10_000_000_000_000, // For it to have more than enough.
1069					));
1070				} else {
1071					let asset_id = match $asset.interior.last() {
1072						Some($crate::macros::GeneralIndex(id)) => *id as u32,
1073						_ => unreachable!(),
1074					};
1075					$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::Assets::mint(
1076						signed_owner.clone(),
1077						asset_id.into(),
1078						owner.clone().into(),
1079						10_000_000_000_000, // For it to have more than enough.
1080					));
1081				}
1082
1083				$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::create_pool(
1084					signed_owner.clone(),
1085					Box::new(native_asset.clone()),
1086					Box::new($asset.clone()),
1087				));
1088
1089				$crate::macros::assert_expected_events!(
1090					$chain,
1091					vec![
1092						RuntimeEvent::AssetConversion($crate::macros::pallet_asset_conversion::Event::PoolCreated { .. }) => {},
1093					]
1094				);
1095
1096				$crate::macros::assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::add_liquidity(
1097					signed_owner,
1098					Box::new(native_asset),
1099					Box::new($asset),
1100					$native_amount,
1101					$asset_amount,
1102					0,
1103					0,
1104					owner.into()
1105				));
1106
1107				$crate::macros::assert_expected_events!(
1108					$chain,
1109					vec![
1110						RuntimeEvent::AssetConversion($crate::macros::pallet_asset_conversion::Event::LiquidityAdded { .. }) => {},
1111					]
1112				);
1113			});
1114		}
1115	};
1116}
1117
1118#[macro_export]
1119macro_rules! assert_whitelisted {
1120    ($chain:ident, $expected_call_hash:expr) => {
1121		type RuntimeEvent = <$chain as $crate::macros::Chain>::RuntimeEvent;
1122		$crate::macros::assert_expected_events!(
1123			$chain,
1124			vec![
1125				RuntimeEvent::Whitelist($crate::macros::pallet_whitelist::Event::CallWhitelisted { call_hash }) => {
1126						call_hash: *call_hash == $expected_call_hash,
1127				},
1128			]
1129		);
1130    };
1131}