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