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