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