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