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