asset_test_utils/
test_cases.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Module contains predefined test-case scenarios for `Runtime` with various assets.
17
18use super::xcm_helpers;
19use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
20use codec::Encode;
21use cumulus_primitives_core::XcmpMessageSource;
22use frame_support::{
23	assert_noop, assert_ok,
24	traits::{
25		fungible::Mutate, fungibles::InspectEnumerable, Currency, Get, OnFinalize, OnInitialize,
26		OriginTrait,
27	},
28	weights::Weight,
29};
30use frame_system::pallet_prelude::BlockNumberFor;
31use parachains_common::{AccountId, Balance};
32use parachains_runtimes_test_utils::{
33	assert_metadata, assert_total, mock_open_hrmp_channel, AccountIdOf, BalanceOf,
34	CollatorSessionKeys, ExtBuilder, SlotDurations, ValidatorIdOf, XcmReceivedFrom,
35};
36use sp_runtime::{
37	traits::{MaybeEquivalence, StaticLookup, Zero},
38	DispatchError, Saturating,
39};
40use xcm::{latest::prelude::*, VersionedAssets};
41use xcm_executor::{traits::ConvertLocation, XcmExecutor};
42
43type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
44	parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
45
46// Re-export test_case from `parachains-runtimes-test-utils`
47pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
48
49/// Test-case makes sure that `Runtime` can receive native asset from relay chain and can teleport
50/// it back
51pub fn teleports_for_native_asset_works<
52	Runtime,
53	AllPalletsWithoutSystem,
54	XcmConfig,
55	CheckingAccount,
56	WeightToFee,
57	HrmpChannelOpener,
58>(
59	collator_session_keys: CollatorSessionKeys<Runtime>,
60	slot_durations: SlotDurations,
61	existential_deposit: BalanceOf<Runtime>,
62	target_account: AccountIdOf<Runtime>,
63	unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
64	runtime_para_id: u32,
65) where
66	Runtime: frame_system::Config
67		+ pallet_balances::Config
68		+ pallet_session::Config
69		+ pallet_xcm::Config
70		+ parachain_info::Config
71		+ pallet_collator_selection::Config
72		+ cumulus_pallet_parachain_system::Config
73		+ cumulus_pallet_xcmp_queue::Config
74		+ pallet_timestamp::Config,
75	AllPalletsWithoutSystem:
76		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
77	AccountIdOf<Runtime>: Into<[u8; 32]>,
78	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
79	BalanceOf<Runtime>: From<Balance> + Into<u128>,
80	WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
81	<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
82	<Runtime as frame_system::Config>::AccountId:
83		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
84	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
85		From<<Runtime as frame_system::Config>::AccountId>,
86	<Runtime as frame_system::Config>::AccountId: From<AccountId>,
87	XcmConfig: xcm_executor::Config,
88	CheckingAccount: Get<AccountIdOf<Runtime>>,
89	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
90		Call = cumulus_pallet_parachain_system::Call<Runtime>,
91	>,
92{
93	ExtBuilder::<Runtime>::default()
94		.with_collators(collator_session_keys.collators())
95		.with_session_keys(collator_session_keys.session_keys())
96		.with_safe_xcm_version(XCM_VERSION)
97		.with_para_id(runtime_para_id.into())
98		.with_tracing()
99		.build()
100		.execute_with(|| {
101			let mut alice = [0u8; 32];
102			alice[0] = 1;
103
104			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
105				2,
106				AccountId::from(alice).into(),
107			);
108			// check Balances before
109			assert_eq!(<pallet_balances::Pallet<Runtime>>::free_balance(&target_account), 0.into());
110			assert_eq!(
111				<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
112				0.into()
113			);
114
115			let native_asset_id = Location::parent();
116			let buy_execution_fee_amount_eta =
117				WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 1024));
118			let native_asset_amount_unit = existential_deposit;
119			let native_asset_amount_received =
120				native_asset_amount_unit * 10.into() + buy_execution_fee_amount_eta.into();
121
122			// 1. process received teleported assets from relaychain
123			let xcm = Xcm(vec![
124				ReceiveTeleportedAsset(Assets::from(vec![Asset {
125					id: AssetId(native_asset_id.clone()),
126					fun: Fungible(native_asset_amount_received.into()),
127				}])),
128				ClearOrigin,
129				BuyExecution {
130					fees: Asset {
131						id: AssetId(native_asset_id.clone()),
132						fun: Fungible(buy_execution_fee_amount_eta),
133					},
134					weight_limit: Limited(Weight::from_parts(3035310000, 65536)),
135				},
136				DepositAsset {
137					assets: Wild(AllCounted(1)),
138					beneficiary: Location {
139						parents: 0,
140						interior: [AccountId32 {
141							network: None,
142							id: target_account.clone().into(),
143						}]
144						.into(),
145					},
146				},
147				ExpectTransactStatus(MaybeErrorCode::Success),
148			]);
149
150			let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
151
152			let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
153				Parent,
154				xcm,
155				&mut hash,
156				RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Parent),
157				Weight::zero(),
158			);
159			assert_ok!(outcome.ensure_complete());
160
161			// check Balances after
162			assert_ne!(<pallet_balances::Pallet<Runtime>>::free_balance(&target_account), 0.into());
163			assert_eq!(
164				<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
165				0.into()
166			);
167
168			// 2. try to teleport asset back to the relaychain
169			{
170				let dest = Location::parent();
171				let mut dest_beneficiary = Location::parent()
172					.appended_with(AccountId32 {
173						network: None,
174						id: sp_runtime::AccountId32::new([3; 32]).into(),
175					})
176					.unwrap();
177				dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
178
179				let target_account_balance_before_teleport =
180					<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
181				let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
182				assert!(
183					native_asset_to_teleport_away <
184						target_account_balance_before_teleport - existential_deposit
185				);
186
187				// Mint funds into account to ensure it has enough balance to pay delivery fees
188				let delivery_fees =
189					xcm_helpers::teleport_assets_delivery_fees::<XcmConfig::XcmSender>(
190						(native_asset_id.clone(), native_asset_to_teleport_away.into()).into(),
191						0,
192						Unlimited,
193						dest_beneficiary.clone(),
194						dest.clone(),
195					);
196				<pallet_balances::Pallet<Runtime>>::mint_into(
197					&target_account,
198					delivery_fees.into(),
199				)
200				.unwrap();
201
202				assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
203					RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
204					dest,
205					dest_beneficiary,
206					(native_asset_id.clone(), native_asset_to_teleport_away.into()),
207					None,
208					included_head.clone(),
209					&alice,
210					&slot_durations,
211				));
212
213				// check balances
214				assert_eq!(
215					<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
216					target_account_balance_before_teleport - native_asset_to_teleport_away
217				);
218				assert_eq!(
219					<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
220					0.into()
221				);
222
223				// check events
224				RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
225					&unwrap_pallet_xcm_event,
226					|outcome| {
227						assert_ok!(outcome.ensure_complete());
228					},
229				);
230			}
231
232			// 3. try to teleport assets away to other parachain (2345): should not work as we don't
233			//    trust `IsTeleporter` for `(relay-native-asset, para(2345))` pair
234			{
235				let other_para_id = 2345;
236				let dest = Location::new(1, [Parachain(other_para_id)]);
237				let mut dest_beneficiary = Location::new(1, [Parachain(other_para_id)])
238					.appended_with(AccountId32 {
239						network: None,
240						id: sp_runtime::AccountId32::new([3; 32]).into(),
241					})
242					.unwrap();
243				dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
244
245				let target_account_balance_before_teleport =
246					<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
247
248				let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
249				assert!(
250					native_asset_to_teleport_away <
251						target_account_balance_before_teleport - existential_deposit
252				);
253
254				assert_eq!(
255					RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
256						RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
257						dest,
258						dest_beneficiary,
259						(native_asset_id, native_asset_to_teleport_away.into()),
260						Some((runtime_para_id, other_para_id)),
261						included_head,
262						&alice,
263						&slot_durations,
264					),
265					Err(DispatchError::Module(sp_runtime::ModuleError {
266						index: 31,
267						error: [2, 0, 0, 0,],
268						message: Some("Filtered",),
269					},),)
270				);
271
272				// check balances
273				assert_eq!(
274					<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
275					target_account_balance_before_teleport
276				);
277				assert_eq!(
278					<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
279					0.into()
280				);
281			}
282		})
283}
284
285#[macro_export]
286macro_rules! include_teleports_for_native_asset_works(
287	(
288		$runtime:path,
289		$all_pallets_without_system:path,
290		$xcm_config:path,
291		$checking_account:path,
292		$weight_to_fee:path,
293		$hrmp_channel_opener:path,
294		$collator_session_key:expr,
295		$slot_durations:expr,
296		$existential_deposit:expr,
297		$unwrap_pallet_xcm_event:expr,
298		$runtime_para_id:expr
299	) => {
300		#[test]
301		fn teleports_for_native_asset_works() {
302			const BOB: [u8; 32] = [2u8; 32];
303			let target_account = parachains_common::AccountId::from(BOB);
304
305			$crate::test_cases::teleports_for_native_asset_works::<
306				$runtime,
307				$all_pallets_without_system,
308				$xcm_config,
309				$checking_account,
310				$weight_to_fee,
311				$hrmp_channel_opener
312			>(
313				$collator_session_key,
314				$slot_durations,
315				$existential_deposit,
316				target_account,
317				$unwrap_pallet_xcm_event,
318				$runtime_para_id
319			)
320		}
321	}
322);
323
324/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain, and
325/// can teleport it back
326pub fn teleports_for_foreign_assets_works<
327	Runtime,
328	AllPalletsWithoutSystem,
329	XcmConfig,
330	CheckingAccount,
331	WeightToFee,
332	HrmpChannelOpener,
333	SovereignAccountOf,
334	ForeignAssetsPalletInstance,
335>(
336	collator_session_keys: CollatorSessionKeys<Runtime>,
337	slot_durations: SlotDurations,
338	target_account: AccountIdOf<Runtime>,
339	existential_deposit: BalanceOf<Runtime>,
340	asset_owner: AccountIdOf<Runtime>,
341	unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
342	unwrap_xcmp_queue_event: Box<
343		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
344	>,
345) where
346	Runtime: frame_system::Config
347		+ pallet_balances::Config
348		+ pallet_session::Config
349		+ pallet_xcm::Config
350		+ parachain_info::Config
351		+ pallet_collator_selection::Config
352		+ cumulus_pallet_parachain_system::Config
353		+ cumulus_pallet_xcmp_queue::Config
354		+ pallet_assets::Config<ForeignAssetsPalletInstance>
355		+ pallet_timestamp::Config,
356	AllPalletsWithoutSystem:
357		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
358	AccountIdOf<Runtime>: Into<[u8; 32]>,
359	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
360	BalanceOf<Runtime>: From<Balance>,
361	XcmConfig: xcm_executor::Config,
362	CheckingAccount: Get<AccountIdOf<Runtime>>,
363	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
364		Call = cumulus_pallet_parachain_system::Call<Runtime>,
365	>,
366	WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
367	<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
368	SovereignAccountOf: ConvertLocation<AccountIdOf<Runtime>>,
369	<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
370		From<xcm::v5::Location> + Into<xcm::v5::Location>,
371	<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
372		From<xcm::v5::Location> + Into<xcm::v5::Location>,
373	<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
374		From<Balance> + Into<u128>,
375	<Runtime as frame_system::Config>::AccountId:
376		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
377	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
378		From<<Runtime as frame_system::Config>::AccountId>,
379	<Runtime as frame_system::Config>::AccountId: From<AccountId>,
380	ForeignAssetsPalletInstance: 'static,
381{
382	// foreign parachain with the same consensus currency as asset
383	let foreign_para_id = 2222;
384	let foreign_asset_id_location = xcm::v5::Location {
385		parents: 1,
386		interior: [
387			xcm::v5::Junction::Parachain(foreign_para_id),
388			xcm::v5::Junction::GeneralIndex(1234567),
389		]
390		.into(),
391	};
392
393	// foreign creator, which can be sibling parachain to match ForeignCreators
394	let foreign_creator = Location { parents: 1, interior: [Parachain(foreign_para_id)].into() };
395	let foreign_creator_as_account_id =
396		SovereignAccountOf::convert_location(&foreign_creator).expect("");
397
398	// we want to buy execution with local relay chain currency
399	let buy_execution_fee_amount =
400		WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0));
401	let buy_execution_fee =
402		Asset { id: AssetId(Location::parent()), fun: Fungible(buy_execution_fee_amount) };
403
404	let teleported_foreign_asset_amount = 10_000_000_000_000;
405	let runtime_para_id = 1000;
406	ExtBuilder::<Runtime>::default()
407		.with_collators(collator_session_keys.collators())
408		.with_session_keys(collator_session_keys.session_keys())
409		.with_balances(vec![
410			(
411				foreign_creator_as_account_id,
412				existential_deposit + (buy_execution_fee_amount * 2).into(),
413			),
414			(target_account.clone(), existential_deposit),
415			(CheckingAccount::get(), existential_deposit),
416		])
417		.with_safe_xcm_version(XCM_VERSION)
418		.with_para_id(runtime_para_id.into())
419		.with_tracing()
420		.build()
421		.execute_with(|| {
422			let mut alice = [0u8; 32];
423			alice[0] = 1;
424
425			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
426				2,
427				AccountId::from(alice).into(),
428			);
429			// checks target_account before
430			assert_eq!(
431				<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
432				existential_deposit
433			);
434			// check `CheckingAccount` before
435			assert_eq!(
436				<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
437				existential_deposit
438			);
439			assert_eq!(
440				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
441					foreign_asset_id_location.clone().into(),
442					&target_account
443				),
444				0.into()
445			);
446			assert_eq!(
447				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
448					foreign_asset_id_location.clone().into(),
449					&CheckingAccount::get()
450				),
451				0.into()
452			);
453			// check totals before
454			assert_total::<
455				pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
456				AccountIdOf<Runtime>,
457			>(foreign_asset_id_location.clone(), 0, 0);
458
459			// create foreign asset (0 total issuance)
460			let asset_minimum_asset_balance = 3333333_u128;
461			assert_ok!(
462				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::force_create(
463					RuntimeHelper::<Runtime>::root_origin(),
464					foreign_asset_id_location.clone().into(),
465					asset_owner.into(),
466					false,
467					asset_minimum_asset_balance.into()
468				)
469			);
470			assert_total::<
471				pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
472				AccountIdOf<Runtime>,
473			>(foreign_asset_id_location.clone(), 0, 0);
474			assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance);
475
476			// 1. process received teleported assets from sibling parachain (foreign_para_id)
477			let xcm = Xcm(vec![
478				// BuyExecution with relaychain native token
479				WithdrawAsset(buy_execution_fee.clone().into()),
480				BuyExecution {
481					fees: Asset {
482						id: AssetId(Location::parent()),
483						fun: Fungible(buy_execution_fee_amount),
484					},
485					weight_limit: Unlimited,
486				},
487				// Process teleported asset
488				ReceiveTeleportedAsset(Assets::from(vec![Asset {
489					id: AssetId(foreign_asset_id_location.clone()),
490					fun: Fungible(teleported_foreign_asset_amount),
491				}])),
492				DepositAsset {
493					assets: Wild(AllOf {
494						id: AssetId(foreign_asset_id_location.clone()),
495						fun: WildFungibility::Fungible,
496					}),
497					beneficiary: Location {
498						parents: 0,
499						interior: [AccountId32 {
500							network: None,
501							id: target_account.clone().into(),
502						}]
503						.into(),
504					},
505				},
506				ExpectTransactStatus(MaybeErrorCode::Success),
507			]);
508			let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
509
510			let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
511				foreign_creator,
512				xcm,
513				&mut hash,
514				RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
515				Weight::zero(),
516			);
517			assert_ok!(outcome.ensure_complete());
518
519			// checks target_account after
520			assert_eq!(
521				<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
522				existential_deposit
523			);
524			assert_eq!(
525				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
526					foreign_asset_id_location.clone().into(),
527					&target_account
528				),
529				teleported_foreign_asset_amount.into()
530			);
531			// checks `CheckingAccount` after
532			assert_eq!(
533				<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
534				existential_deposit
535			);
536			assert_eq!(
537				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
538					foreign_asset_id_location.clone().into(),
539					&CheckingAccount::get()
540				),
541				0.into()
542			);
543			// check total after (twice: target_account + CheckingAccount)
544			assert_total::<
545				pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
546				AccountIdOf<Runtime>,
547			>(
548				foreign_asset_id_location.clone(),
549				teleported_foreign_asset_amount,
550				teleported_foreign_asset_amount,
551			);
552
553			// 2. try to teleport asset back to source parachain (foreign_para_id)
554			{
555				let dest = Location::new(1, [Parachain(foreign_para_id)]);
556				let mut dest_beneficiary = Location::new(1, [Parachain(foreign_para_id)])
557					.appended_with(AccountId32 {
558						network: None,
559						id: sp_runtime::AccountId32::new([3; 32]).into(),
560					})
561					.unwrap();
562				dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
563
564				let target_account_balance_before_teleport =
565					<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
566						foreign_asset_id_location.clone().into(),
567						&target_account,
568					);
569				let asset_to_teleport_away = asset_minimum_asset_balance * 3;
570				assert!(
571					asset_to_teleport_away <
572						(target_account_balance_before_teleport -
573							asset_minimum_asset_balance.into())
574						.into()
575				);
576
577				// Make sure the target account has enough native asset to pay for delivery fees
578				let delivery_fees =
579					xcm_helpers::teleport_assets_delivery_fees::<XcmConfig::XcmSender>(
580						(foreign_asset_id_location.clone(), asset_to_teleport_away).into(),
581						0,
582						Unlimited,
583						dest_beneficiary.clone(),
584						dest.clone(),
585					);
586				<pallet_balances::Pallet<Runtime>>::mint_into(
587					&target_account,
588					delivery_fees.into(),
589				)
590				.unwrap();
591
592				assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
593					RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
594					dest,
595					dest_beneficiary,
596					(foreign_asset_id_location.clone(), asset_to_teleport_away),
597					Some((runtime_para_id, foreign_para_id)),
598					included_head,
599					&alice,
600					&slot_durations,
601				));
602
603				// check balances
604				assert_eq!(
605					<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
606						foreign_asset_id_location.clone().into(),
607						&target_account
608					),
609					(target_account_balance_before_teleport - asset_to_teleport_away.into())
610				);
611				assert_eq!(
612					<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
613						foreign_asset_id_location.clone().into(),
614						&CheckingAccount::get()
615					),
616					0.into()
617				);
618				// check total after (twice: target_account + CheckingAccount)
619				assert_total::<
620					pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
621					AccountIdOf<Runtime>,
622				>(
623					foreign_asset_id_location.clone(),
624					teleported_foreign_asset_amount - asset_to_teleport_away,
625					teleported_foreign_asset_amount - asset_to_teleport_away,
626				);
627
628				// check events
629				RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
630					&unwrap_pallet_xcm_event,
631					|outcome| {
632						assert_ok!(outcome.ensure_complete());
633					},
634				);
635				assert!(RuntimeHelper::<Runtime>::xcmp_queue_message_sent(unwrap_xcmp_queue_event)
636					.is_some());
637			}
638		})
639}
640
641#[macro_export]
642macro_rules! include_teleports_for_foreign_assets_works(
643	(
644		$runtime:path,
645		$all_pallets_without_system:path,
646		$xcm_config:path,
647		$checking_account:path,
648		$weight_to_fee:path,
649		$hrmp_channel_opener:path,
650		$sovereign_account_of:path,
651		$assets_pallet_instance:path,
652		$collator_session_key:expr,
653		$slot_durations:expr,
654		$existential_deposit:expr,
655		$unwrap_pallet_xcm_event:expr,
656		$unwrap_xcmp_queue_event:expr
657	) => {
658		#[test]
659		fn teleports_for_foreign_assets_works() {
660			const BOB: [u8; 32] = [2u8; 32];
661			let target_account = parachains_common::AccountId::from(BOB);
662			const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32];
663			let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER);
664
665			$crate::test_cases::teleports_for_foreign_assets_works::<
666				$runtime,
667				$all_pallets_without_system,
668				$xcm_config,
669				$checking_account,
670				$weight_to_fee,
671				$hrmp_channel_opener,
672				$sovereign_account_of,
673				$assets_pallet_instance
674			>(
675				$collator_session_key,
676				$slot_durations,
677				target_account,
678				$existential_deposit,
679				asset_owner,
680				$unwrap_pallet_xcm_event,
681				$unwrap_xcmp_queue_event
682			)
683		}
684	}
685);
686
687/// Test-case makes sure that `Runtime`'s `xcm::AssetTransactor` can handle native relay chain
688/// currency
689pub fn asset_transactor_transfer_with_local_consensus_currency_works<Runtime, XcmConfig>(
690	collator_session_keys: CollatorSessionKeys<Runtime>,
691	source_account: AccountIdOf<Runtime>,
692	target_account: AccountIdOf<Runtime>,
693	existential_deposit: BalanceOf<Runtime>,
694	additional_checks_before: Box<dyn Fn()>,
695	additional_checks_after: Box<dyn Fn()>,
696) where
697	Runtime: frame_system::Config
698		+ pallet_balances::Config
699		+ pallet_session::Config
700		+ pallet_xcm::Config
701		+ parachain_info::Config
702		+ pallet_collator_selection::Config
703		+ cumulus_pallet_parachain_system::Config
704		+ pallet_timestamp::Config,
705	AccountIdOf<Runtime>: Into<[u8; 32]>,
706	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
707	BalanceOf<Runtime>: From<Balance>,
708	XcmConfig: xcm_executor::Config,
709	<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
710	<Runtime as frame_system::Config>::AccountId:
711		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
712	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
713		From<<Runtime as frame_system::Config>::AccountId>,
714{
715	let unit = existential_deposit;
716
717	ExtBuilder::<Runtime>::default()
718		.with_collators(collator_session_keys.collators())
719		.with_session_keys(collator_session_keys.session_keys())
720		.with_balances(vec![(source_account.clone(), (BalanceOf::<Runtime>::from(10_u128) * unit))])
721		.with_tracing()
722		.build()
723		.execute_with(|| {
724			// check Balances before
725			assert_eq!(
726				<pallet_balances::Pallet<Runtime>>::free_balance(&source_account),
727				(BalanceOf::<Runtime>::from(10_u128) * unit)
728			);
729			assert_eq!(
730				<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
731				(BalanceOf::<Runtime>::zero() * unit)
732			);
733
734			// additional check before
735			additional_checks_before();
736
737			// transfer_asset (deposit/withdraw) ALICE -> BOB
738			let _ = RuntimeHelper::<XcmConfig>::do_transfer(
739				Location {
740					parents: 0,
741					interior: [AccountId32 { network: None, id: source_account.clone().into() }]
742						.into(),
743				},
744				Location {
745					parents: 0,
746					interior: [AccountId32 { network: None, id: target_account.clone().into() }]
747						.into(),
748				},
749				// local_consensus_currency_asset, e.g.: relaychain token (KSM, DOT, ...)
750				(
751					Location { parents: 1, interior: Here },
752					(BalanceOf::<Runtime>::from(1_u128) * unit).into(),
753				),
754			)
755			.expect("no error");
756
757			// check Balances after
758			assert_eq!(
759				<pallet_balances::Pallet<Runtime>>::free_balance(source_account),
760				(BalanceOf::<Runtime>::from(9_u128) * unit)
761			);
762			assert_eq!(
763				<pallet_balances::Pallet<Runtime>>::free_balance(target_account),
764				(BalanceOf::<Runtime>::from(1_u128) * unit)
765			);
766
767			additional_checks_after();
768		})
769}
770
771#[macro_export]
772macro_rules! include_asset_transactor_transfer_with_local_consensus_currency_works(
773	(
774		$runtime:path,
775		$xcm_config:path,
776		$collator_session_key:expr,
777		$existential_deposit:expr,
778		$additional_checks_before:expr,
779		$additional_checks_after:expr
780	) => {
781		#[test]
782		fn asset_transactor_transfer_with_local_consensus_currency_works() {
783			const ALICE: [u8; 32] = [1u8; 32];
784			let source_account = parachains_common::AccountId::from(ALICE);
785			const BOB: [u8; 32] = [2u8; 32];
786			let target_account = parachains_common::AccountId::from(BOB);
787
788			$crate::test_cases::asset_transactor_transfer_with_local_consensus_currency_works::<
789				$runtime,
790				$xcm_config
791			>(
792				$collator_session_key,
793				source_account,
794				target_account,
795				$existential_deposit,
796				$additional_checks_before,
797				$additional_checks_after
798			)
799		}
800	}
801);
802
803/// Test-case makes sure that `Runtime`'s `xcm::AssetTransactor` can handle native relay chain
804/// currency
805pub fn asset_transactor_transfer_with_pallet_assets_instance_works<
806	Runtime,
807	XcmConfig,
808	AssetsPalletInstance,
809	AssetId,
810	AssetIdConverter,
811>(
812	collator_session_keys: CollatorSessionKeys<Runtime>,
813	existential_deposit: BalanceOf<Runtime>,
814	asset_id: AssetId,
815	asset_owner: AccountIdOf<Runtime>,
816	alice_account: AccountIdOf<Runtime>,
817	bob_account: AccountIdOf<Runtime>,
818	charlie_account: AccountIdOf<Runtime>,
819	additional_checks_before: Box<dyn Fn()>,
820	additional_checks_after: Box<dyn Fn()>,
821) where
822	Runtime: frame_system::Config
823		+ pallet_balances::Config
824		+ pallet_session::Config
825		+ pallet_xcm::Config
826		+ parachain_info::Config
827		+ pallet_collator_selection::Config
828		+ cumulus_pallet_parachain_system::Config
829		+ pallet_assets::Config<AssetsPalletInstance>
830		+ pallet_timestamp::Config,
831	AccountIdOf<Runtime>: Into<[u8; 32]>,
832	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
833	BalanceOf<Runtime>: From<Balance>,
834	XcmConfig: xcm_executor::Config,
835	<Runtime as pallet_assets::Config<AssetsPalletInstance>>::AssetId:
836		From<AssetId> + Into<AssetId>,
837	<Runtime as pallet_assets::Config<AssetsPalletInstance>>::AssetIdParameter:
838		From<AssetId> + Into<AssetId>,
839	<Runtime as pallet_assets::Config<AssetsPalletInstance>>::Balance: From<Balance> + Into<u128>,
840	<Runtime as frame_system::Config>::AccountId:
841		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
842	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
843		From<<Runtime as frame_system::Config>::AccountId>,
844	AssetsPalletInstance: 'static,
845	AssetId: Clone,
846	AssetIdConverter: MaybeEquivalence<Location, AssetId>,
847{
848	ExtBuilder::<Runtime>::default()
849		.with_collators(collator_session_keys.collators())
850		.with_session_keys(collator_session_keys.session_keys())
851		.with_balances(vec![
852			(asset_owner.clone(), existential_deposit),
853			(alice_account.clone(), existential_deposit),
854			(bob_account.clone(), existential_deposit),
855		])
856		.with_tracing()
857		.build()
858		.execute_with(|| {
859			// create  some asset class
860			let asset_minimum_asset_balance = 3333333_u128;
861			let asset_id_as_location = AssetIdConverter::convert_back(&asset_id).unwrap();
862			assert_ok!(<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::force_create(
863				RuntimeHelper::<Runtime>::root_origin(),
864				asset_id.clone().into(),
865				asset_owner.clone().into(),
866				false,
867				asset_minimum_asset_balance.into()
868			));
869
870			// We first mint enough asset for the account to exist for assets
871			assert_ok!(<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::mint(
872				RuntimeHelper::<Runtime>::origin_of(asset_owner.clone()),
873				asset_id.clone().into(),
874				alice_account.clone().into(),
875				(6 * asset_minimum_asset_balance).into()
876			));
877
878			// check Assets before
879			assert_eq!(
880				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
881					asset_id.clone().into(),
882					&alice_account
883				),
884				(6 * asset_minimum_asset_balance).into()
885			);
886			assert_eq!(
887				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
888					asset_id.clone().into(),
889					&bob_account
890				),
891				0.into()
892			);
893			assert_eq!(
894				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
895					asset_id.clone().into(),
896					&charlie_account
897				),
898				0.into()
899			);
900			assert_eq!(
901				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
902					asset_id.clone().into(),
903					&asset_owner
904				),
905				0.into()
906			);
907			assert_eq!(
908				<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
909				existential_deposit
910			);
911			assert_eq!(
912				<pallet_balances::Pallet<Runtime>>::free_balance(&bob_account),
913				existential_deposit
914			);
915			assert_eq!(
916				<pallet_balances::Pallet<Runtime>>::free_balance(&charlie_account),
917				0.into()
918			);
919			assert_eq!(
920				<pallet_balances::Pallet<Runtime>>::free_balance(&asset_owner),
921				existential_deposit
922			);
923			additional_checks_before();
924
925			// transfer_asset (deposit/withdraw) ALICE -> CHARLIE (not ok - Charlie does not have
926			// ExistentialDeposit)
927			assert_noop!(
928				RuntimeHelper::<XcmConfig>::do_transfer(
929					Location {
930						parents: 0,
931						interior: [AccountId32 { network: None, id: alice_account.clone().into() }]
932							.into(),
933					},
934					Location {
935						parents: 0,
936						interior: [AccountId32 {
937							network: None,
938							id: charlie_account.clone().into()
939						}]
940						.into(),
941					},
942					(asset_id_as_location.clone(), asset_minimum_asset_balance),
943				),
944				XcmError::FailedToTransactAsset(Into::<&str>::into(
945					sp_runtime::TokenError::CannotCreate
946				))
947			);
948
949			// transfer_asset (deposit/withdraw) ALICE -> BOB (ok - has ExistentialDeposit)
950			assert!(matches!(
951				RuntimeHelper::<XcmConfig>::do_transfer(
952					Location {
953						parents: 0,
954						interior: [AccountId32 { network: None, id: alice_account.clone().into() }]
955							.into(),
956					},
957					Location {
958						parents: 0,
959						interior: [AccountId32 { network: None, id: bob_account.clone().into() }]
960							.into(),
961					},
962					(asset_id_as_location, asset_minimum_asset_balance),
963				),
964				Ok(_)
965			));
966
967			// check Assets after
968			assert_eq!(
969				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
970					asset_id.clone().into(),
971					&alice_account
972				),
973				(5 * asset_minimum_asset_balance).into()
974			);
975			assert_eq!(
976				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
977					asset_id.clone().into(),
978					&bob_account
979				),
980				asset_minimum_asset_balance.into()
981			);
982			assert_eq!(
983				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
984					asset_id.clone().into(),
985					&charlie_account
986				),
987				0.into()
988			);
989			assert_eq!(
990				<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
991					asset_id.into(),
992					&asset_owner
993				),
994				0.into()
995			);
996			assert_eq!(
997				<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
998				existential_deposit
999			);
1000			assert_eq!(
1001				<pallet_balances::Pallet<Runtime>>::free_balance(&bob_account),
1002				existential_deposit
1003			);
1004			assert_eq!(
1005				<pallet_balances::Pallet<Runtime>>::free_balance(&charlie_account),
1006				0.into()
1007			);
1008			assert_eq!(
1009				<pallet_balances::Pallet<Runtime>>::free_balance(&asset_owner),
1010				existential_deposit
1011			);
1012
1013			additional_checks_after();
1014		})
1015}
1016
1017#[macro_export]
1018macro_rules! include_asset_transactor_transfer_with_pallet_assets_instance_works(
1019	(
1020		$test_name:tt,
1021		$runtime:path,
1022		$xcm_config:path,
1023		$assets_pallet_instance:path,
1024		$asset_id:path,
1025		$asset_id_converter:path,
1026		$collator_session_key:expr,
1027		$existential_deposit:expr,
1028		$tested_asset_id:expr,
1029		$additional_checks_before:expr,
1030		$additional_checks_after:expr
1031	) => {
1032		#[test]
1033		fn $test_name() {
1034			const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32];
1035			let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER);
1036			const ALICE: [u8; 32] = [1u8; 32];
1037			let alice_account = parachains_common::AccountId::from(ALICE);
1038			const BOB: [u8; 32] = [2u8; 32];
1039			let bob_account = parachains_common::AccountId::from(BOB);
1040			const CHARLIE: [u8; 32] = [3u8; 32];
1041			let charlie_account = parachains_common::AccountId::from(CHARLIE);
1042
1043			$crate::test_cases::asset_transactor_transfer_with_pallet_assets_instance_works::<
1044				$runtime,
1045				$xcm_config,
1046				$assets_pallet_instance,
1047				$asset_id,
1048				$asset_id_converter
1049			>(
1050				$collator_session_key,
1051				$existential_deposit,
1052				$tested_asset_id,
1053				asset_owner,
1054				alice_account,
1055				bob_account,
1056				charlie_account,
1057				$additional_checks_before,
1058				$additional_checks_after
1059			)
1060		}
1061	}
1062);
1063
1064/// Test-case makes sure that `Runtime` can create and manage `ForeignAssets`
1065pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works<
1066	Runtime,
1067	XcmConfig,
1068	WeightToFee,
1069	SovereignAccountOf,
1070	ForeignAssetsPalletInstance,
1071	AssetId,
1072	AssetIdConverter,
1073>(
1074	collator_session_keys: CollatorSessionKeys<Runtime>,
1075	existential_deposit: BalanceOf<Runtime>,
1076	asset_deposit: BalanceOf<Runtime>,
1077	metadata_deposit_base: BalanceOf<Runtime>,
1078	metadata_deposit_per_byte: BalanceOf<Runtime>,
1079	alice_account: AccountIdOf<Runtime>,
1080	bob_account: AccountIdOf<Runtime>,
1081	runtime_call_encode: Box<
1082		dyn Fn(pallet_assets::Call<Runtime, ForeignAssetsPalletInstance>) -> Vec<u8>,
1083	>,
1084	unwrap_pallet_assets_event: Box<
1085		dyn Fn(Vec<u8>) -> Option<pallet_assets::Event<Runtime, ForeignAssetsPalletInstance>>,
1086	>,
1087	additional_checks_before: Box<dyn Fn()>,
1088	additional_checks_after: Box<dyn Fn()>,
1089) where
1090	Runtime: frame_system::Config
1091		+ pallet_balances::Config
1092		+ pallet_session::Config
1093		+ pallet_xcm::Config
1094		+ parachain_info::Config
1095		+ pallet_collator_selection::Config
1096		+ cumulus_pallet_parachain_system::Config
1097		+ pallet_assets::Config<ForeignAssetsPalletInstance>
1098		+ pallet_timestamp::Config,
1099	AccountIdOf<Runtime>: Into<[u8; 32]>,
1100	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
1101	BalanceOf<Runtime>: From<Balance>,
1102	XcmConfig: xcm_executor::Config,
1103	WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
1104	<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
1105	SovereignAccountOf: ConvertLocation<AccountIdOf<Runtime>>,
1106	<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
1107		From<AssetId> + Into<AssetId>,
1108	<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
1109		From<AssetId> + Into<AssetId>,
1110	<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
1111		From<Balance> + Into<u128>,
1112	<Runtime as frame_system::Config>::AccountId:
1113		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
1114	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
1115		From<<Runtime as frame_system::Config>::AccountId>,
1116	ForeignAssetsPalletInstance: 'static,
1117	AssetId: Clone,
1118	AssetIdConverter: MaybeEquivalence<Location, AssetId>,
1119{
1120	// foreign parachain with the same consensus currency as asset
1121	let foreign_asset_id_location = Location::new(1, [Parachain(2222), GeneralIndex(1234567)]);
1122	let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap();
1123
1124	// foreign creator, which can be sibling parachain to match ForeignCreators
1125	let foreign_creator = Location { parents: 1, interior: [Parachain(2222)].into() };
1126	let foreign_creator_as_account_id =
1127		SovereignAccountOf::convert_location(&foreign_creator).expect("");
1128
1129	// we want to buy execution with local relay chain currency
1130	let buy_execution_fee_amount =
1131		WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0));
1132	let buy_execution_fee =
1133		Asset { id: AssetId(Location::parent()), fun: Fungible(buy_execution_fee_amount) };
1134
1135	const ASSET_NAME: &str = "My super coin";
1136	const ASSET_SYMBOL: &str = "MY_S_COIN";
1137	let metadata_deposit_per_byte_eta = metadata_deposit_per_byte
1138		.saturating_mul(((ASSET_NAME.len() + ASSET_SYMBOL.len()) as u128).into());
1139
1140	ExtBuilder::<Runtime>::default()
1141		.with_collators(collator_session_keys.collators())
1142		.with_session_keys(collator_session_keys.session_keys())
1143		.with_balances(vec![(
1144			foreign_creator_as_account_id.clone(),
1145			existential_deposit +
1146				asset_deposit +
1147				metadata_deposit_base +
1148				metadata_deposit_per_byte_eta +
1149				buy_execution_fee_amount.into() +
1150				buy_execution_fee_amount.into(),
1151		)])
1152		.with_tracing()
1153		.build()
1154		.execute_with(|| {
1155			assert!(<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::asset_ids()
1156				.collect::<Vec<_>>()
1157				.is_empty());
1158			assert_eq!(
1159				<pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id),
1160				existential_deposit +
1161					asset_deposit + metadata_deposit_base +
1162					metadata_deposit_per_byte_eta +
1163					buy_execution_fee_amount.into() +
1164					buy_execution_fee_amount.into()
1165			);
1166			additional_checks_before();
1167
1168			// execute XCM with Transacts to create/manage foreign assets by foreign governance
1169			// prepare data for xcm::Transact(create)
1170			let foreign_asset_create = runtime_call_encode(pallet_assets::Call::<
1171				Runtime,
1172				ForeignAssetsPalletInstance,
1173			>::create {
1174				id: asset_id.clone().into(),
1175				// admin as sovereign_account
1176				admin: foreign_creator_as_account_id.clone().into(),
1177				min_balance: 1.into(),
1178			});
1179			// prepare data for xcm::Transact(set_metadata)
1180			let foreign_asset_set_metadata = runtime_call_encode(pallet_assets::Call::<
1181				Runtime,
1182				ForeignAssetsPalletInstance,
1183			>::set_metadata {
1184				id: asset_id.clone().into(),
1185				name: Vec::from(ASSET_NAME),
1186				symbol: Vec::from(ASSET_SYMBOL),
1187				decimals: 12,
1188			});
1189			// prepare data for xcm::Transact(set_team - change just freezer to Bob)
1190			let foreign_asset_set_team = runtime_call_encode(pallet_assets::Call::<
1191				Runtime,
1192				ForeignAssetsPalletInstance,
1193			>::set_team {
1194				id: asset_id.clone().into(),
1195				issuer: foreign_creator_as_account_id.clone().into(),
1196				admin: foreign_creator_as_account_id.clone().into(),
1197				freezer: bob_account.clone().into(),
1198			});
1199
1200			// lets simulate this was triggered by relay chain from local consensus sibling
1201			// parachain
1202			let xcm = Xcm(vec![
1203				WithdrawAsset(buy_execution_fee.clone().into()),
1204				BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
1205				Transact {
1206					origin_kind: OriginKind::Xcm,
1207					call: foreign_asset_create.into(),
1208					fallback_max_weight: None,
1209				},
1210				Transact {
1211					origin_kind: OriginKind::SovereignAccount,
1212					call: foreign_asset_set_metadata.into(),
1213					fallback_max_weight: None,
1214				},
1215				Transact {
1216					origin_kind: OriginKind::SovereignAccount,
1217					call: foreign_asset_set_team.into(),
1218					fallback_max_weight: None,
1219				},
1220				ExpectTransactStatus(MaybeErrorCode::Success),
1221			]);
1222
1223			// messages with different consensus should go through the local bridge-hub
1224			let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
1225
1226			// execute xcm as XcmpQueue would do
1227			let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
1228				foreign_creator.clone(),
1229				xcm,
1230				&mut hash,
1231				RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
1232				Weight::zero(),
1233			);
1234			assert_ok!(outcome.ensure_complete());
1235
1236			// check events
1237			let mut events = <frame_system::Pallet<Runtime>>::events()
1238				.into_iter()
1239				.filter_map(|e| unwrap_pallet_assets_event(e.event.encode()));
1240			assert!(events.any(|e| matches!(e, pallet_assets::Event::Created { .. })));
1241			assert!(events.any(|e| matches!(e, pallet_assets::Event::MetadataSet { .. })));
1242			assert!(events.any(|e| matches!(e, pallet_assets::Event::TeamChanged { .. })));
1243
1244			// check assets after
1245			assert!(!<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::asset_ids()
1246				.collect::<Vec<_>>()
1247				.is_empty());
1248
1249			// check update metadata
1250			use frame_support::traits::fungibles::roles::Inspect as InspectRoles;
1251			assert_eq!(
1252				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::owner(
1253					asset_id.clone().into()
1254				),
1255				Some(foreign_creator_as_account_id.clone())
1256			);
1257			assert_eq!(
1258				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::admin(
1259					asset_id.clone().into()
1260				),
1261				Some(foreign_creator_as_account_id.clone())
1262			);
1263			assert_eq!(
1264				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::issuer(
1265					asset_id.clone().into()
1266				),
1267				Some(foreign_creator_as_account_id.clone())
1268			);
1269			assert_eq!(
1270				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freezer(
1271					asset_id.clone().into()
1272				),
1273				Some(bob_account.clone())
1274			);
1275			assert!(
1276				<pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id) >=
1277					existential_deposit + buy_execution_fee_amount.into(),
1278				"Free balance: {:?} should be ge {:?}",
1279				<pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id),
1280				existential_deposit + buy_execution_fee_amount.into()
1281			);
1282			assert_metadata::<
1283				pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
1284				AccountIdOf<Runtime>,
1285			>(asset_id.clone(), ASSET_NAME, ASSET_SYMBOL, 12);
1286
1287			// check if changed freezer, can freeze
1288			assert_noop!(
1289				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freeze(
1290					RuntimeHelper::<Runtime>::origin_of(bob_account),
1291					asset_id.clone().into(),
1292					alice_account.clone().into()
1293				),
1294				pallet_assets::Error::<Runtime, ForeignAssetsPalletInstance>::NoAccount
1295			);
1296			assert_noop!(
1297				<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freeze(
1298					RuntimeHelper::<Runtime>::origin_of(foreign_creator_as_account_id.clone()),
1299					asset_id.into(),
1300					alice_account.into()
1301				),
1302				pallet_assets::Error::<Runtime, ForeignAssetsPalletInstance>::NoPermission
1303			);
1304
1305			// lets try create asset for different parachain(3333) (foreign_creator(2222) can create
1306			// just his assets)
1307			let foreign_asset_id_location =
1308				Location { parents: 1, interior: [Parachain(3333), GeneralIndex(1234567)].into() };
1309			let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap();
1310
1311			// prepare data for xcm::Transact(create)
1312			let foreign_asset_create = runtime_call_encode(pallet_assets::Call::<
1313				Runtime,
1314				ForeignAssetsPalletInstance,
1315			>::create {
1316				id: asset_id.into(),
1317				// admin as sovereign_account
1318				admin: foreign_creator_as_account_id.clone().into(),
1319				min_balance: 1.into(),
1320			});
1321			let xcm = Xcm(vec![
1322				WithdrawAsset(buy_execution_fee.clone().into()),
1323				BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
1324				Transact {
1325					origin_kind: OriginKind::Xcm,
1326					call: foreign_asset_create.into(),
1327					fallback_max_weight: None,
1328				},
1329				ExpectTransactStatus(MaybeErrorCode::from(DispatchError::BadOrigin.encode())),
1330			]);
1331
1332			// messages with different consensus should go through the local bridge-hub
1333			let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
1334
1335			// execute xcm as XcmpQueue would do
1336			let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
1337				foreign_creator,
1338				xcm,
1339				&mut hash,
1340				RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
1341				Weight::zero(),
1342			);
1343			assert_ok!(outcome.ensure_complete());
1344
1345			additional_checks_after();
1346		})
1347}
1348
1349#[macro_export]
1350macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works(
1351	(
1352		$runtime:path,
1353		$xcm_config:path,
1354		$weight_to_fee:path,
1355		$sovereign_account_of:path,
1356		$assets_pallet_instance:path,
1357		$asset_id:path,
1358		$asset_id_converter:path,
1359		$collator_session_key:expr,
1360		$existential_deposit:expr,
1361		$asset_deposit:expr,
1362		$metadata_deposit_base:expr,
1363		$metadata_deposit_per_byte:expr,
1364		$runtime_call_encode:expr,
1365		$unwrap_pallet_assets_event:expr,
1366		$additional_checks_before:expr,
1367		$additional_checks_after:expr
1368	) => {
1369		#[test]
1370		fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works() {
1371			const ALICE: [u8; 32] = [1u8; 32];
1372			let alice_account = parachains_common::AccountId::from(ALICE);
1373			const BOB: [u8; 32] = [2u8; 32];
1374			let bob_account = parachains_common::AccountId::from(BOB);
1375
1376			$crate::test_cases::create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works::<
1377				$runtime,
1378				$xcm_config,
1379				$weight_to_fee,
1380				$sovereign_account_of,
1381				$assets_pallet_instance,
1382				$asset_id,
1383				$asset_id_converter
1384			>(
1385				$collator_session_key,
1386				$existential_deposit,
1387				$asset_deposit,
1388				$metadata_deposit_base,
1389				$metadata_deposit_per_byte,
1390				alice_account,
1391				bob_account,
1392				$runtime_call_encode,
1393				$unwrap_pallet_assets_event,
1394				$additional_checks_before,
1395				$additional_checks_after
1396			)
1397		}
1398	}
1399);
1400
1401/// Test-case makes sure that `Runtime` can reserve-transfer asset to other parachains (where
1402/// teleport is not trusted)
1403pub fn reserve_transfer_native_asset_to_non_teleport_para_works<
1404	Runtime,
1405	AllPalletsWithoutSystem,
1406	XcmConfig,
1407	HrmpChannelOpener,
1408	HrmpChannelSource,
1409	LocationToAccountId,
1410>(
1411	collator_session_keys: CollatorSessionKeys<Runtime>,
1412	slot_durations: SlotDurations,
1413	existential_deposit: BalanceOf<Runtime>,
1414	alice_account: AccountIdOf<Runtime>,
1415	unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
1416	unwrap_xcmp_queue_event: Box<
1417		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
1418	>,
1419	weight_limit: WeightLimit,
1420) where
1421	Runtime: frame_system::Config
1422		+ pallet_balances::Config
1423		+ pallet_session::Config
1424		+ pallet_xcm::Config
1425		+ parachain_info::Config
1426		+ pallet_collator_selection::Config
1427		+ cumulus_pallet_parachain_system::Config
1428		+ cumulus_pallet_xcmp_queue::Config
1429		+ pallet_timestamp::Config,
1430	AllPalletsWithoutSystem:
1431		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
1432	AccountIdOf<Runtime>: Into<[u8; 32]>,
1433	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
1434	BalanceOf<Runtime>: From<Balance>,
1435	<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
1436	XcmConfig: xcm_executor::Config,
1437	LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
1438	<Runtime as frame_system::Config>::AccountId:
1439		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
1440	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
1441		From<<Runtime as frame_system::Config>::AccountId>,
1442	<Runtime as frame_system::Config>::AccountId: From<AccountId>,
1443	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
1444		Call = cumulus_pallet_parachain_system::Call<Runtime>,
1445	>,
1446	HrmpChannelSource: XcmpMessageSource,
1447{
1448	let runtime_para_id = 1000;
1449	ExtBuilder::<Runtime>::default()
1450		.with_collators(collator_session_keys.collators())
1451		.with_session_keys(collator_session_keys.session_keys())
1452		.with_tracing()
1453		.with_safe_xcm_version(3)
1454		.with_para_id(runtime_para_id.into())
1455		.build()
1456		.execute_with(|| {
1457			let mut alice = [0u8; 32];
1458			alice[0] = 1;
1459			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
1460				2,
1461				AccountId::from(alice).into(),
1462			);
1463
1464			// reserve-transfer native asset with local reserve to remote parachain (2345)
1465
1466			let other_para_id = 2345;
1467			let native_asset = Location::parent();
1468			let dest = Location::new(1, [Parachain(other_para_id)]);
1469			let mut dest_beneficiary = Location::new(1, [Parachain(other_para_id)])
1470				.appended_with(AccountId32 {
1471					network: None,
1472					id: sp_runtime::AccountId32::new([3; 32]).into(),
1473				})
1474				.unwrap();
1475			dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
1476
1477			let reserve_account = LocationToAccountId::convert_location(&dest)
1478				.expect("Sovereign account for reserves");
1479			let balance_to_transfer = 1_000_000_000_000_u128;
1480
1481			// open HRMP to other parachain
1482			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
1483				runtime_para_id.into(),
1484				other_para_id.into(),
1485				included_head,
1486				&alice,
1487				&slot_durations,
1488			);
1489
1490			// we calculate exact delivery fees _after_ sending the message by weighing the sent
1491			// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
1492			// then verify the arithmetics check out on final balance.
1493			let delivery_fees_buffer = 40_000_000_000u128;
1494			// drip 2xED + transfer_amount + delivery_fees_buffer to Alice account
1495			let alice_account_init_balance = existential_deposit.saturating_mul(2.into()) +
1496				balance_to_transfer.into() +
1497				delivery_fees_buffer.into();
1498			let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
1499				&alice_account,
1500				alice_account_init_balance,
1501			);
1502			// SA of target location needs to have at least ED, otherwise making reserve fails
1503			let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
1504				&reserve_account,
1505				existential_deposit,
1506			);
1507
1508			// we just check here, that user retains enough balance after withdrawal
1509			// and also we check if `balance_to_transfer` is more than `existential_deposit`,
1510			assert!(
1511				(<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
1512					balance_to_transfer.into()) >=
1513					existential_deposit
1514			);
1515			// SA has just ED
1516			assert_eq!(
1517				<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
1518				existential_deposit
1519			);
1520
1521			// local native asset (pallet_balances)
1522			let asset_to_transfer =
1523				Asset { fun: Fungible(balance_to_transfer.into()), id: AssetId(native_asset) };
1524
1525			// pallet_xcm call reserve transfer
1526			assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
1527				RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
1528				Box::new(dest.clone().into_versioned()),
1529				Box::new(dest_beneficiary.clone().into_versioned()),
1530				Box::new(VersionedAssets::from(Assets::from(asset_to_transfer))),
1531				0,
1532				weight_limit,
1533			));
1534
1535			// check events
1536			// check pallet_xcm attempted
1537			RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
1538				&unwrap_pallet_xcm_event,
1539				|outcome| {
1540					assert_ok!(outcome.ensure_complete());
1541				},
1542			);
1543
1544			// check that xcm was sent
1545			let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
1546				.into_iter()
1547				.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
1548				.find_map(|e| match e {
1549					cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
1550						Some(message_hash),
1551					_ => None,
1552				});
1553
1554			// read xcm
1555			let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
1556				other_para_id.into(),
1557			)
1558			.unwrap();
1559
1560			let delivery_fees = get_fungible_delivery_fees::<
1561				<XcmConfig as xcm_executor::Config>::XcmSender,
1562			>(dest.clone(), Xcm::try_from(xcm_sent.clone()).unwrap());
1563
1564			assert_eq!(
1565				xcm_sent_message_hash,
1566				Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
1567			);
1568			let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
1569
1570			// check sent XCM Program to other parachain
1571			println!("reserve_transfer_native_asset_works sent xcm: {:?}", xcm_sent);
1572			let reserve_assets_deposited = Assets::from(vec![Asset {
1573				id: AssetId(Location { parents: 1, interior: Here }),
1574				fun: Fungible(1000000000000),
1575			}]);
1576
1577			assert_matches_reserve_asset_deposited_instructions(
1578				&mut xcm_sent,
1579				&reserve_assets_deposited,
1580				&dest_beneficiary,
1581			);
1582
1583			// check alice account decreased by balance_to_transfer ( + delivery_fees)
1584			assert_eq!(
1585				<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
1586				alice_account_init_balance - balance_to_transfer.into() - delivery_fees.into()
1587			);
1588
1589			// check reserve account
1590			// check reserve account increased by balance_to_transfer
1591			assert_eq!(
1592				<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
1593				existential_deposit + balance_to_transfer.into()
1594			);
1595		})
1596}