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