1use super::xcm_helpers;
19use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
20use codec::Encode;
21use cumulus_primitives_core::XcmpMessageSource;
22use frame_support::{
23 assert_noop, assert_ok,
24 traits::{
25 fungible::Mutate, fungibles::InspectEnumerable, Currency, Get, OnFinalize, OnInitialize,
26 OriginTrait,
27 },
28 weights::Weight,
29};
30use frame_system::pallet_prelude::BlockNumberFor;
31use parachains_common::{AccountId, Balance};
32use parachains_runtimes_test_utils::{
33 assert_metadata, assert_total, mock_open_hrmp_channel, AccountIdOf, BalanceOf,
34 CollatorSessionKeys, ExtBuilder, SlotDurations, ValidatorIdOf, XcmReceivedFrom,
35};
36use sp_runtime::{
37 traits::{MaybeEquivalence, StaticLookup, Zero},
38 DispatchError, Saturating,
39};
40use xcm::{latest::prelude::*, VersionedAssets};
41use xcm_executor::{traits::ConvertLocation, XcmExecutor};
42
43type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
44 parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
45
46pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
48
49pub fn teleports_for_native_asset_works<
52 Runtime,
53 AllPalletsWithoutSystem,
54 XcmConfig,
55 CheckingAccount,
56 WeightToFee,
57 HrmpChannelOpener,
58>(
59 collator_session_keys: CollatorSessionKeys<Runtime>,
60 slot_durations: SlotDurations,
61 existential_deposit: BalanceOf<Runtime>,
62 target_account: AccountIdOf<Runtime>,
63 unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
64 runtime_para_id: u32,
65) where
66 Runtime: frame_system::Config
67 + pallet_balances::Config
68 + pallet_session::Config
69 + pallet_xcm::Config
70 + parachain_info::Config
71 + pallet_collator_selection::Config
72 + cumulus_pallet_parachain_system::Config
73 + cumulus_pallet_xcmp_queue::Config
74 + pallet_timestamp::Config,
75 AllPalletsWithoutSystem:
76 OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
77 AccountIdOf<Runtime>: Into<[u8; 32]>,
78 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
79 BalanceOf<Runtime>: From<Balance> + Into<u128>,
80 WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
81 <WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
82 <Runtime as frame_system::Config>::AccountId:
83 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
84 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
85 From<<Runtime as frame_system::Config>::AccountId>,
86 <Runtime as frame_system::Config>::AccountId: From<AccountId>,
87 XcmConfig: xcm_executor::Config,
88 CheckingAccount: Get<AccountIdOf<Runtime>>,
89 HrmpChannelOpener: frame_support::inherent::ProvideInherent<
90 Call = cumulus_pallet_parachain_system::Call<Runtime>,
91 >,
92{
93 ExtBuilder::<Runtime>::default()
94 .with_collators(collator_session_keys.collators())
95 .with_session_keys(collator_session_keys.session_keys())
96 .with_safe_xcm_version(XCM_VERSION)
97 .with_para_id(runtime_para_id.into())
98 .with_tracing()
99 .build()
100 .execute_with(|| {
101 let mut alice = [0u8; 32];
102 alice[0] = 1;
103
104 let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
105 2,
106 AccountId::from(alice).into(),
107 );
108 assert_eq!(<pallet_balances::Pallet<Runtime>>::free_balance(&target_account), 0.into());
110 assert_eq!(
111 <pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
112 0.into()
113 );
114
115 let native_asset_id = Location::parent();
116 let buy_execution_fee_amount_eta =
117 WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 1024));
118 let native_asset_amount_unit = existential_deposit;
119 let native_asset_amount_received =
120 native_asset_amount_unit * 10.into() + buy_execution_fee_amount_eta.into();
121
122 let xcm = Xcm(vec![
124 ReceiveTeleportedAsset(Assets::from(vec![Asset {
125 id: AssetId(native_asset_id.clone()),
126 fun: Fungible(native_asset_amount_received.into()),
127 }])),
128 ClearOrigin,
129 BuyExecution {
130 fees: Asset {
131 id: AssetId(native_asset_id.clone()),
132 fun: Fungible(buy_execution_fee_amount_eta),
133 },
134 weight_limit: Limited(Weight::from_parts(3035310000, 65536)),
135 },
136 DepositAsset {
137 assets: Wild(AllCounted(1)),
138 beneficiary: Location {
139 parents: 0,
140 interior: [AccountId32 {
141 network: None,
142 id: target_account.clone().into(),
143 }]
144 .into(),
145 },
146 },
147 ExpectTransactStatus(MaybeErrorCode::Success),
148 ]);
149
150 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
151
152 let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
153 Parent,
154 xcm,
155 &mut hash,
156 RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Parent),
157 Weight::zero(),
158 );
159 assert_ok!(outcome.ensure_complete());
160
161 assert_ne!(<pallet_balances::Pallet<Runtime>>::free_balance(&target_account), 0.into());
163 assert_eq!(
164 <pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
165 0.into()
166 );
167
168 {
170 let dest = Location::parent();
171 let mut dest_beneficiary = Location::parent()
172 .appended_with(AccountId32 {
173 network: None,
174 id: sp_runtime::AccountId32::new([3; 32]).into(),
175 })
176 .unwrap();
177 dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
178
179 let target_account_balance_before_teleport =
180 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
181 let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
182 assert!(
183 native_asset_to_teleport_away <
184 target_account_balance_before_teleport - existential_deposit
185 );
186
187 let delivery_fees =
189 xcm_helpers::teleport_assets_delivery_fees::<XcmConfig::XcmSender>(
190 (native_asset_id.clone(), native_asset_to_teleport_away.into()).into(),
191 0,
192 Unlimited,
193 dest_beneficiary.clone(),
194 dest.clone(),
195 );
196 <pallet_balances::Pallet<Runtime>>::mint_into(
197 &target_account,
198 delivery_fees.into(),
199 )
200 .unwrap();
201
202 assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
203 RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
204 dest,
205 dest_beneficiary,
206 (native_asset_id.clone(), native_asset_to_teleport_away.into()),
207 None,
208 included_head.clone(),
209 &alice,
210 &slot_durations,
211 ));
212
213 assert_eq!(
215 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
216 target_account_balance_before_teleport - native_asset_to_teleport_away
217 );
218 assert_eq!(
219 <pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
220 0.into()
221 );
222
223 RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
225 &unwrap_pallet_xcm_event,
226 |outcome| {
227 assert_ok!(outcome.ensure_complete());
228 },
229 );
230 }
231
232 {
235 let other_para_id = 2345;
236 let dest = Location::new(1, [Parachain(other_para_id)]);
237 let mut dest_beneficiary = Location::new(1, [Parachain(other_para_id)])
238 .appended_with(AccountId32 {
239 network: None,
240 id: sp_runtime::AccountId32::new([3; 32]).into(),
241 })
242 .unwrap();
243 dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
244
245 let target_account_balance_before_teleport =
246 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
247
248 let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
249 assert!(
250 native_asset_to_teleport_away <
251 target_account_balance_before_teleport - existential_deposit
252 );
253
254 assert_eq!(
255 RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
256 RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
257 dest,
258 dest_beneficiary,
259 (native_asset_id, native_asset_to_teleport_away.into()),
260 Some((runtime_para_id, other_para_id)),
261 included_head,
262 &alice,
263 &slot_durations,
264 ),
265 Err(DispatchError::Module(sp_runtime::ModuleError {
266 index: 31,
267 error: [2, 0, 0, 0,],
268 message: Some("Filtered",),
269 },),)
270 );
271
272 assert_eq!(
274 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
275 target_account_balance_before_teleport
276 );
277 assert_eq!(
278 <pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
279 0.into()
280 );
281 }
282 })
283}
284
285#[macro_export]
286macro_rules! include_teleports_for_native_asset_works(
287 (
288 $runtime:path,
289 $all_pallets_without_system:path,
290 $xcm_config:path,
291 $checking_account:path,
292 $weight_to_fee:path,
293 $hrmp_channel_opener:path,
294 $collator_session_key:expr,
295 $slot_durations:expr,
296 $existential_deposit:expr,
297 $unwrap_pallet_xcm_event:expr,
298 $runtime_para_id:expr
299 ) => {
300 #[test]
301 fn teleports_for_native_asset_works() {
302 const BOB: [u8; 32] = [2u8; 32];
303 let target_account = parachains_common::AccountId::from(BOB);
304
305 $crate::test_cases::teleports_for_native_asset_works::<
306 $runtime,
307 $all_pallets_without_system,
308 $xcm_config,
309 $checking_account,
310 $weight_to_fee,
311 $hrmp_channel_opener
312 >(
313 $collator_session_key,
314 $slot_durations,
315 $existential_deposit,
316 target_account,
317 $unwrap_pallet_xcm_event,
318 $runtime_para_id
319 )
320 }
321 }
322);
323
324pub fn teleports_for_foreign_assets_works<
327 Runtime,
328 AllPalletsWithoutSystem,
329 XcmConfig,
330 CheckingAccount,
331 WeightToFee,
332 HrmpChannelOpener,
333 SovereignAccountOf,
334 ForeignAssetsPalletInstance,
335>(
336 collator_session_keys: CollatorSessionKeys<Runtime>,
337 slot_durations: SlotDurations,
338 target_account: AccountIdOf<Runtime>,
339 existential_deposit: BalanceOf<Runtime>,
340 asset_owner: AccountIdOf<Runtime>,
341 unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
342 unwrap_xcmp_queue_event: Box<
343 dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
344 >,
345) where
346 Runtime: frame_system::Config
347 + pallet_balances::Config
348 + pallet_session::Config
349 + pallet_xcm::Config
350 + parachain_info::Config
351 + pallet_collator_selection::Config
352 + cumulus_pallet_parachain_system::Config
353 + cumulus_pallet_xcmp_queue::Config
354 + pallet_assets::Config<ForeignAssetsPalletInstance>
355 + pallet_timestamp::Config,
356 AllPalletsWithoutSystem:
357 OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
358 AccountIdOf<Runtime>: Into<[u8; 32]>,
359 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
360 BalanceOf<Runtime>: From<Balance>,
361 XcmConfig: xcm_executor::Config,
362 CheckingAccount: Get<AccountIdOf<Runtime>>,
363 HrmpChannelOpener: frame_support::inherent::ProvideInherent<
364 Call = cumulus_pallet_parachain_system::Call<Runtime>,
365 >,
366 WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
367 <WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
368 SovereignAccountOf: ConvertLocation<AccountIdOf<Runtime>>,
369 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
370 From<xcm::v5::Location> + Into<xcm::v5::Location>,
371 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
372 From<xcm::v5::Location> + Into<xcm::v5::Location>,
373 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
374 From<Balance> + Into<u128>,
375 <Runtime as frame_system::Config>::AccountId:
376 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
377 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
378 From<<Runtime as frame_system::Config>::AccountId>,
379 <Runtime as frame_system::Config>::AccountId: From<AccountId>,
380 ForeignAssetsPalletInstance: 'static,
381{
382 let foreign_para_id = 2222;
384 let foreign_asset_id_location = xcm::v5::Location {
385 parents: 1,
386 interior: [
387 xcm::v5::Junction::Parachain(foreign_para_id),
388 xcm::v5::Junction::GeneralIndex(1234567),
389 ]
390 .into(),
391 };
392
393 let foreign_creator = Location { parents: 1, interior: [Parachain(foreign_para_id)].into() };
395 let foreign_creator_as_account_id =
396 SovereignAccountOf::convert_location(&foreign_creator).expect("");
397
398 let buy_execution_fee_amount =
400 WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0));
401 let buy_execution_fee =
402 Asset { id: AssetId(Location::parent()), fun: Fungible(buy_execution_fee_amount) };
403
404 let teleported_foreign_asset_amount = 10_000_000_000_000;
405 let runtime_para_id = 1000;
406 ExtBuilder::<Runtime>::default()
407 .with_collators(collator_session_keys.collators())
408 .with_session_keys(collator_session_keys.session_keys())
409 .with_balances(vec![
410 (
411 foreign_creator_as_account_id,
412 existential_deposit + (buy_execution_fee_amount * 2).into(),
413 ),
414 (target_account.clone(), existential_deposit),
415 (CheckingAccount::get(), existential_deposit),
416 ])
417 .with_safe_xcm_version(XCM_VERSION)
418 .with_para_id(runtime_para_id.into())
419 .with_tracing()
420 .build()
421 .execute_with(|| {
422 let mut alice = [0u8; 32];
423 alice[0] = 1;
424
425 let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
426 2,
427 AccountId::from(alice).into(),
428 );
429 assert_eq!(
431 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
432 existential_deposit
433 );
434 assert_eq!(
436 <pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
437 existential_deposit
438 );
439 assert_eq!(
440 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
441 foreign_asset_id_location.clone().into(),
442 &target_account
443 ),
444 0.into()
445 );
446 assert_eq!(
447 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
448 foreign_asset_id_location.clone().into(),
449 &CheckingAccount::get()
450 ),
451 0.into()
452 );
453 assert_total::<
455 pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
456 AccountIdOf<Runtime>,
457 >(foreign_asset_id_location.clone(), 0, 0);
458
459 let asset_minimum_asset_balance = 3333333_u128;
461 assert_ok!(
462 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::force_create(
463 RuntimeHelper::<Runtime>::root_origin(),
464 foreign_asset_id_location.clone().into(),
465 asset_owner.into(),
466 false,
467 asset_minimum_asset_balance.into()
468 )
469 );
470 assert_total::<
471 pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
472 AccountIdOf<Runtime>,
473 >(foreign_asset_id_location.clone(), 0, 0);
474 assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance);
475
476 let xcm = Xcm(vec![
478 WithdrawAsset(buy_execution_fee.clone().into()),
480 BuyExecution {
481 fees: Asset {
482 id: AssetId(Location::parent()),
483 fun: Fungible(buy_execution_fee_amount),
484 },
485 weight_limit: Unlimited,
486 },
487 ReceiveTeleportedAsset(Assets::from(vec![Asset {
489 id: AssetId(foreign_asset_id_location.clone()),
490 fun: Fungible(teleported_foreign_asset_amount),
491 }])),
492 DepositAsset {
493 assets: Wild(AllOf {
494 id: AssetId(foreign_asset_id_location.clone()),
495 fun: WildFungibility::Fungible,
496 }),
497 beneficiary: Location {
498 parents: 0,
499 interior: [AccountId32 {
500 network: None,
501 id: target_account.clone().into(),
502 }]
503 .into(),
504 },
505 },
506 ExpectTransactStatus(MaybeErrorCode::Success),
507 ]);
508 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
509
510 let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
511 foreign_creator,
512 xcm,
513 &mut hash,
514 RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
515 Weight::zero(),
516 );
517 assert_ok!(outcome.ensure_complete());
518
519 assert_eq!(
521 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
522 existential_deposit
523 );
524 assert_eq!(
525 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
526 foreign_asset_id_location.clone().into(),
527 &target_account
528 ),
529 teleported_foreign_asset_amount.into()
530 );
531 assert_eq!(
533 <pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
534 existential_deposit
535 );
536 assert_eq!(
537 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
538 foreign_asset_id_location.clone().into(),
539 &CheckingAccount::get()
540 ),
541 0.into()
542 );
543 assert_total::<
545 pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
546 AccountIdOf<Runtime>,
547 >(
548 foreign_asset_id_location.clone(),
549 teleported_foreign_asset_amount,
550 teleported_foreign_asset_amount,
551 );
552
553 {
555 let dest = Location::new(1, [Parachain(foreign_para_id)]);
556 let mut dest_beneficiary = Location::new(1, [Parachain(foreign_para_id)])
557 .appended_with(AccountId32 {
558 network: None,
559 id: sp_runtime::AccountId32::new([3; 32]).into(),
560 })
561 .unwrap();
562 dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
563
564 let target_account_balance_before_teleport =
565 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
566 foreign_asset_id_location.clone().into(),
567 &target_account,
568 );
569 let asset_to_teleport_away = asset_minimum_asset_balance * 3;
570 assert!(
571 asset_to_teleport_away <
572 (target_account_balance_before_teleport -
573 asset_minimum_asset_balance.into())
574 .into()
575 );
576
577 let delivery_fees =
579 xcm_helpers::teleport_assets_delivery_fees::<XcmConfig::XcmSender>(
580 (foreign_asset_id_location.clone(), asset_to_teleport_away).into(),
581 0,
582 Unlimited,
583 dest_beneficiary.clone(),
584 dest.clone(),
585 );
586 <pallet_balances::Pallet<Runtime>>::mint_into(
587 &target_account,
588 delivery_fees.into(),
589 )
590 .unwrap();
591
592 assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
593 RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
594 dest,
595 dest_beneficiary,
596 (foreign_asset_id_location.clone(), asset_to_teleport_away),
597 Some((runtime_para_id, foreign_para_id)),
598 included_head,
599 &alice,
600 &slot_durations,
601 ));
602
603 assert_eq!(
605 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
606 foreign_asset_id_location.clone().into(),
607 &target_account
608 ),
609 (target_account_balance_before_teleport - asset_to_teleport_away.into())
610 );
611 assert_eq!(
612 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
613 foreign_asset_id_location.clone().into(),
614 &CheckingAccount::get()
615 ),
616 0.into()
617 );
618 assert_total::<
620 pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
621 AccountIdOf<Runtime>,
622 >(
623 foreign_asset_id_location.clone(),
624 teleported_foreign_asset_amount - asset_to_teleport_away,
625 teleported_foreign_asset_amount - asset_to_teleport_away,
626 );
627
628 RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
630 &unwrap_pallet_xcm_event,
631 |outcome| {
632 assert_ok!(outcome.ensure_complete());
633 },
634 );
635 assert!(RuntimeHelper::<Runtime>::xcmp_queue_message_sent(unwrap_xcmp_queue_event)
636 .is_some());
637 }
638 })
639}
640
641#[macro_export]
642macro_rules! include_teleports_for_foreign_assets_works(
643 (
644 $runtime:path,
645 $all_pallets_without_system:path,
646 $xcm_config:path,
647 $checking_account:path,
648 $weight_to_fee:path,
649 $hrmp_channel_opener:path,
650 $sovereign_account_of:path,
651 $assets_pallet_instance:path,
652 $collator_session_key:expr,
653 $slot_durations:expr,
654 $existential_deposit:expr,
655 $unwrap_pallet_xcm_event:expr,
656 $unwrap_xcmp_queue_event:expr
657 ) => {
658 #[test]
659 fn teleports_for_foreign_assets_works() {
660 const BOB: [u8; 32] = [2u8; 32];
661 let target_account = parachains_common::AccountId::from(BOB);
662 const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32];
663 let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER);
664
665 $crate::test_cases::teleports_for_foreign_assets_works::<
666 $runtime,
667 $all_pallets_without_system,
668 $xcm_config,
669 $checking_account,
670 $weight_to_fee,
671 $hrmp_channel_opener,
672 $sovereign_account_of,
673 $assets_pallet_instance
674 >(
675 $collator_session_key,
676 $slot_durations,
677 target_account,
678 $existential_deposit,
679 asset_owner,
680 $unwrap_pallet_xcm_event,
681 $unwrap_xcmp_queue_event
682 )
683 }
684 }
685);
686
687pub fn asset_transactor_transfer_with_local_consensus_currency_works<Runtime, XcmConfig>(
690 collator_session_keys: CollatorSessionKeys<Runtime>,
691 source_account: AccountIdOf<Runtime>,
692 target_account: AccountIdOf<Runtime>,
693 existential_deposit: BalanceOf<Runtime>,
694 additional_checks_before: Box<dyn Fn()>,
695 additional_checks_after: Box<dyn Fn()>,
696) where
697 Runtime: frame_system::Config
698 + pallet_balances::Config
699 + pallet_session::Config
700 + pallet_xcm::Config
701 + parachain_info::Config
702 + pallet_collator_selection::Config
703 + cumulus_pallet_parachain_system::Config
704 + pallet_timestamp::Config,
705 AccountIdOf<Runtime>: Into<[u8; 32]>,
706 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
707 BalanceOf<Runtime>: From<Balance>,
708 XcmConfig: xcm_executor::Config,
709 <Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
710 <Runtime as frame_system::Config>::AccountId:
711 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
712 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
713 From<<Runtime as frame_system::Config>::AccountId>,
714{
715 let unit = existential_deposit;
716
717 ExtBuilder::<Runtime>::default()
718 .with_collators(collator_session_keys.collators())
719 .with_session_keys(collator_session_keys.session_keys())
720 .with_balances(vec![(source_account.clone(), (BalanceOf::<Runtime>::from(10_u128) * unit))])
721 .with_tracing()
722 .build()
723 .execute_with(|| {
724 assert_eq!(
726 <pallet_balances::Pallet<Runtime>>::free_balance(&source_account),
727 (BalanceOf::<Runtime>::from(10_u128) * unit)
728 );
729 assert_eq!(
730 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
731 (BalanceOf::<Runtime>::zero() * unit)
732 );
733
734 additional_checks_before();
736
737 let _ = RuntimeHelper::<XcmConfig>::do_transfer(
739 Location {
740 parents: 0,
741 interior: [AccountId32 { network: None, id: source_account.clone().into() }]
742 .into(),
743 },
744 Location {
745 parents: 0,
746 interior: [AccountId32 { network: None, id: target_account.clone().into() }]
747 .into(),
748 },
749 (
751 Location { parents: 1, interior: Here },
752 (BalanceOf::<Runtime>::from(1_u128) * unit).into(),
753 ),
754 )
755 .expect("no error");
756
757 assert_eq!(
759 <pallet_balances::Pallet<Runtime>>::free_balance(source_account),
760 (BalanceOf::<Runtime>::from(9_u128) * unit)
761 );
762 assert_eq!(
763 <pallet_balances::Pallet<Runtime>>::free_balance(target_account),
764 (BalanceOf::<Runtime>::from(1_u128) * unit)
765 );
766
767 additional_checks_after();
768 })
769}
770
771#[macro_export]
772macro_rules! include_asset_transactor_transfer_with_local_consensus_currency_works(
773 (
774 $runtime:path,
775 $xcm_config:path,
776 $collator_session_key:expr,
777 $existential_deposit:expr,
778 $additional_checks_before:expr,
779 $additional_checks_after:expr
780 ) => {
781 #[test]
782 fn asset_transactor_transfer_with_local_consensus_currency_works() {
783 const ALICE: [u8; 32] = [1u8; 32];
784 let source_account = parachains_common::AccountId::from(ALICE);
785 const BOB: [u8; 32] = [2u8; 32];
786 let target_account = parachains_common::AccountId::from(BOB);
787
788 $crate::test_cases::asset_transactor_transfer_with_local_consensus_currency_works::<
789 $runtime,
790 $xcm_config
791 >(
792 $collator_session_key,
793 source_account,
794 target_account,
795 $existential_deposit,
796 $additional_checks_before,
797 $additional_checks_after
798 )
799 }
800 }
801);
802
803pub fn asset_transactor_transfer_with_pallet_assets_instance_works<
806 Runtime,
807 XcmConfig,
808 AssetsPalletInstance,
809 AssetId,
810 AssetIdConverter,
811>(
812 collator_session_keys: CollatorSessionKeys<Runtime>,
813 existential_deposit: BalanceOf<Runtime>,
814 asset_id: AssetId,
815 asset_owner: AccountIdOf<Runtime>,
816 alice_account: AccountIdOf<Runtime>,
817 bob_account: AccountIdOf<Runtime>,
818 charlie_account: AccountIdOf<Runtime>,
819 additional_checks_before: Box<dyn Fn()>,
820 additional_checks_after: Box<dyn Fn()>,
821) where
822 Runtime: frame_system::Config
823 + pallet_balances::Config
824 + pallet_session::Config
825 + pallet_xcm::Config
826 + parachain_info::Config
827 + pallet_collator_selection::Config
828 + cumulus_pallet_parachain_system::Config
829 + pallet_assets::Config<AssetsPalletInstance>
830 + pallet_timestamp::Config,
831 AccountIdOf<Runtime>: Into<[u8; 32]>,
832 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
833 BalanceOf<Runtime>: From<Balance>,
834 XcmConfig: xcm_executor::Config,
835 <Runtime as pallet_assets::Config<AssetsPalletInstance>>::AssetId:
836 From<AssetId> + Into<AssetId>,
837 <Runtime as pallet_assets::Config<AssetsPalletInstance>>::AssetIdParameter:
838 From<AssetId> + Into<AssetId>,
839 <Runtime as pallet_assets::Config<AssetsPalletInstance>>::Balance: From<Balance> + Into<u128>,
840 <Runtime as frame_system::Config>::AccountId:
841 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
842 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
843 From<<Runtime as frame_system::Config>::AccountId>,
844 AssetsPalletInstance: 'static,
845 AssetId: Clone,
846 AssetIdConverter: MaybeEquivalence<Location, AssetId>,
847{
848 ExtBuilder::<Runtime>::default()
849 .with_collators(collator_session_keys.collators())
850 .with_session_keys(collator_session_keys.session_keys())
851 .with_balances(vec![
852 (asset_owner.clone(), existential_deposit),
853 (alice_account.clone(), existential_deposit),
854 (bob_account.clone(), existential_deposit),
855 ])
856 .with_tracing()
857 .build()
858 .execute_with(|| {
859 let asset_minimum_asset_balance = 3333333_u128;
861 let asset_id_as_location = AssetIdConverter::convert_back(&asset_id).unwrap();
862 assert_ok!(<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::force_create(
863 RuntimeHelper::<Runtime>::root_origin(),
864 asset_id.clone().into(),
865 asset_owner.clone().into(),
866 false,
867 asset_minimum_asset_balance.into()
868 ));
869
870 assert_ok!(<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::mint(
872 RuntimeHelper::<Runtime>::origin_of(asset_owner.clone()),
873 asset_id.clone().into(),
874 alice_account.clone().into(),
875 (6 * asset_minimum_asset_balance).into()
876 ));
877
878 assert_eq!(
880 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
881 asset_id.clone().into(),
882 &alice_account
883 ),
884 (6 * asset_minimum_asset_balance).into()
885 );
886 assert_eq!(
887 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
888 asset_id.clone().into(),
889 &bob_account
890 ),
891 0.into()
892 );
893 assert_eq!(
894 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
895 asset_id.clone().into(),
896 &charlie_account
897 ),
898 0.into()
899 );
900 assert_eq!(
901 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
902 asset_id.clone().into(),
903 &asset_owner
904 ),
905 0.into()
906 );
907 assert_eq!(
908 <pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
909 existential_deposit
910 );
911 assert_eq!(
912 <pallet_balances::Pallet<Runtime>>::free_balance(&bob_account),
913 existential_deposit
914 );
915 assert_eq!(
916 <pallet_balances::Pallet<Runtime>>::free_balance(&charlie_account),
917 0.into()
918 );
919 assert_eq!(
920 <pallet_balances::Pallet<Runtime>>::free_balance(&asset_owner),
921 existential_deposit
922 );
923 additional_checks_before();
924
925 assert_noop!(
928 RuntimeHelper::<XcmConfig>::do_transfer(
929 Location {
930 parents: 0,
931 interior: [AccountId32 { network: None, id: alice_account.clone().into() }]
932 .into(),
933 },
934 Location {
935 parents: 0,
936 interior: [AccountId32 {
937 network: None,
938 id: charlie_account.clone().into()
939 }]
940 .into(),
941 },
942 (asset_id_as_location.clone(), asset_minimum_asset_balance),
943 ),
944 XcmError::FailedToTransactAsset(Into::<&str>::into(
945 sp_runtime::TokenError::CannotCreate
946 ))
947 );
948
949 assert!(matches!(
951 RuntimeHelper::<XcmConfig>::do_transfer(
952 Location {
953 parents: 0,
954 interior: [AccountId32 { network: None, id: alice_account.clone().into() }]
955 .into(),
956 },
957 Location {
958 parents: 0,
959 interior: [AccountId32 { network: None, id: bob_account.clone().into() }]
960 .into(),
961 },
962 (asset_id_as_location, asset_minimum_asset_balance),
963 ),
964 Ok(_)
965 ));
966
967 assert_eq!(
969 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
970 asset_id.clone().into(),
971 &alice_account
972 ),
973 (5 * asset_minimum_asset_balance).into()
974 );
975 assert_eq!(
976 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
977 asset_id.clone().into(),
978 &bob_account
979 ),
980 asset_minimum_asset_balance.into()
981 );
982 assert_eq!(
983 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
984 asset_id.clone().into(),
985 &charlie_account
986 ),
987 0.into()
988 );
989 assert_eq!(
990 <pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
991 asset_id.into(),
992 &asset_owner
993 ),
994 0.into()
995 );
996 assert_eq!(
997 <pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
998 existential_deposit
999 );
1000 assert_eq!(
1001 <pallet_balances::Pallet<Runtime>>::free_balance(&bob_account),
1002 existential_deposit
1003 );
1004 assert_eq!(
1005 <pallet_balances::Pallet<Runtime>>::free_balance(&charlie_account),
1006 0.into()
1007 );
1008 assert_eq!(
1009 <pallet_balances::Pallet<Runtime>>::free_balance(&asset_owner),
1010 existential_deposit
1011 );
1012
1013 additional_checks_after();
1014 })
1015}
1016
1017#[macro_export]
1018macro_rules! include_asset_transactor_transfer_with_pallet_assets_instance_works(
1019 (
1020 $test_name:tt,
1021 $runtime:path,
1022 $xcm_config:path,
1023 $assets_pallet_instance:path,
1024 $asset_id:path,
1025 $asset_id_converter:path,
1026 $collator_session_key:expr,
1027 $existential_deposit:expr,
1028 $tested_asset_id:expr,
1029 $additional_checks_before:expr,
1030 $additional_checks_after:expr
1031 ) => {
1032 #[test]
1033 fn $test_name() {
1034 const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32];
1035 let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER);
1036 const ALICE: [u8; 32] = [1u8; 32];
1037 let alice_account = parachains_common::AccountId::from(ALICE);
1038 const BOB: [u8; 32] = [2u8; 32];
1039 let bob_account = parachains_common::AccountId::from(BOB);
1040 const CHARLIE: [u8; 32] = [3u8; 32];
1041 let charlie_account = parachains_common::AccountId::from(CHARLIE);
1042
1043 $crate::test_cases::asset_transactor_transfer_with_pallet_assets_instance_works::<
1044 $runtime,
1045 $xcm_config,
1046 $assets_pallet_instance,
1047 $asset_id,
1048 $asset_id_converter
1049 >(
1050 $collator_session_key,
1051 $existential_deposit,
1052 $tested_asset_id,
1053 asset_owner,
1054 alice_account,
1055 bob_account,
1056 charlie_account,
1057 $additional_checks_before,
1058 $additional_checks_after
1059 )
1060 }
1061 }
1062);
1063
1064pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works<
1066 Runtime,
1067 XcmConfig,
1068 WeightToFee,
1069 SovereignAccountOf,
1070 ForeignAssetsPalletInstance,
1071 AssetId,
1072 AssetIdConverter,
1073>(
1074 collator_session_keys: CollatorSessionKeys<Runtime>,
1075 existential_deposit: BalanceOf<Runtime>,
1076 asset_deposit: BalanceOf<Runtime>,
1077 metadata_deposit_base: BalanceOf<Runtime>,
1078 metadata_deposit_per_byte: BalanceOf<Runtime>,
1079 alice_account: AccountIdOf<Runtime>,
1080 bob_account: AccountIdOf<Runtime>,
1081 runtime_call_encode: Box<
1082 dyn Fn(pallet_assets::Call<Runtime, ForeignAssetsPalletInstance>) -> Vec<u8>,
1083 >,
1084 unwrap_pallet_assets_event: Box<
1085 dyn Fn(Vec<u8>) -> Option<pallet_assets::Event<Runtime, ForeignAssetsPalletInstance>>,
1086 >,
1087 additional_checks_before: Box<dyn Fn()>,
1088 additional_checks_after: Box<dyn Fn()>,
1089) where
1090 Runtime: frame_system::Config
1091 + pallet_balances::Config
1092 + pallet_session::Config
1093 + pallet_xcm::Config
1094 + parachain_info::Config
1095 + pallet_collator_selection::Config
1096 + cumulus_pallet_parachain_system::Config
1097 + pallet_assets::Config<ForeignAssetsPalletInstance>
1098 + pallet_timestamp::Config,
1099 AccountIdOf<Runtime>: Into<[u8; 32]>,
1100 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
1101 BalanceOf<Runtime>: From<Balance>,
1102 XcmConfig: xcm_executor::Config,
1103 WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
1104 <WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
1105 SovereignAccountOf: ConvertLocation<AccountIdOf<Runtime>>,
1106 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
1107 From<AssetId> + Into<AssetId>,
1108 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
1109 From<AssetId> + Into<AssetId>,
1110 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
1111 From<Balance> + Into<u128>,
1112 <Runtime as frame_system::Config>::AccountId:
1113 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
1114 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
1115 From<<Runtime as frame_system::Config>::AccountId>,
1116 ForeignAssetsPalletInstance: 'static,
1117 AssetId: Clone,
1118 AssetIdConverter: MaybeEquivalence<Location, AssetId>,
1119{
1120 let foreign_asset_id_location = Location::new(1, [Parachain(2222), GeneralIndex(1234567)]);
1122 let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap();
1123
1124 let foreign_creator = Location { parents: 1, interior: [Parachain(2222)].into() };
1126 let foreign_creator_as_account_id =
1127 SovereignAccountOf::convert_location(&foreign_creator).expect("");
1128
1129 let buy_execution_fee_amount =
1131 WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0));
1132 let buy_execution_fee =
1133 Asset { id: AssetId(Location::parent()), fun: Fungible(buy_execution_fee_amount) };
1134
1135 const ASSET_NAME: &str = "My super coin";
1136 const ASSET_SYMBOL: &str = "MY_S_COIN";
1137 let metadata_deposit_per_byte_eta = metadata_deposit_per_byte
1138 .saturating_mul(((ASSET_NAME.len() + ASSET_SYMBOL.len()) as u128).into());
1139
1140 ExtBuilder::<Runtime>::default()
1141 .with_collators(collator_session_keys.collators())
1142 .with_session_keys(collator_session_keys.session_keys())
1143 .with_balances(vec![(
1144 foreign_creator_as_account_id.clone(),
1145 existential_deposit +
1146 asset_deposit +
1147 metadata_deposit_base +
1148 metadata_deposit_per_byte_eta +
1149 buy_execution_fee_amount.into() +
1150 buy_execution_fee_amount.into(),
1151 )])
1152 .with_tracing()
1153 .build()
1154 .execute_with(|| {
1155 assert!(<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::asset_ids()
1156 .collect::<Vec<_>>()
1157 .is_empty());
1158 assert_eq!(
1159 <pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id),
1160 existential_deposit +
1161 asset_deposit + metadata_deposit_base +
1162 metadata_deposit_per_byte_eta +
1163 buy_execution_fee_amount.into() +
1164 buy_execution_fee_amount.into()
1165 );
1166 additional_checks_before();
1167
1168 let foreign_asset_create = runtime_call_encode(pallet_assets::Call::<
1171 Runtime,
1172 ForeignAssetsPalletInstance,
1173 >::create {
1174 id: asset_id.clone().into(),
1175 admin: foreign_creator_as_account_id.clone().into(),
1177 min_balance: 1.into(),
1178 });
1179 let foreign_asset_set_metadata = runtime_call_encode(pallet_assets::Call::<
1181 Runtime,
1182 ForeignAssetsPalletInstance,
1183 >::set_metadata {
1184 id: asset_id.clone().into(),
1185 name: Vec::from(ASSET_NAME),
1186 symbol: Vec::from(ASSET_SYMBOL),
1187 decimals: 12,
1188 });
1189 let foreign_asset_set_team = runtime_call_encode(pallet_assets::Call::<
1191 Runtime,
1192 ForeignAssetsPalletInstance,
1193 >::set_team {
1194 id: asset_id.clone().into(),
1195 issuer: foreign_creator_as_account_id.clone().into(),
1196 admin: foreign_creator_as_account_id.clone().into(),
1197 freezer: bob_account.clone().into(),
1198 });
1199
1200 let xcm = Xcm(vec![
1203 WithdrawAsset(buy_execution_fee.clone().into()),
1204 BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
1205 Transact {
1206 origin_kind: OriginKind::Xcm,
1207 call: foreign_asset_create.into(),
1208 fallback_max_weight: None,
1209 },
1210 Transact {
1211 origin_kind: OriginKind::SovereignAccount,
1212 call: foreign_asset_set_metadata.into(),
1213 fallback_max_weight: None,
1214 },
1215 Transact {
1216 origin_kind: OriginKind::SovereignAccount,
1217 call: foreign_asset_set_team.into(),
1218 fallback_max_weight: None,
1219 },
1220 ExpectTransactStatus(MaybeErrorCode::Success),
1221 ]);
1222
1223 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
1225
1226 let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
1228 foreign_creator.clone(),
1229 xcm,
1230 &mut hash,
1231 RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
1232 Weight::zero(),
1233 );
1234 assert_ok!(outcome.ensure_complete());
1235
1236 let mut events = <frame_system::Pallet<Runtime>>::events()
1238 .into_iter()
1239 .filter_map(|e| unwrap_pallet_assets_event(e.event.encode()));
1240 assert!(events.any(|e| matches!(e, pallet_assets::Event::Created { .. })));
1241 assert!(events.any(|e| matches!(e, pallet_assets::Event::MetadataSet { .. })));
1242 assert!(events.any(|e| matches!(e, pallet_assets::Event::TeamChanged { .. })));
1243
1244 assert!(!<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::asset_ids()
1246 .collect::<Vec<_>>()
1247 .is_empty());
1248
1249 use frame_support::traits::fungibles::roles::Inspect as InspectRoles;
1251 assert_eq!(
1252 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::owner(
1253 asset_id.clone().into()
1254 ),
1255 Some(foreign_creator_as_account_id.clone())
1256 );
1257 assert_eq!(
1258 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::admin(
1259 asset_id.clone().into()
1260 ),
1261 Some(foreign_creator_as_account_id.clone())
1262 );
1263 assert_eq!(
1264 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::issuer(
1265 asset_id.clone().into()
1266 ),
1267 Some(foreign_creator_as_account_id.clone())
1268 );
1269 assert_eq!(
1270 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freezer(
1271 asset_id.clone().into()
1272 ),
1273 Some(bob_account.clone())
1274 );
1275 assert!(
1276 <pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id) >=
1277 existential_deposit + buy_execution_fee_amount.into(),
1278 "Free balance: {:?} should be ge {:?}",
1279 <pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id),
1280 existential_deposit + buy_execution_fee_amount.into()
1281 );
1282 assert_metadata::<
1283 pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
1284 AccountIdOf<Runtime>,
1285 >(asset_id.clone(), ASSET_NAME, ASSET_SYMBOL, 12);
1286
1287 assert_noop!(
1289 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freeze(
1290 RuntimeHelper::<Runtime>::origin_of(bob_account),
1291 asset_id.clone().into(),
1292 alice_account.clone().into()
1293 ),
1294 pallet_assets::Error::<Runtime, ForeignAssetsPalletInstance>::NoAccount
1295 );
1296 assert_noop!(
1297 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freeze(
1298 RuntimeHelper::<Runtime>::origin_of(foreign_creator_as_account_id.clone()),
1299 asset_id.into(),
1300 alice_account.into()
1301 ),
1302 pallet_assets::Error::<Runtime, ForeignAssetsPalletInstance>::NoPermission
1303 );
1304
1305 let foreign_asset_id_location =
1308 Location { parents: 1, interior: [Parachain(3333), GeneralIndex(1234567)].into() };
1309 let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap();
1310
1311 let foreign_asset_create = runtime_call_encode(pallet_assets::Call::<
1313 Runtime,
1314 ForeignAssetsPalletInstance,
1315 >::create {
1316 id: asset_id.into(),
1317 admin: foreign_creator_as_account_id.clone().into(),
1319 min_balance: 1.into(),
1320 });
1321 let xcm = Xcm(vec![
1322 WithdrawAsset(buy_execution_fee.clone().into()),
1323 BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
1324 Transact {
1325 origin_kind: OriginKind::Xcm,
1326 call: foreign_asset_create.into(),
1327 fallback_max_weight: None,
1328 },
1329 ExpectTransactStatus(MaybeErrorCode::from(DispatchError::BadOrigin.encode())),
1330 ]);
1331
1332 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
1334
1335 let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
1337 foreign_creator,
1338 xcm,
1339 &mut hash,
1340 RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
1341 Weight::zero(),
1342 );
1343 assert_ok!(outcome.ensure_complete());
1344
1345 additional_checks_after();
1346 })
1347}
1348
1349#[macro_export]
1350macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works(
1351 (
1352 $runtime:path,
1353 $xcm_config:path,
1354 $weight_to_fee:path,
1355 $sovereign_account_of:path,
1356 $assets_pallet_instance:path,
1357 $asset_id:path,
1358 $asset_id_converter:path,
1359 $collator_session_key:expr,
1360 $existential_deposit:expr,
1361 $asset_deposit:expr,
1362 $metadata_deposit_base:expr,
1363 $metadata_deposit_per_byte:expr,
1364 $runtime_call_encode:expr,
1365 $unwrap_pallet_assets_event:expr,
1366 $additional_checks_before:expr,
1367 $additional_checks_after:expr
1368 ) => {
1369 #[test]
1370 fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works() {
1371 const ALICE: [u8; 32] = [1u8; 32];
1372 let alice_account = parachains_common::AccountId::from(ALICE);
1373 const BOB: [u8; 32] = [2u8; 32];
1374 let bob_account = parachains_common::AccountId::from(BOB);
1375
1376 $crate::test_cases::create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works::<
1377 $runtime,
1378 $xcm_config,
1379 $weight_to_fee,
1380 $sovereign_account_of,
1381 $assets_pallet_instance,
1382 $asset_id,
1383 $asset_id_converter
1384 >(
1385 $collator_session_key,
1386 $existential_deposit,
1387 $asset_deposit,
1388 $metadata_deposit_base,
1389 $metadata_deposit_per_byte,
1390 alice_account,
1391 bob_account,
1392 $runtime_call_encode,
1393 $unwrap_pallet_assets_event,
1394 $additional_checks_before,
1395 $additional_checks_after
1396 )
1397 }
1398 }
1399);
1400
1401pub fn reserve_transfer_native_asset_to_non_teleport_para_works<
1404 Runtime,
1405 AllPalletsWithoutSystem,
1406 XcmConfig,
1407 HrmpChannelOpener,
1408 HrmpChannelSource,
1409 LocationToAccountId,
1410>(
1411 collator_session_keys: CollatorSessionKeys<Runtime>,
1412 slot_durations: SlotDurations,
1413 existential_deposit: BalanceOf<Runtime>,
1414 alice_account: AccountIdOf<Runtime>,
1415 unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
1416 unwrap_xcmp_queue_event: Box<
1417 dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
1418 >,
1419 weight_limit: WeightLimit,
1420) where
1421 Runtime: frame_system::Config
1422 + pallet_balances::Config
1423 + pallet_session::Config
1424 + pallet_xcm::Config
1425 + parachain_info::Config
1426 + pallet_collator_selection::Config
1427 + cumulus_pallet_parachain_system::Config
1428 + cumulus_pallet_xcmp_queue::Config
1429 + pallet_timestamp::Config,
1430 AllPalletsWithoutSystem:
1431 OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
1432 AccountIdOf<Runtime>: Into<[u8; 32]>,
1433 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
1434 BalanceOf<Runtime>: From<Balance>,
1435 <Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
1436 XcmConfig: xcm_executor::Config,
1437 LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
1438 <Runtime as frame_system::Config>::AccountId:
1439 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
1440 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
1441 From<<Runtime as frame_system::Config>::AccountId>,
1442 <Runtime as frame_system::Config>::AccountId: From<AccountId>,
1443 HrmpChannelOpener: frame_support::inherent::ProvideInherent<
1444 Call = cumulus_pallet_parachain_system::Call<Runtime>,
1445 >,
1446 HrmpChannelSource: XcmpMessageSource,
1447{
1448 let runtime_para_id = 1000;
1449 ExtBuilder::<Runtime>::default()
1450 .with_collators(collator_session_keys.collators())
1451 .with_session_keys(collator_session_keys.session_keys())
1452 .with_tracing()
1453 .with_safe_xcm_version(3)
1454 .with_para_id(runtime_para_id.into())
1455 .build()
1456 .execute_with(|| {
1457 let mut alice = [0u8; 32];
1458 alice[0] = 1;
1459 let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
1460 2,
1461 AccountId::from(alice).into(),
1462 );
1463
1464 let other_para_id = 2345;
1467 let native_asset = Location::parent();
1468 let dest = Location::new(1, [Parachain(other_para_id)]);
1469 let mut dest_beneficiary = Location::new(1, [Parachain(other_para_id)])
1470 .appended_with(AccountId32 {
1471 network: None,
1472 id: sp_runtime::AccountId32::new([3; 32]).into(),
1473 })
1474 .unwrap();
1475 dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
1476
1477 let reserve_account = LocationToAccountId::convert_location(&dest)
1478 .expect("Sovereign account for reserves");
1479 let balance_to_transfer = 1_000_000_000_000_u128;
1480
1481 mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
1483 runtime_para_id.into(),
1484 other_para_id.into(),
1485 included_head,
1486 &alice,
1487 &slot_durations,
1488 );
1489
1490 let delivery_fees_buffer = 40_000_000_000u128;
1494 let alice_account_init_balance = existential_deposit.saturating_mul(2.into()) +
1496 balance_to_transfer.into() +
1497 delivery_fees_buffer.into();
1498 let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
1499 &alice_account,
1500 alice_account_init_balance,
1501 );
1502 let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
1504 &reserve_account,
1505 existential_deposit,
1506 );
1507
1508 assert!(
1511 (<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
1512 balance_to_transfer.into()) >=
1513 existential_deposit
1514 );
1515 assert_eq!(
1517 <pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
1518 existential_deposit
1519 );
1520
1521 let asset_to_transfer =
1523 Asset { fun: Fungible(balance_to_transfer.into()), id: AssetId(native_asset) };
1524
1525 assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
1527 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
1528 Box::new(dest.clone().into_versioned()),
1529 Box::new(dest_beneficiary.clone().into_versioned()),
1530 Box::new(VersionedAssets::from(Assets::from(asset_to_transfer))),
1531 0,
1532 weight_limit,
1533 ));
1534
1535 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
1538 &unwrap_pallet_xcm_event,
1539 |outcome| {
1540 assert_ok!(outcome.ensure_complete());
1541 },
1542 );
1543
1544 let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
1546 .into_iter()
1547 .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
1548 .find_map(|e| match e {
1549 cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
1550 Some(message_hash),
1551 _ => None,
1552 });
1553
1554 let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
1556 other_para_id.into(),
1557 )
1558 .unwrap();
1559
1560 let delivery_fees = get_fungible_delivery_fees::<
1561 <XcmConfig as xcm_executor::Config>::XcmSender,
1562 >(dest.clone(), Xcm::try_from(xcm_sent.clone()).unwrap());
1563
1564 assert_eq!(
1565 xcm_sent_message_hash,
1566 Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
1567 );
1568 let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
1569
1570 println!("reserve_transfer_native_asset_works sent xcm: {:?}", xcm_sent);
1572 let reserve_assets_deposited = Assets::from(vec![Asset {
1573 id: AssetId(Location { parents: 1, interior: Here }),
1574 fun: Fungible(1000000000000),
1575 }]);
1576
1577 assert_matches_reserve_asset_deposited_instructions(
1578 &mut xcm_sent,
1579 &reserve_assets_deposited,
1580 &dest_beneficiary,
1581 );
1582
1583 assert_eq!(
1585 <pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
1586 alice_account_init_balance - balance_to_transfer.into() - delivery_fees.into()
1587 );
1588
1589 assert_eq!(
1592 <pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
1593 existential_deposit + balance_to_transfer.into()
1594 );
1595 })
1596}