penpal_runtime/
xcm_config.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3
4// Cumulus is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Cumulus is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Holds the XCM specific configuration that would otherwise be in lib.rs
18//!
19//! This configuration dictates how the Penpal chain will communicate with other chains.
20//!
21//! One of the main uses of the penpal chain will be to be a benefactor of reserve asset transfers
22//! with Asset Hub as the reserve. At present no derivative tokens are minted on receipt of a
23//! `ReserveAssetTransferDeposited` message but that will but the intension will be to support this
24//! soon.
25use super::{
26	AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Authorship, Balance,
27	Balances, CollatorSelection, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance,
28	ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
29	WeightToFee, XcmpQueue,
30};
31use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee};
32use assets_common::TrustBackedAssetsAsLocation;
33use core::marker::PhantomData;
34use frame_support::{
35	parameter_types,
36	traits::{
37		tokens::imbalance::ResolveAssetTo, ConstU32, Contains, ContainsPair, Everything,
38		EverythingBut, Get, Nothing, PalletInfoAccess,
39	},
40	weights::Weight,
41};
42use frame_system::EnsureRoot;
43use pallet_xcm::XcmPassthrough;
44use parachains_common::{xcm_config::AssetFeeAsExistentialDepositMultiplier, TREASURY_PALLET_ID};
45use polkadot_parachain_primitives::primitives::Sibling;
46use polkadot_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice};
47use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor;
48use sp_runtime::traits::{AccountIdConversion, ConvertInto, Identity, TryConvertInto};
49use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH};
50use xcm_builder::{
51	AccountId32Aliases, AliasOriginRootUsingFilter, AllowHrmpNotificationsFromRelayChain,
52	AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom,
53	AsPrefixedGeneralIndex, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily,
54	EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter,
55	FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete,
56	LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative,
57	SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia,
58	SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter,
59	SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId,
60	UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents,
61};
62use xcm_executor::{traits::JustTry, XcmExecutor};
63
64parameter_types! {
65	pub const RelayLocation: Location = Location::parent();
66	// Local native currency which is stored in `pallet_balances`
67	pub const PenpalNativeCurrency: Location = Location::here();
68	// The Penpal runtime is utilized for testing with various environment setups.
69	// This storage item allows us to customize the `NetworkId` where Penpal is deployed.
70	// By default, it is set to `Westend Network` and can be changed using `System::set_storage`.
71	pub storage RelayNetworkId: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH);
72	pub RelayNetwork: Option<NetworkId> = Some(RelayNetworkId::get());
73	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
74	pub UniversalLocation: InteriorLocation = [
75		GlobalConsensus(RelayNetworkId::get()),
76		Parachain(ParachainInfo::parachain_id().into())
77	].into();
78	pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating();
79	pub StakingPot: AccountId = CollatorSelection::account_id();
80	pub TrustBackedAssetsPalletIndex: u8 = <Assets as PalletInfoAccess>::index() as u8;
81	pub TrustBackedAssetsPalletLocation: Location =
82		PalletInstance(TrustBackedAssetsPalletIndex::get()).into();
83}
84
85/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
86/// when determining ownership of accounts for asset transacting and when attempting to use XCM
87/// `Transact` in order to determine the dispatch Origin.
88pub type LocationToAccountId = (
89	// The parent (Relay-chain) origin converts to the parent `AccountId`.
90	ParentIsPreset<AccountId>,
91	// Sibling parachain origins convert to AccountId via the `ParaId::into`.
92	SiblingParachainConvertsVia<Sibling, AccountId>,
93	// Straight up local `AccountId32` origins just alias directly to `AccountId`.
94	AccountId32Aliases<RelayNetwork, AccountId>,
95	// Foreign locations alias into accounts according to a hash of their standard description.
96	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
97	// Different global consensus parachain sovereign account.
98	// (Used for over-bridge transfers and reserve processing)
99	GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>,
100	// Ethereum contract sovereign account.
101	// (Used to get convert ethereum contract locations to sovereign account)
102	EthereumLocationsConverterFor<AccountId>,
103);
104
105/// Means for transacting assets on this chain.
106pub type FungibleTransactor = FungibleAdapter<
107	// Use this currency:
108	Balances,
109	// Use this currency when it is a fungible asset matching the given location or name:
110	IsConcrete<PenpalNativeCurrency>,
111	// Do a simple punn to convert an AccountId32 Location into a native chain account ID:
112	LocationToAccountId,
113	// Our chain's account ID type (we can't get away without mentioning it explicitly):
114	AccountId,
115	// We don't track any teleports.
116	(),
117>;
118
119/// Means for transacting assets besides the native currency on this chain.
120pub type FungiblesTransactor = FungiblesAdapter<
121	// Use this fungibles implementation:
122	Assets,
123	// Use this currency when it is a fungible asset matching the given location or name:
124	(
125		ConvertedConcreteId<
126			AssetIdPalletAssets,
127			Balance,
128			AsPrefixedGeneralIndex<AssetsPalletLocation, AssetIdPalletAssets, JustTry>,
129			JustTry,
130		>,
131		ConvertedConcreteId<
132			AssetIdPalletAssets,
133			Balance,
134			AsPrefixedGeneralIndex<
135				SystemAssetHubAssetsPalletLocation,
136				AssetIdPalletAssets,
137				JustTry,
138			>,
139			JustTry,
140		>,
141	),
142	// Convert an XCM Location into a local account id:
143	LocationToAccountId,
144	// Our chain's account ID type (we can't get away without mentioning it explicitly):
145	AccountId,
146	// We only want to allow teleports of known assets. We use non-zero issuance as an indication
147	// that this asset is known.
148	LocalMint<NonZeroIssuance<AccountId, Assets>>,
149	// The account to use for tracking teleports.
150	CheckingAccount,
151>;
152
153// Using the latest `Location`, we don't need to worry about migrations for Penpal.
154pub type ForeignAssetsAssetId = Location;
155pub type ForeignAssetsConvertedConcreteId = xcm_builder::MatchedConvertedConcreteId<
156	Location,
157	Balance,
158	EverythingBut<(
159		// Here we rely on fact that something like this works:
160		// assert!(Location::new(1,
161		// [Parachain(100)]).starts_with(&Location::parent()));
162		// assert!([Parachain(100)].into().starts_with(&Here));
163		StartsWith<assets_common::matching::LocalLocationPattern>,
164	)>,
165	Identity,
166	TryConvertInto,
167>;
168
169/// Means for transacting foreign assets from different global consensus.
170pub type ForeignFungiblesTransactor = FungiblesAdapter<
171	// Use this fungibles implementation:
172	ForeignAssets,
173	// Use this currency when it is a fungible asset matching the given location or name:
174	ForeignAssetsConvertedConcreteId,
175	// Convert an XCM Location into a local account id:
176	LocationToAccountId,
177	// Our chain's account ID type (we can't get away without mentioning it explicitly):
178	AccountId,
179	// We don't need to check teleports here.
180	NoChecking,
181	// The account to use for tracking teleports.
182	CheckingAccount,
183>;
184
185/// Means for transacting assets on this chain.
186pub type AssetTransactors = (FungibleTransactor, ForeignFungiblesTransactor, FungiblesTransactor);
187
188/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
189/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
190/// biases the kind of local `Origin` it will become.
191pub type XcmOriginToTransactDispatchOrigin = (
192	// Sovereign account converter; this attempts to derive an `AccountId` from the origin location
193	// using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
194	// foreign chains who want to have a local sovereign account on this chain which they control.
195	SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
196	// Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when
197	// recognized.
198	RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
199	// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
200	// recognized.
201	SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
202	// Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a
203	// transaction from the Root origin.
204	ParentAsSuperuser<RuntimeOrigin>,
205	// Native signed account converter; this just converts an `AccountId32` origin into a normal
206	// `RuntimeOrigin::Signed` origin of the same 32-byte value.
207	SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
208	// Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
209	XcmPassthrough<RuntimeOrigin>,
210);
211
212parameter_types! {
213	// One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
214	pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
215	pub const MaxInstructions: u32 = 100;
216	pub const MaxAssetsIntoHolding: u32 = 64;
217	pub XcmAssetFeesReceiver: Option<AccountId> = Authorship::author();
218}
219
220pub struct ParentOrParentsExecutivePlurality;
221impl Contains<Location> for ParentOrParentsExecutivePlurality {
222	fn contains(location: &Location) -> bool {
223		matches!(location.unpack(), (1, []) | (1, [Plurality { id: BodyId::Executive, .. }]))
224	}
225}
226
227pub type Barrier = TrailingSetTopicAsId<(
228	TakeWeightCredit,
229	// Expected responses are OK.
230	AllowKnownQueryResponses<PolkadotXcm>,
231	// Allow XCMs with some computed origins to pass through.
232	WithComputedOrigin<
233		(
234			// If the message is one that immediately attempts to pay for execution, then
235			// allow it.
236			AllowTopLevelPaidExecutionFrom<Everything>,
237			// Subscriptions for version tracking are OK.
238			AllowSubscriptionsFrom<Everything>,
239			// HRMP notifications from the relay chain are OK.
240			AllowHrmpNotificationsFromRelayChain,
241		),
242		UniversalLocation,
243		ConstU32<8>,
244	>,
245)>;
246
247/// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`.
248pub type AccountIdOf<R> = <R as frame_system::Config>::AccountId;
249
250/// Asset filter that allows all assets from a certain location matching asset id.
251pub struct AssetPrefixFrom<Prefix, Origin>(PhantomData<(Prefix, Origin)>);
252impl<Prefix, Origin> ContainsPair<Asset, Location> for AssetPrefixFrom<Prefix, Origin>
253where
254	Prefix: Get<Location>,
255	Origin: Get<Location>,
256{
257	fn contains(asset: &Asset, origin: &Location) -> bool {
258		let loc = Origin::get();
259		&loc == origin &&
260			matches!(asset, Asset { id: AssetId(asset_loc), fun: Fungible(_a) }
261			if asset_loc.starts_with(&Prefix::get()))
262	}
263}
264
265type AssetsFrom<T> = AssetPrefixFrom<T, T>;
266
267/// Asset filter that allows native/relay asset if coming from a certain location.
268pub struct NativeAssetFrom<T>(PhantomData<T>);
269impl<T: Get<Location>> ContainsPair<Asset, Location> for NativeAssetFrom<T> {
270	fn contains(asset: &Asset, origin: &Location) -> bool {
271		let loc = T::get();
272		&loc == origin &&
273			matches!(asset, Asset { id: AssetId(asset_loc), fun: Fungible(_a) }
274			if *asset_loc == Location::from(Parent))
275	}
276}
277
278// This asset can be added to AH as Asset and reserved transfer between Penpal and AH
279pub const RESERVABLE_ASSET_ID: u32 = 1;
280// This asset can be added to AH as ForeignAsset and teleported between Penpal and AH
281pub const TELEPORTABLE_ASSET_ID: u32 = 2;
282
283pub const ASSETS_PALLET_ID: u8 = 50;
284pub const ASSET_HUB_ID: u32 = 1000;
285
286pub const USDT_ASSET_ID: u128 = 1984;
287
288parameter_types! {
289	/// The location that this chain recognizes as the Relay network's Asset Hub.
290	pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]);
291	// the Relay Chain's Asset Hub's Assets pallet index
292	pub SystemAssetHubAssetsPalletLocation: Location =
293		Location::new(1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID)]);
294	pub AssetsPalletLocation: Location =
295		Location::new(0, [PalletInstance(ASSETS_PALLET_ID)]);
296	pub CheckingAccount: AccountId = PolkadotXcm::check_account();
297	pub LocalTeleportableToAssetHub: Location = Location::new(
298		0,
299		[PalletInstance(ASSETS_PALLET_ID), GeneralIndex(TELEPORTABLE_ASSET_ID.into())]
300	);
301	pub LocalReservableFromAssetHub: Location = Location::new(
302		1,
303		[Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())]
304	);
305	pub UsdtFromAssetHub: Location = Location::new(
306		1,
307		[Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ASSET_ID)],
308	);
309
310	/// The Penpal runtime is utilized for testing with various environment setups.
311	/// This storage item provides the opportunity to customize testing scenarios
312	/// by configuring the trusted asset from the `SystemAssetHub`.
313	///
314	/// By default, it is configured as a `SystemAssetHubLocation` and can be modified using `System::set_storage`.
315	pub storage CustomizableAssetFromSystemAssetHub: Location = SystemAssetHubLocation::get();
316}
317
318/// Accepts asset with ID `AssetLocation` and is coming from `Origin` chain.
319pub struct AssetFromChain<AssetLocation, Origin>(PhantomData<(AssetLocation, Origin)>);
320impl<AssetLocation: Get<Location>, Origin: Get<Location>> ContainsPair<Asset, Location>
321	for AssetFromChain<AssetLocation, Origin>
322{
323	fn contains(asset: &Asset, origin: &Location) -> bool {
324		log::trace!(target: "xcm::contains", "AssetFromChain asset: {:?}, origin: {:?}", asset, origin);
325		*origin == Origin::get() &&
326			matches!(asset.id.clone(), AssetId(id) if id == AssetLocation::get())
327	}
328}
329
330pub type TrustedReserves = (
331	NativeAsset,
332	AssetsFrom<SystemAssetHubLocation>,
333	NativeAssetFrom<SystemAssetHubLocation>,
334	AssetPrefixFrom<CustomizableAssetFromSystemAssetHub, SystemAssetHubLocation>,
335);
336pub type TrustedTeleporters =
337	(AssetFromChain<LocalTeleportableToAssetHub, SystemAssetHubLocation>,);
338
339/// `AssetId`/`Balance` converter for `TrustBackedAssets`.
340pub type TrustBackedAssetsConvertedConcreteId =
341	assets_common::TrustBackedAssetsConvertedConcreteId<AssetsPalletLocation, Balance>;
342
343/// Asset converter for pool assets.
344/// Used to convert assets in pools to the asset required for fee payment.
345/// The pool must be between the first asset and the one required for fee payment.
346/// This type allows paying fees with any asset in a pool with the asset required for fee payment.
347pub type PoolAssetsExchanger = SingleAssetExchangeAdapter<
348	crate::AssetConversion,
349	crate::NativeAndAssets,
350	(
351		TrustBackedAssetsAsLocation<
352			TrustBackedAssetsPalletLocation,
353			Balance,
354			xcm::latest::Location,
355		>,
356		ForeignAssetsConvertedConcreteId,
357	),
358	AccountId,
359>;
360
361pub struct XcmConfig;
362impl xcm_executor::Config for XcmConfig {
363	type RuntimeCall = RuntimeCall;
364	type XcmSender = XcmRouter;
365	// How to withdraw and deposit an asset.
366	type AssetTransactor = AssetTransactors;
367	type OriginConverter = XcmOriginToTransactDispatchOrigin;
368	type IsReserve = TrustedReserves;
369	// no teleport trust established with other chains
370	type IsTeleporter = TrustedTeleporters;
371	type UniversalLocation = UniversalLocation;
372	type Barrier = Barrier;
373	type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
374	type Trader = (
375		UsingComponents<WeightToFee, RelayLocation, AccountId, Balances, ToAuthor<Runtime>>,
376		cumulus_primitives_utility::SwapFirstAssetTrader<
377			RelayLocation,
378			crate::AssetConversion,
379			WeightToFee,
380			crate::NativeAndAssets,
381			(
382				TrustBackedAssetsAsLocation<
383					TrustBackedAssetsPalletLocation,
384					Balance,
385					xcm::latest::Location,
386				>,
387				ForeignAssetsConvertedConcreteId,
388			),
389			ResolveAssetTo<StakingPot, crate::NativeAndAssets>,
390			AccountId,
391		>,
392	);
393	type ResponseHandler = PolkadotXcm;
394	type AssetTrap = PolkadotXcm;
395	type AssetClaims = PolkadotXcm;
396	type SubscriptionService = PolkadotXcm;
397	type PalletInstancesInfo = AllPalletsWithSystem;
398	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
399	type AssetLocker = ();
400	type AssetExchanger = PoolAssetsExchanger;
401	type FeeManager = XcmFeeManagerFromComponents<
402		(),
403		SendXcmFeeToAccount<Self::AssetTransactor, TreasuryAccount>,
404	>;
405	type MessageExporter = ();
406	type UniversalAliases = Nothing;
407	type CallDispatcher = RuntimeCall;
408	type SafeCallFilter = Everything;
409	// We allow trusted Asset Hub root to alias other locations.
410	type Aliasers = AliasOriginRootUsingFilter<SystemAssetHubLocation, Everything>;
411	type TransactionalProcessor = FrameTransactionalProcessor;
412	type HrmpNewChannelOpenRequestHandler = ();
413	type HrmpChannelAcceptedHandler = ();
414	type HrmpChannelClosingHandler = ();
415	type XcmRecorder = PolkadotXcm;
416}
417
418/// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance.
419pub type ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger =
420	AssetFeeAsExistentialDepositMultiplier<
421		Runtime,
422		WeightToFee,
423		pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto, ForeignAssetsInstance>,
424		ForeignAssetsInstance,
425	>;
426
427/// No local origins on this chain are allowed to dispatch XCM sends/executions.
428pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
429
430pub type PriceForParentDelivery =
431	ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, ParachainSystem>;
432
433/// The means for routing XCM messages which are not for local execution into the right message
434/// queues.
435pub type XcmRouter = WithUniqueTopic<(
436	// Two routers - use UMP to communicate with the relay chain:
437	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, PriceForParentDelivery>,
438	// ..and XCMP to communicate with the sibling chains.
439	XcmpQueue,
440)>;
441
442impl pallet_xcm::Config for Runtime {
443	type RuntimeEvent = RuntimeEvent;
444	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
445	type XcmRouter = XcmRouter;
446	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
447	type XcmExecuteFilter = Everything;
448	type XcmExecutor = XcmExecutor<XcmConfig>;
449	type XcmTeleportFilter = Everything;
450	type XcmReserveTransferFilter = Everything;
451	type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
452	type UniversalLocation = UniversalLocation;
453	type RuntimeOrigin = RuntimeOrigin;
454	type RuntimeCall = RuntimeCall;
455
456	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
457	// ^ Override for AdvertisedXcmVersion default
458	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
459	type Currency = Balances;
460	type CurrencyMatcher = ();
461	type TrustedLockers = ();
462	type SovereignAccountOf = LocationToAccountId;
463	type MaxLockers = ConstU32<8>;
464	type WeightInfo = pallet_xcm::TestWeightInfo;
465	type AdminOrigin = EnsureRoot<AccountId>;
466	type MaxRemoteLockConsumers = ConstU32<0>;
467	type RemoteLockConsumerIdentifier = ();
468}
469
470impl cumulus_pallet_xcm::Config for Runtime {
471	type RuntimeEvent = RuntimeEvent;
472	type XcmExecutor = XcmExecutor<XcmConfig>;
473}
474
475/// Simple conversion of `u32` into an `AssetId` for use in benchmarking.
476pub struct XcmBenchmarkHelper;
477#[cfg(feature = "runtime-benchmarks")]
478impl pallet_assets::BenchmarkHelper<ForeignAssetsAssetId> for XcmBenchmarkHelper {
479	fn create_asset_id_parameter(id: u32) -> ForeignAssetsAssetId {
480		Location::new(1, [Parachain(id)])
481	}
482}