// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Cumulus is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . //! Holds the XCM specific configuration that would otherwise be in lib.rs //! //! This configuration dictates how the Penpal chain will communicate with other chains. //! //! One of the main uses of the penpal chain will be to be a benefactor of reserve asset transfers //! with Asset Hub as the reserve. At present no derivative tokens are minted on receipt of a //! `ReserveAssetTransferDeposited` message but that will but the intension will be to support this //! soon. use super::{ AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Authorship, Balance, Balances, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee}; use core::marker::PhantomData; use frame_support::{ parameter_types, traits::{ConstU32, Contains, ContainsPair, Everything, EverythingBut, Get, Nothing}, weights::Weight, }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{xcm_config::AssetFeeAsExistentialDepositMultiplier, TREASURY_PALLET_ID}; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice}; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); // Local native currency which is stored in `pallet_balances`` pub const PenpalNativeCurrency: Location = Location::here(); // The Penpal runtime is utilized for testing with various environment setups. // This storage item allows us to customize the `NetworkId` where Penpal is deployed. // By default, it is set to `NetworkId::Rococo` and can be changed using `System::set_storage`. pub storage RelayNetworkId: NetworkId = NetworkId::Westend; pub RelayNetwork: Option = Some(RelayNetworkId::get()); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [ GlobalConsensus(RelayNetworkId::get()), Parachain(ParachainInfo::parachain_id().into()) ].into(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch Origin. pub type LocationToAccountId = ( // The parent (Relay-chain) origin converts to the parent `AccountId`. ParentIsPreset, // Sibling parachain origins convert to AccountId via the `ParaId::into`. SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, ); /// Means for transacting assets on this chain. pub type CurrencyTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: IsConcrete, // Do a simple punn to convert an AccountId32 Location into a native chain account ID: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, // We don't track any teleports. (), >; /// Means for transacting assets besides the native currency on this chain. pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: ( ConvertedConcreteId< AssetIdPalletAssets, Balance, AsPrefixedGeneralIndex, JustTry, >, ConvertedConcreteId< AssetIdPalletAssets, Balance, AsPrefixedGeneralIndex< SystemAssetHubAssetsPalletLocation, AssetIdPalletAssets, JustTry, >, JustTry, >, ), // Convert an XCM Location into a local account id: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. LocalMint>, // The account to use for tracking teleports. CheckingAccount, >; pub type ForeignAssetsConvertedConcreteId = assets_common::LocationConvertedConcreteId< EverythingBut<( // Here we rely on fact that something like this works: // assert!(Location::new(1, // [Parachain(100)]).starts_with(&Location::parent())); // assert!([Parachain(100)].into().starts_with(&Here)); StartsWith, )>, Balance, >; /// Means for transacting foreign assets from different global consensus. pub type ForeignFungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: ForeignAssets, // Use this currency when it is a fungible asset matching the given location or name: ForeignAssetsConvertedConcreteId, // Convert an XCM Location into a local account id: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, // We don't need to check teleports here. NoChecking, // The account to use for tracking teleports. CheckingAccount, >; /// Means for transacting assets on this chain. pub type AssetTransactors = (CurrencyTransactor, ForeignFungiblesTransactor, FungiblesTransactor); /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can /// biases the kind of local `Origin` it will become. pub type XcmOriginToTransactDispatchOrigin = ( // Sovereign account converter; this attempts to derive an `AccountId` from the origin location // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for // foreign chains who want to have a local sovereign account on this chain which they control. SovereignSignedViaLocation, // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when // recognized. RelayChainAsNative, // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when // recognized. SiblingParachainAsNative, // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a // transaction from the Root origin. ParentAsSuperuser, // Native signed account converter; this just converts an `AccountId32` origin into a normal // `RuntimeOrigin::Signed` origin of the same 32-byte value. SignedAccountId32AsNative, // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. XcmPassthrough, ); parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; pub XcmAssetFeesReceiver: Option = Authorship::author(); } pub struct ParentOrParentsExecutivePlurality; impl Contains for ParentOrParentsExecutivePlurality { fn contains(location: &Location) -> bool { matches!(location.unpack(), (1, []) | (1, [Plurality { id: BodyId::Executive, .. }])) } } pub type Barrier = TrailingSetTopicAsId<( TakeWeightCredit, // Expected responses are OK. AllowKnownQueryResponses, // Allow XCMs with some computed origins to pass through. WithComputedOrigin< ( // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), UniversalLocation, ConstU32<8>, >, )>; /// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`. pub type AccountIdOf = ::AccountId; /// Asset filter that allows all assets from a certain location matching asset id. pub struct AssetPrefixFrom(PhantomData<(Prefix, Origin)>); impl ContainsPair for AssetPrefixFrom where Prefix: Get, Origin: Get, { fn contains(asset: &Asset, origin: &Location) -> bool { let loc = Origin::get(); &loc == origin && matches!(asset, Asset { id: AssetId(asset_loc), fun: Fungible(_a) } if asset_loc.starts_with(&Prefix::get())) } } type AssetsFrom = AssetPrefixFrom; /// Asset filter that allows native/relay asset if coming from a certain location. pub struct NativeAssetFrom(PhantomData); impl> ContainsPair for NativeAssetFrom { fn contains(asset: &Asset, origin: &Location) -> bool { let loc = T::get(); &loc == origin && matches!(asset, Asset { id: AssetId(asset_loc), fun: Fungible(_a) } if *asset_loc == Location::from(Parent)) } } // This asset can be added to AH as Asset and reserved transfer between Penpal and AH pub const RESERVABLE_ASSET_ID: u32 = 1; // This asset can be added to AH as ForeignAsset and teleported between Penpal and AH pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const ASSETS_PALLET_ID: u8 = 50; pub const ASSET_HUB_ID: u32 = 1000; parameter_types! { /// The location that this chain recognizes as the Relay network's Asset Hub. pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]); // the Relay Chain's Asset Hub's Assets pallet index pub SystemAssetHubAssetsPalletLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID)]); pub AssetsPalletLocation: Location = Location::new(0, [PalletInstance(ASSETS_PALLET_ID)]); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub LocalTeleportableToAssetHub: Location = Location::new( 0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(TELEPORTABLE_ASSET_ID.into())] ); pub LocalReservableFromAssetHub: Location = Location::new( 1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())] ); /// The Penpal runtime is utilized for testing with various environment setups. /// This storage item provides the opportunity to customize testing scenarios /// by configuring the trusted asset from the `SystemAssetHub`. /// /// By default, it is configured as a `SystemAssetHubLocation` and can be modified using `System::set_storage`. pub storage CustomizableAssetFromSystemAssetHub: Location = SystemAssetHubLocation::get(); } /// Accepts asset with ID `AssetLocation` and is coming from `Origin` chain. pub struct AssetFromChain(PhantomData<(AssetLocation, Origin)>); impl, Origin: Get> ContainsPair for AssetFromChain { fn contains(asset: &Asset, origin: &Location) -> bool { log::trace!(target: "xcm::contains", "AssetFromChain asset: {:?}, origin: {:?}", asset, origin); *origin == Origin::get() && matches!(asset.id.clone(), AssetId(id) if id == AssetLocation::get()) } } pub type TrustedReserves = ( NativeAsset, AssetsFrom, NativeAssetFrom, AssetPrefixFrom, ); pub type TrustedTeleporters = (AssetFromChain,); pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; // How to withdraw and deposit an asset. type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = TrustedReserves; // no teleport trust established with other chains type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = ( UsingComponents>, // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated // `pallet_assets` instance - `ForeignAssets`. cumulus_primitives_utility::TakeFirstAssetTrader< AccountId, ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignAssetsConvertedConcreteId, ForeignAssets, cumulus_primitives_utility::XcmFeesTo32ByteAccount< ForeignFungiblesTransactor, AccountId, XcmAssetFeesReceiver, >, >, ); type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); type FeeManager = XcmFeeManagerFromComponents< (), XcmFeeToAccount, >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); } /// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. pub type ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger = AssetFeeAsExistentialDepositMultiplier< Runtime, WeightToFee, pallet_assets::BalanceToAssetBalance, ForeignAssetsInstance, >; /// No local origins on this chain are allowed to dispatch XCM sends/executions. pub type LocalOriginToLocation = SignedToAccountId32; pub type PriceForParentDelivery = ExponentialPrice; /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Everything; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; type UniversalLocation = UniversalLocation; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; // ^ Override for AdvertisedXcmVersion default type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type Currency = Balances; type CurrencyMatcher = (); type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pallet_xcm::TestWeightInfo; type AdminOrigin = EnsureRoot; type MaxRemoteLockConsumers = ConstU32<0>; type RemoteLockConsumerIdentifier = (); } impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { fn create_asset_id_parameter(id: u32) -> xcm::v3::Location { xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) } }