emulated_integration_tests_common/
macros.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16pub use paste;
17
18// Substrate
19pub use frame_support::{pallet_prelude::Weight, weights::WeightToFee};
20pub use pallet_assets;
21pub use pallet_balances;
22pub use pallet_message_queue;
23pub use pallet_xcm;
24
25// Polkadot
26pub use xcm::{
27	prelude::{
28		AccountId32, All, Asset, AssetId, BuyExecution, DepositAsset, ExpectTransactStatus,
29		Fungible, Here, Location, MaybeErrorCode, OriginKind, RefundSurplus, Transact, Unlimited,
30		VersionedAssets, VersionedXcm, WeightLimit, WithdrawAsset, Xcm,
31	},
32	v3::Location as V3Location,
33};
34
35// Cumulus
36pub use asset_test_utils;
37pub use cumulus_pallet_xcmp_queue;
38pub use parachains_common::AccountId;
39pub use xcm_emulator::Chain;
40
41#[macro_export]
42macro_rules! test_parachain_is_trusted_teleporter {
43	( $sender_para:ty, $sender_xcm_config:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr) ) => {
44		$crate::macros::paste::paste! {
45			// init Origin variables
46			let sender = [<$sender_para Sender>]::get();
47			let mut para_sender_balance_before =
48				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
49			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
50			let fee_asset_item = 0;
51			let weight_limit = $crate::macros::WeightLimit::Unlimited;
52
53			$(
54				{
55					// init Destination variables
56					let receiver = [<$receiver_para Receiver>]::get();
57					let para_receiver_balance_before =
58						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
59					let para_destination =
60						<$sender_para>::sibling_location_of(<$receiver_para>::para_id());
61					let beneficiary: Location =
62						$crate::macros::AccountId32 { network: None, id: receiver.clone().into() }.into();
63
64					// Send XCM message from Origin Parachain
65					// We are only testing the limited teleport version, which should be ok since success will
66					// depend only on a proper `XcmConfig` at destination.
67					<$sender_para>::execute_with(|| {
68						assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::limited_teleport_assets(
69							origin.clone(),
70							bx!(para_destination.clone().into()),
71							bx!(beneficiary.clone().into()),
72							bx!($assets.clone().into()),
73							fee_asset_item,
74							weight_limit.clone(),
75						));
76
77						type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
78
79						assert_expected_events!(
80							$sender_para,
81							vec![
82								RuntimeEvent::PolkadotXcm(
83									$crate::macros::pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
84								) => {},
85								RuntimeEvent::XcmpQueue(
86									$crate::macros::cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
87								) => {},
88								RuntimeEvent::Balances(
89									$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
90								) => {},
91							]
92						);
93					});
94
95					// Receive XCM message in Destination Parachain
96					<$receiver_para>::execute_with(|| {
97						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
98
99						assert_expected_events!(
100							$receiver_para,
101							vec![
102								RuntimeEvent::Balances(
103									$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
104								) => {},
105								RuntimeEvent::MessageQueue(
106									$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
107								) => {},
108							]
109						);
110					});
111
112					// Check if balances are updated accordingly in Origin and Destination Parachains
113					let para_sender_balance_after =
114						<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
115					let para_receiver_balance_after =
116						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
117					let delivery_fees = <$sender_para>::execute_with(|| {
118						$crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::<
119							<$sender_xcm_config as xcm_executor::Config>::XcmSender,
120						>($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination)
121					});
122
123					assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after);
124					assert!(para_receiver_balance_after > para_receiver_balance_before);
125
126					// Update sender balance
127					para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
128				}
129			)+
130		}
131	};
132}
133
134#[macro_export]
135macro_rules! test_relay_is_trusted_teleporter {
136	( $sender_relay:ty, $sender_xcm_config:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr) ) => {
137		$crate::macros::paste::paste! {
138			// init Origin variables
139			let sender = [<$sender_relay Sender>]::get();
140			let mut relay_sender_balance_before =
141				<$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
142			let origin = <$sender_relay as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
143			let fee_asset_item = 0;
144			let weight_limit = $crate::macros::WeightLimit::Unlimited;
145
146			$(
147				{
148					// init Destination variables
149					let receiver = [<$receiver_para Receiver>]::get();
150					let para_receiver_balance_before =
151						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
152					let para_destination =
153						<$sender_relay>::child_location_of(<$receiver_para>::para_id());
154					let beneficiary: Location =
155						$crate::macros::AccountId32 { network: None, id: receiver.clone().into() }.into();
156
157					// Send XCM message from Relay
158					<$sender_relay>::execute_with(|| {
159						assert_ok!(<$sender_relay as [<$sender_relay Pallet>]>::XcmPallet::limited_teleport_assets(
160							origin.clone(),
161							bx!(para_destination.clone().into()),
162							bx!(beneficiary.clone().into()),
163							bx!($assets.clone().into()),
164							fee_asset_item,
165							weight_limit.clone(),
166						));
167
168						type RuntimeEvent = <$sender_relay as $crate::macros::Chain>::RuntimeEvent;
169
170						assert_expected_events!(
171							$sender_relay,
172							vec![
173								RuntimeEvent::XcmPallet(
174									$crate::macros::pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
175								) => {},
176								RuntimeEvent::Balances(
177									$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
178								) => {},
179								RuntimeEvent::XcmPallet(
180									$crate::macros::pallet_xcm::Event::Sent { .. }
181								) => {},
182							]
183						);
184					});
185
186					// Receive XCM message in Destination Parachain
187					<$receiver_para>::execute_with(|| {
188						type RuntimeEvent = <$receiver_para as $crate::macros::Chain>::RuntimeEvent;
189
190						assert_expected_events!(
191							$receiver_para,
192							vec![
193								RuntimeEvent::Balances(
194									$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
195								) => {},
196								RuntimeEvent::MessageQueue(
197									$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
198								) => {},
199							]
200						);
201					});
202
203					// Check if balances are updated accordingly in Origin and Parachain
204					let relay_sender_balance_after =
205						<$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
206					let para_receiver_balance_after =
207						<$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
208					let delivery_fees = <$sender_relay>::execute_with(|| {
209						$crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::<
210							<$sender_xcm_config as xcm_executor::Config>::XcmSender,
211						>($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination)
212					});
213
214					assert_eq!(relay_sender_balance_before - $amount - delivery_fees, relay_sender_balance_after);
215					assert!(para_receiver_balance_after > para_receiver_balance_before);
216
217					// Update sender balance
218					relay_sender_balance_before = <$sender_relay as $crate::macros::Chain>::account_data_of(sender.clone()).free;
219				}
220			)+
221		}
222	};
223}
224
225#[macro_export]
226macro_rules! test_parachain_is_trusted_teleporter_for_relay {
227	( $sender_para:ty, $sender_xcm_config:ty, $receiver_relay:ty, $amount:expr ) => {
228		$crate::macros::paste::paste! {
229			// init Origin variables
230			let sender = [<$sender_para Sender>]::get();
231			let para_sender_balance_before =
232				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
233			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
234			let assets: Assets = (Parent, $amount).into();
235			let fee_asset_item = 0;
236			let weight_limit = $crate::macros::WeightLimit::Unlimited;
237
238			// init Destination variables
239			let receiver = [<$receiver_relay Receiver>]::get();
240			let relay_receiver_balance_before =
241				<$receiver_relay as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
242			let relay_destination: Location = Parent.into();
243			let beneficiary: Location =
244				$crate::macros::AccountId32 { network: None, id: receiver.clone().into() }.into();
245
246			// Send XCM message from Parachain
247			<$sender_para>::execute_with(|| {
248				assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::limited_teleport_assets(
249					origin.clone(),
250					bx!(relay_destination.clone().into()),
251					bx!(beneficiary.clone().into()),
252					bx!(assets.clone().into()),
253					fee_asset_item,
254					weight_limit.clone(),
255				));
256
257				type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
258
259				assert_expected_events!(
260					$sender_para,
261					vec![
262						RuntimeEvent::PolkadotXcm(
263							$crate::macros::pallet_xcm::Event::Attempted { outcome: Outcome::Complete { .. } }
264						) => {},
265						RuntimeEvent::Balances(
266							$crate::macros::pallet_balances::Event::Burned { who: sender, amount }
267						) => {},
268						RuntimeEvent::PolkadotXcm(
269							$crate::macros::pallet_xcm::Event::Sent { .. }
270						) => {},
271					]
272				);
273			});
274
275			// Receive XCM message in Destination Parachain
276			<$receiver_relay>::execute_with(|| {
277				type RuntimeEvent = <$receiver_relay as $crate::macros::Chain>::RuntimeEvent;
278
279				assert_expected_events!(
280					$receiver_relay,
281					vec![
282						RuntimeEvent::Balances(
283							$crate::macros::pallet_balances::Event::Minted { who: receiver, .. }
284						) => {},
285						RuntimeEvent::MessageQueue(
286							$crate::macros::pallet_message_queue::Event::Processed { success: true, .. }
287						) => {},
288					]
289				);
290			});
291
292			// Check if balances are updated accordingly in Origin and Relay Chain
293			let para_sender_balance_after =
294				<$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free;
295			let relay_receiver_balance_after =
296				<$receiver_relay as $crate::macros::Chain>::account_data_of(receiver.clone()).free;
297			let delivery_fees = <$sender_para>::execute_with(|| {
298				$crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::<
299					<$sender_xcm_config as xcm_executor::Config>::XcmSender,
300				>(assets, fee_asset_item, weight_limit.clone(), beneficiary, relay_destination)
301			});
302
303			assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after);
304			assert!(relay_receiver_balance_after > relay_receiver_balance_before);
305		}
306	};
307}
308
309#[macro_export]
310macro_rules! test_chain_can_claim_assets {
311	( $sender_para:ty, $runtime_call:ty, $network_id:expr, $assets:expr, $amount:expr ) => {
312		$crate::macros::paste::paste! {
313			let sender = [<$sender_para Sender>]::get();
314			let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone());
315			// Receiver is the same as sender
316			let beneficiary: Location =
317				$crate::macros::AccountId32 { network: Some($network_id), id: sender.clone().into() }.into();
318			let versioned_assets: $crate::macros::VersionedAssets = $assets.clone().into();
319
320			<$sender_para>::execute_with(|| {
321				// Assets are trapped for whatever reason.
322				// The possible reasons for this might differ from runtime to runtime, so here we just drop them directly.
323				<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::drop_assets(
324					&beneficiary,
325					$assets.clone().into(),
326					&XcmContext { origin: None, message_id: [0u8; 32], topic: None },
327				);
328
329				type RuntimeEvent = <$sender_para as $crate::macros::Chain>::RuntimeEvent;
330				assert_expected_events!(
331					$sender_para,
332					vec![
333						RuntimeEvent::PolkadotXcm(
334							$crate::macros::pallet_xcm::Event::AssetsTrapped { origin: beneficiary, assets: versioned_assets, .. }
335						) => {},
336					]
337				);
338
339				let balance_before = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&sender);
340
341				// Different origin or different assets won't work.
342				let other_origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed([<$sender_para Receiver>]::get());
343				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
344					other_origin,
345					bx!(versioned_assets.clone().into()),
346					bx!(beneficiary.clone().into()),
347				).is_err());
348				let other_versioned_assets: $crate::macros::VersionedAssets = Assets::new().into();
349				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
350					origin.clone(),
351					bx!(other_versioned_assets.into()),
352					bx!(beneficiary.clone().into()),
353				).is_err());
354
355				// Assets will be claimed to `beneficiary`, which is the same as `sender`.
356				assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
357					origin.clone(),
358					bx!(versioned_assets.clone().into()),
359					bx!(beneficiary.clone().into()),
360				));
361
362				assert_expected_events!(
363					$sender_para,
364					vec![
365						RuntimeEvent::PolkadotXcm(
366							$crate::macros::pallet_xcm::Event::AssetsClaimed { origin: beneficiary, assets: versioned_assets, .. }
367						) => {},
368					]
369				);
370
371				// After claiming the assets, the balance has increased.
372				let balance_after = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&sender);
373				assert_eq!(balance_after, balance_before + $amount);
374
375				// Claiming the assets again doesn't work.
376				assert!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
377					origin.clone(),
378					bx!(versioned_assets.clone().into()),
379					bx!(beneficiary.clone().into()),
380				).is_err());
381
382				let balance = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&sender);
383				assert_eq!(balance, balance_after);
384
385				// You can also claim assets and send them to a different account.
386				<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::drop_assets(
387					&beneficiary,
388					$assets.clone().into(),
389					&XcmContext { origin: None, message_id: [0u8; 32], topic: None },
390				);
391				let receiver = [<$sender_para Receiver>]::get();
392				let other_beneficiary: Location =
393					$crate::macros::AccountId32 { network: Some($network_id), id: receiver.clone().into() }.into();
394				let balance_before = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&receiver);
395				assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::claim_assets(
396					origin.clone(),
397					bx!(versioned_assets.clone().into()),
398					bx!(other_beneficiary.clone().into()),
399				));
400				let balance_after = <$sender_para as [<$sender_para Pallet>]>::Balances::free_balance(&receiver);
401				assert_eq!(balance_after, balance_before + $amount);
402			});
403		}
404	};
405}
406
407#[macro_export]
408macro_rules! test_dry_run_transfer_across_pk_bridge {
409	( $sender_asset_hub:ty, $sender_bridge_hub:ty, $destination:expr ) => {
410		$crate::macros::paste::paste! {
411			use frame_support::{dispatch::RawOrigin, traits::fungible};
412			use sp_runtime::AccountId32;
413			use xcm::prelude::*;
414			use xcm_runtime_apis::dry_run::runtime_decl_for_dry_run_api::DryRunApiV1;
415
416			let who = AccountId32::new([1u8; 32]);
417			let transfer_amount = 10_000_000_000_000u128;
418			let initial_balance = transfer_amount * 10;
419
420			// AssetHub setup.
421			$sender_asset_hub::force_xcm_version($destination, XCM_VERSION);
422
423			<$sender_asset_hub as TestExt>::execute_with(|| {
424				type Runtime = <$sender_asset_hub as Chain>::Runtime;
425				type RuntimeCall = <$sender_asset_hub as Chain>::RuntimeCall;
426				type OriginCaller = <$sender_asset_hub as Chain>::OriginCaller;
427				type Balances = <$sender_asset_hub as [<$sender_asset_hub Pallet>]>::Balances;
428
429				// Give some initial funds.
430				<Balances as fungible::Mutate<_>>::set_balance(&who, initial_balance);
431
432				let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets {
433					dest: Box::new(VersionedLocation::from($destination)),
434					beneficiary: Box::new(VersionedLocation::from(Junction::AccountId32 {
435						id: who.clone().into(),
436						network: None,
437					})),
438					assets: Box::new(VersionedAssets::from(vec![
439						(Parent, transfer_amount).into(),
440					])),
441					fee_asset_item: 0,
442					weight_limit: Unlimited,
443				});
444				let result = Runtime::dry_run_call(OriginCaller::system(RawOrigin::Signed(who)), call).unwrap();
445				// We assert the dry run succeeds and sends only one message to the local bridge hub.
446				assert!(result.execution_result.is_ok());
447				assert_eq!(result.forwarded_xcms.len(), 1);
448				assert_eq!(result.forwarded_xcms[0].0, VersionedLocation::from(Location::new(1, [Parachain($sender_bridge_hub::para_id().into())])));
449			});
450		}
451	};
452}
453
454#[macro_export]
455macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub {
456	( $asset_hub:ty ) => {
457		$crate::macros::paste::paste! {
458			use emulated_integration_tests_common::USDT_ID;
459			use xcm_runtime_apis::fees::{Error as XcmPaymentApiError, runtime_decl_for_xcm_payment_api::XcmPaymentApiV1};
460
461			$asset_hub::execute_with(|| {
462				// Setup a pool between USDT and WND.
463				type RuntimeOrigin = <$asset_hub as Chain>::RuntimeOrigin;
464				type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets;
465				type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion;
466				let wnd = Location::new(1, []);
467				let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
468				let sender = [<$asset_hub Sender>]::get();
469				assert_ok!(AssetConversion::create_pool(
470					RuntimeOrigin::signed(sender.clone()),
471					Box::new(wnd.clone()),
472					Box::new(usdt.clone()),
473				));
474
475				type Runtime = <$asset_hub as Chain>::Runtime;
476				let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(4).unwrap();
477				assert_eq!(acceptable_payment_assets, vec![
478					VersionedAssetId::from(AssetId(wnd.clone())),
479					VersionedAssetId::from(AssetId(usdt.clone())),
480				]);
481
482				let program = Xcm::<()>::builder()
483					.withdraw_asset((Parent, 100u128))
484					.buy_execution((Parent, 10u128), Unlimited)
485					.deposit_asset(All, [0u8; 32])
486					.build();
487				let weight = Runtime::query_xcm_weight(VersionedXcm::from(program)).unwrap();
488				let fee_in_wnd = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(wnd.clone()))).unwrap();
489				// Assets not in a pool don't work.
490				assert!(Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1)])))).is_err());
491				let fee_in_usdt_fail = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt.clone())));
492				// Weight to asset fee fails because there's not enough asset in the pool.
493				// We just created it, there's none.
494				assert_eq!(fee_in_usdt_fail, Err(XcmPaymentApiError::AssetNotFound));
495				// We add some.
496				assert_ok!(Assets::mint(
497					RuntimeOrigin::signed(sender.clone()),
498					USDT_ID.into(),
499					sender.clone().into(),
500					5_000_000_000_000
501				));
502				// We make 1 WND = 4 USDT.
503				assert_ok!(AssetConversion::add_liquidity(
504					RuntimeOrigin::signed(sender.clone()),
505					Box::new(wnd),
506					Box::new(usdt.clone()),
507					1_000_000_000_000,
508					4_000_000_000_000,
509					0,
510					0,
511					sender.into()
512				));
513				// Now it works.
514				let fee_in_usdt = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt)));
515				assert_ok!(fee_in_usdt);
516				assert!(fee_in_usdt.unwrap() > fee_in_wnd);
517			});
518		}
519	};
520}