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