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