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