// This file is part of Pezcumulus. // SPDX-License-Identifier: Unlicense // This is free and unencumbered software released into the public domain. // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. We make this dedication for the benefit // of the public at large and to the detriment of our heirs and // successors. We intend this dedication to be an overt act of // relinquishment in perpetuity of all present and future rights to this // software under copyright law. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // For more information, please refer to //! 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, CollatorSelection, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, PezkuwiXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, RuntimeOrigin, TeyrchainInfo, TeyrchainSystem, WeightToFee, XcmpQueue, }; use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee}; use assets_common::TrustBackedAssetsAsLocation; use core::marker::PhantomData; use pezframe_support::{ parameter_types, traits::{ fungible::HoldConsideration, tokens::imbalance::ResolveAssetTo, ConstU32, Contains, ContainsPair, Equals, Everything, EverythingBut, Get, LinearStoragePrice, Nothing, PalletInfoAccess, }, weights::Weight, }; use pezframe_system::EnsureRoot; use pezpallet_xcm::{AuthorizedAliasers, XcmPassthrough}; use pezkuwi_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice}; use pezkuwi_teyrchain_primitives::primitives::Sibling; use pezsp_runtime::traits::{AccountIdConversion, ConvertInto, Identity, TryConvertInto}; use testnet_teyrchains_constants::zagros::currency::deposit; use teyrchains_common::{ xcm_config::{AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem}, TREASURY_PALLET_ID, }; use xcm::latest::{prelude::*, ZAGROS_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AliasChildLocation, AliasOriginRootUsingFilter, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, DescribeTerminus, EnsureXcmOrigin, ExternalConsensusLocationsConverterFor, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingTeyrchainAsNative, SiblingTeyrchainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); // Local native currency which is stored in `pezpallet_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 `Zagros Network` and can be changed using `System::set_storage`. pub storage RelayNetworkId: NetworkId = NetworkId::ByGenesis(ZAGROS_GENESIS_HASH); pub RelayNetwork: Option = Some(RelayNetworkId::get()); pub RelayChainOrigin: RuntimeOrigin = pezcumulus_pezpallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [ GlobalConsensus(RelayNetworkId::get()), Teyrchain(TeyrchainInfo::teyrchain_id().into()) ].into(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub StakingPot: AccountId = CollatorSelection::account_id(); pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); } /// 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 teyrchain origins convert to AccountId via the `ParaId::into`. SiblingTeyrchainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, // Foreign locations alias into accounts according to a hash of their standard description. HashedDescription)>, // Different global consensus locations sovereign accounts. ExternalConsensusLocationsConverterFor, ); /// Means for transacting assets on this chain. pub type FungibleTransactor = 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, >; // Using the latest `Location`, we don't need to worry about migrations for Penpal. pub type ForeignAssetsAssetId = Location; pub type ForeignAssetsConvertedConcreteId = xcm_builder::MatchedConvertedConcreteId< Location, Balance, EverythingBut<( // Here we rely on fact that something like this works: // assert!(Location::new(1, // [Teyrchain(100)]).starts_with(&Location::parent())); // assert!([Teyrchain(100)].into().starts_with(&Here)); StartsWith, )>, Identity, TryConvertInto, >; /// 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 = (FungibleTransactor, 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 Teyrchains; will convert to a `SiblingPara` origin when // recognized. SiblingTeyrchainAsNative, // 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 pezpallet's Xcm origin. XcmPassthrough, ); parameter_types! { pub const RootLocation: Location = Location::here(); // 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, // Parent and its pluralities (i.e. governance bodies) get free execution. AllowExplicitUnpaidExecutionFrom<(ParentOrParentsExecutivePlurality,)>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, // HRMP notifications from the relay chain are OK. AllowHrmpNotificationsFromRelayChain, ), UniversalLocation, ConstU32<8>, >, )>; /// Type alias to conveniently refer to `pezframe_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; // 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; pub const USDT_ASSET_ID: u128 = 1984; parameter_types! { /// The location that this chain recognizes as the Relay network's Asset Hub. pub SystemAssetHubLocation: Location = Location::new(1, [Teyrchain(ASSET_HUB_ID)]); // the Relay Chain's Asset Hub's Assets pezpallet index pub SystemAssetHubAssetsPalletLocation: Location = Location::new(1, [Teyrchain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID)]); pub AssetsPalletLocation: Location = Location::new(0, [PalletInstance(ASSETS_PALLET_ID)]); pub CheckingAccount: AccountId = PezkuwiXcm::check_account(); pub LocalReservableFromAssetHub: Location = Location::new( 1, [Teyrchain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())] ); pub UsdtFromAssetHub: Location = Location::new( 1, [Teyrchain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ASSET_ID)], ); /// 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(); pub storage LocalTeleportableToAssetHub: Location = Location::new( 0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(TELEPORTABLE_ASSET_ID.into())] ); pub const NativeAssetId: AssetId = AssetId(Location::here()); pub const NativeAssetFilter: AssetFilter = Wild(AllOf { fun: WildFungible, id: NativeAssetId::get() }); pub AssetHubTrustedTeleporter: (AssetFilter, Location) = (NativeAssetFilter::get(), 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 { tracing::trace!(target: "xcm::contains", ?asset, ?origin, "AssetFromChain"); *origin == Origin::get() && matches!(asset.id.clone(), AssetId(id) if id == AssetLocation::get()) } } pub type TrustedReserves = ( NativeAsset, ConcreteAssetFromSystem, AssetsFrom, AssetPrefixFrom, ); pub type TrustedTeleporters = ( AssetFromChain, // This is used in the `IsTeleporter` configuration, meaning it accepts // native tokens teleported from Asset Hub. xcm_builder::Case, ); /// Defines origin aliasing rules for this chain. /// /// - Allow any origin to alias into a child sub-location (equivalent to DescendOrigin), /// - Allow AssetHub root to alias into anything, /// - Allow origins explicitly authorized by the alias target location. pub type TrustedAliasers = ( AliasChildLocation, AliasOriginRootUsingFilter, AuthorizedAliasers, ); pub type WaivedLocations = Equals; /// `AssetId`/`Balance` converter for `TrustBackedAssets`. pub type TrustBackedAssetsConvertedConcreteId = assets_common::TrustBackedAssetsConvertedConcreteId; /// Asset converter for pool assets. /// Used to convert assets in pools to the asset required for fee payment. /// The pool must be between the first asset and the one required for fee payment. /// This type allows paying fees with any asset in a pool with the asset required for fee payment. pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< crate::AssetConversion, crate::NativeAndAssets, ( TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, xcm::latest::Location, >, ForeignAssetsConvertedConcreteId, ), AccountId, >; pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; type XcmEventEmitter = PezkuwiXcm; // 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>, // Allow native asset to pay the execution fee UsingComponents>, pezcumulus_primitives_utility::SwapFirstAssetTrader< RelayLocation, crate::AssetConversion, WeightToFee, crate::NativeAndAssets, ( TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, xcm::latest::Location, >, ForeignAssetsConvertedConcreteId, ), ResolveAssetTo, AccountId, >, ); type ResponseHandler = PezkuwiXcm; type AssetTrap = PezkuwiXcm; type AssetClaims = PezkuwiXcm; type SubscriptionService = PezkuwiXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, SendXcmFeeToAccount, >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = TrustedAliasers; type TransactionalProcessor = FrameTransactionalProcessor; type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); type XcmRecorder = PezkuwiXcm; } /// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. pub type ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger = AssetFeeAsExistentialDepositMultiplier< Runtime, WeightToFee, pezpallet_assets::BalanceToAssetBalance, ForeignAssetsInstance, >; /// Converts a local signed origin into an XCM location. Forms the basis for local origins /// sending/executing XCMs. 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: pezcumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; parameter_types! { pub const DepositPerItem: Balance = deposit(1, 0); pub const DepositPerByte: Balance = deposit(0, 1); pub const AuthorizeAliasHoldReason: RuntimeHoldReason = RuntimeHoldReason::PezkuwiXcm(pezpallet_xcm::HoldReason::AuthorizeAlias); } impl pezpallet_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 = pezpallet_xcm::CurrentXcmVersion; type Currency = Balances; type CurrencyMatcher = (); type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pezpallet_xcm::TestWeightInfo; type AdminOrigin = EnsureRoot; type MaxRemoteLockConsumers = ConstU32<0>; type RemoteLockConsumerIdentifier = (); // xcm_executor::Config::Aliasers also uses pezpallet_xcm::AuthorizedAliasers. type AuthorizedAliasConsideration = HoldConsideration< AccountId, Balances, AuthorizeAliasHoldReason, LinearStoragePrice, >; } impl pezcumulus_pezpallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; }