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