Files
pezkuwi-runtime-templates/evm-template/runtime/src/configs/xcm_config.rs
T
pezkuwichain d839cbd92b Complete terminology rebrand to Pezkuwi ecosystem
Applied global changes: Polkadot->Pezkuwi, Parachain->TeyrChain, pallet->pezpallet, frame->pezframe.

Updated authors in Cargo.toml to include Kurdistan Tech Institute and pezkuwichain team.

Used Cargo aliases to maintain SDK compatibility while using rebranded names in source code.
2025-12-22 09:25:35 +03:00

862 lines
33 KiB
Rust

use core::marker::PhantomData;
use pezframe_support::{
parameter_types,
traits::{ConstU32, Contains, ContainsPair, Everything, PezpalletInfoAccess},
weights::Weight,
};
use orml_traits::{location::Reserve, parameter_type_with_key};
use orml_xcm_support::MultiNativeAsset;
use pezpallet_xcm::XcmPassthrough;
use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode};
use pezkuwi_teyrchain_primitives::primitives::{self, Sibling};
use scale_info::TypeInfo;
use sp_core::H160;
use sp_runtime::Vec;
use xcm::latest::prelude::{Assets as XcmAssets, *};
use xcm_builder::{
AccountKey20Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, Case,
ConvertedConcreteId, DenyReserveTransferToRelayChain, DenyThenTry, FixedWeightBounds,
FungibleAdapter, FungiblesAdapter, HandleFee, IsChildSystemTeyrChain, IsConcrete, NoChecking,
ParentIsPreset, RelayChainAsNative, SiblingTeyrChainAsNative, SiblingTeyrChainConvertsVia,
SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId,
WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents,
};
use xcm_executor::traits::{ConvertLocation, FeeReason, JustTry, TransactAsset};
use xcm_primitives::{AsAssetType, UtilityAvailableCalls, UtilityEncodeCall, XcmTransact};
use crate::{
configs::{
AssetType, Erc20XcmBridgePezpalletLocation, TeyrChainSystem, Runtime, RuntimeCall,
RuntimeOrigin, XcmpQueue,
},
types::{AccountId, AssetId, Balance},
AssetManager, Assets, Balances, TeyrChainInfo, Treasury,
};
parameter_types! {
pub const RelayNetwork: Option<NetworkId> = None;
pub AssetsPezpalletLocation: Location =
PezpalletInstance(<Assets as PezpalletInfoAccess>::index() as u8).into();
pub BalancesPezpalletLocation: Location = PezpalletInstance(<Balances as PezpalletInfoAccess>::index() as u8).into();
pub RelayChainOrigin: RuntimeOrigin = cumulus_pezpallet_xcm::Origin::Relay.into();
pub UniversalLocation: InteriorLocation = TeyrChain(TeyrChainInfo::teyrchain_id().into()).into();
// Self Reserve location, defines the multilocation identifiying the self-reserve currency
// This is used to match it also against our Balances pezpallet when we receive such
// a Location: (Self Balances pezpallet index)
// We use the RELATIVE multilocation
pub SelfReserve: Location = Location {
parents:0,
interior: [
PezpalletInstance(<Balances as PezpalletInfoAccess>::index() as u8)
].into()
};
}
/// `AssetId/Balancer` converter for `TrustBackedAssets`
pub type TrustBackedAssetsConvertedConcreteId =
assets_common::TrustBackedAssetsConvertedConcreteId<AssetsPezpalletLocation, Balance>;
/// 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<AccountId>,
// Sibling teyrchain origins convert to AccountId via the `ParaId::into`.
SiblingTeyrChainConvertsVia<Sibling, AccountId>,
// If we receive a Location of type AccountKey20, just generate a native account
AccountKey20Aliases<RelayNetwork, AccountId>,
);
/// Means for transacting native currency on this chain.
pub type LocalAssetTransactor = FungibleAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<BalancesPezpalletLocation>,
// Do a simple punn to convert an AccountId20 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 LocalFungiblesTransactor = FungiblesAdapter<
// Use this fungibles implementation:
Assets,
// Use this currency when it is a fungible asset matching the given location or name:
ConvertedConcreteId<AssetId, Balance, AsAssetType<AssetId, AssetType, AssetManager>, JustTry>,
// Convert an XCM MultiLocation 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 track any teleports of `Assets`.
NoChecking,
// We don't track any teleports.
(),
>;
/// Means for transacting assets on this chain.
pub type AssetTransactors = (LocalAssetTransactor, LocalFungiblesTransactor);
/// 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<LocationToAccountId, RuntimeOrigin>,
// Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when
// recognized.
RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
// Native converter for sibling TeyrChains; will convert to a `SiblingPara` origin when
// recognized.
SiblingTeyrChainAsNative<cumulus_pezpallet_xcm::Origin, RuntimeOrigin>,
// Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte-
// account local origin
SignedAccountKey20AsNative<RelayNetwork, RuntimeOrigin>,
// Xcm origins can be represented natively under the Xcm pezpallet's Xcm origin.
XcmPassthrough<RuntimeOrigin>,
);
parameter_types! {
// One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
pub const UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub struct ParentOrParentsExecutivePlurality;
impl Contains<Location> for ParentOrParentsExecutivePlurality {
fn contains(location: &Location) -> bool {
matches!(location.unpack(), (1, []) | (1, [Plurality { id: BodyId::Executive, .. }]))
}
}
pub type Barrier = TrailingSetTopicAsId<
DenyThenTry<
DenyReserveTransferToRelayChain,
(
TakeWeightCredit,
WithComputedOrigin<
(
AllowTopLevelPaidExecutionFrom<Everything>,
AllowExplicitUnpaidExecutionFrom<ParentOrParentsExecutivePlurality>,
// ^^^ Parent and its exec plurality get free execution
),
UniversalLocation,
ConstU32<8>,
>,
),
>,
>;
/// A `HandleFee` implementation that simply deposits the fees into a specific on-chain
/// `ReceiverAccount`.
///
/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets. If
/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be
/// logged and the fee burned.
pub struct XcmFeeToAccount<AssetTransactor, AccountId, ReceiverAccount>(
PhantomData<(AssetTransactor, AccountId, ReceiverAccount)>,
);
impl<
AssetTransactor: TransactAsset,
AccountId: Clone + Into<[u8; 20]>,
ReceiverAccount: Get<AccountId>,
> HandleFee for XcmFeeToAccount<AssetTransactor, AccountId, ReceiverAccount>
{
fn handle_fee(fee: XcmAssets, context: Option<&XcmContext>, _reason: FeeReason) -> XcmAssets {
deposit_or_burn_fee::<AssetTransactor, _>(fee, context, ReceiverAccount::get());
XcmAssets::new()
}
}
pub fn deposit_or_burn_fee<AssetTransactor: TransactAsset, AccountId: Clone + Into<[u8; 20]>>(
fee: XcmAssets,
context: Option<&XcmContext>,
receiver: AccountId,
) {
let dest = AccountKey20 { network: None, key: receiver.into() }.into();
for asset in fee.into_inner() {
if let Err(e) = AssetTransactor::deposit_asset(&asset, &dest, context) {
log::trace!(
target: "xcm::fees",
"`AssetTransactor::deposit_asset` returned error: {:?}. Burning fee: {:?}. \
They might be burned.",
e, asset,
);
}
}
}
/// Matches foreign assets from a given origin.
/// Foreign assets are assets bridged from other consensus systems. i.e parents > 1.
pub struct IsBridgedConcreteAssetFrom<Origin>(PhantomData<Origin>);
impl<Origin> ContainsPair<Asset, Location> for IsBridgedConcreteAssetFrom<Origin>
where
Origin: Get<Location>,
{
fn contains(asset: &Asset, origin: &Location) -> bool {
let loc = Origin::get();
&loc == origin
&& matches!(
asset,
Asset { id: AssetId(Location { parents: 2, .. }), fun: Fungibility::Fungible(_) },
)
}
}
parameter_types! {
/// Location of Asset Hub
pub AssetHubLocation: Location = Location::new(1, [TeyrChain(1000)]);
pub const RelayLocation: Location = Location::parent();
pub RelayLocationFilter: AssetFilter = Wild(AllOf {
fun: WildFungible,
id: xcm::prelude::AssetId(RelayLocation::get()),
});
pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
RelayLocationFilter::get(),
AssetHubLocation::get()
);
}
pub type Reserves = (
// Assets bridged from different consensus systems held in reserve on Asset Hub.
IsBridgedConcreteAssetFrom<AssetHubLocation>,
// Relaychain (HEZ) from Asset Hub
Case<RelayChainNativeAssetFromAssetHub>,
// Assets which the reserve is the same as the origin.
MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
);
parameter_types! {
pub TreasuryAccount: AccountId = Treasury::account_id();
}
/// If you change this config, keep in mind that you should define how you collect fees.
pub type FeeManager = XcmFeeManagerFromComponents<
IsChildSystemTeyrChain<primitives::Id>,
XcmFeeToAccount<AssetTransactors, AccountId, TreasuryAccount>,
>;
pub type XcmWeigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
use pezframe_support::{pezpallet_prelude::Get, traits::OriginTrait};
use sp_runtime::traits::TryConvert;
// Convert a local Origin (i.e., a signed 20 byte account Origin) to a Multilocation
pub struct SignedToAccountId20<Origin, AccountId, Network>(
sp_std::marker::PhantomData<(Origin, AccountId, Network)>,
);
impl<Origin: OriginTrait + Clone, AccountId: Into<[u8; 20]>, Network: Get<Option<NetworkId>>>
TryConvert<Origin, Location> for SignedToAccountId20<Origin, AccountId, Network>
where
Origin::PalletsOrigin: From<pezframe_system::RawOrigin<AccountId>>
+ TryInto<pezframe_system::RawOrigin<AccountId>, Error = Origin::PalletsOrigin>,
{
fn try_convert(o: Origin) -> Result<Location, Origin> {
o.try_with_caller(|caller| match caller.try_into() {
Ok(pezframe_system::RawOrigin::Signed(who)) =>
Ok(AccountKey20 { key: who.into(), network: Network::get() }.into()),
Ok(other) => Err(other.into()),
Err(other) => Err(other),
})
}
}
// Converts a Signed Local Origin into a Location
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
/// 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<TeyrChainSystem, (), ()>,
// ..and XCMP to communicate with the sibling chains.
XcmpQueue,
)>;
// We are not using all of these below atm, but we will need them when configuring `orml_xtokens`
parameter_types! {
pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
pub const MaxAssetsForTransfer: usize = 2;
// This is how we are going to detect whether the asset is a Reserve asset
// This however is the chain part only
pub SelfLocation: Location = Location::here();
// We need this to be able to catch when someone is trying to execute a non-
// cross-chain transfer in xtokens through the absolute path way
pub SelfLocationAbsolute: Location = Location {
parents:1,
interior: [
TeyrChain(TeyrChainInfo::teyrchain_id().into())
].into()
};
}
parameter_type_with_key! {
pub TeyrChainMinFee: |location: Location| -> Option<u128> {
match (location.parents, location.first_interior()) {
// Pezkuwi AssetHub fee
(1, Some(TeyrChain(1000u32))) => Some(50_000_000u128),
_ => None,
}
};
}
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
#[derive(
Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, DecodeWithMemTracking, TypeInfo,
)]
pub enum CurrencyId {
// Our native token
SelfReserve,
// Assets representing other chains native tokens
ForeignAsset(AssetId),
// Erc20 token
Erc20 { contract_address: H160 },
}
// How to convert from CurrencyId to Location
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
for CurrencyIdToLocation<AssetXConverter>
where
AssetXConverter: sp_runtime::traits::MaybeEquivalence<Location, AssetId>,
{
fn convert(currency: CurrencyId) -> Option<Location> {
match currency {
CurrencyId::SelfReserve => {
let multi: Location = SelfReserve::get();
Some(multi)
}
CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
CurrencyId::Erc20 { contract_address } => {
let mut location = Erc20XcmBridgePezpalletLocation::get();
location
.push_interior(Junction::AccountKey20 {
key: contract_address.0,
network: None,
})
.ok();
Some(location)
}
}
}
}
/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`.
pub struct LocationToH160;
impl ConvertLocation<H160> for LocationToH160 {
fn convert_location(location: &Location) -> Option<H160> {
<LocationToAccountId as ConvertLocation<AccountId>>::convert_location(location)
.map(Into::into)
}
}
/// The `HEZReserveProvider` overrides the default reserve location for HEZ (Pezkuwi's native token).
///
/// HEZ can exist in multiple locations, and this provider ensures that the reserve is correctly set
/// to the AssetHub teyrchain.
///
/// - **Default Location:** `{ parents: 1, location: Here }`
/// - **Reserve Location on AssetHub:** `{ parents: 1, location: X1(TeyrChain(AssetHubParaId)) }`
///
/// This provider ensures that if the asset's ID points to the default "Here" location,
/// it will instead be mapped to the AssetHub teyrchain.
pub struct HEZReserveProvider;
impl Reserve for HEZReserveProvider {
fn reserve(asset: &Asset) -> Option<Location> {
let AssetId(location) = &asset.id;
let dot_here = Location::new(1, Here);
let dot_asset_hub = AssetHubLocation::get();
if location == &dot_here {
Some(dot_asset_hub) // Reserve is on AssetHub.
} else {
None
}
}
}
/// The `BridgedAssetReserveProvider` handles assets that are bridged from external consensus systems
/// (e.g., Ethereum) and may have multiple valid reserve locations.
///
/// Specifically, these bridged assets can have two known reserves:
/// 1. **Ethereum-based Reserve:**
/// `{ parents: 1, location: X1(GlobalConsensus(Ethereum{ chain_id: 1 })) }`
/// 2. **AssetHub TeyrChain Reserve:**
/// `{ parents: 1, location: X1(TeyrChain(AssetHubParaId)) }`
///
/// This provider maps the reserve for bridged assets to AssetHub when the asset originates
/// from a global consensus system, such as Ethereum.
pub struct BridgedAssetReserveProvider;
impl Reserve for BridgedAssetReserveProvider {
fn reserve(asset: &Asset) -> Option<Location> {
let AssetId(location) = &asset.id;
let asset_hub_reserve = AssetHubLocation::get();
// any asset that has parents > 1 and interior that starts with GlobalConsensus(_) pattern
// can be considered a bridged asset.
//
// `split_global` will return an `Err` if the first item is not a `GlobalConsensus`
if location.parents > 1 && location.interior.clone().split_global().is_ok() {
Some(asset_hub_reserve)
} else {
None
}
}
}
// Provide reserve in relative path view
// Self tokens are represeneted as Here
// Moved from Moonbeam to implement orml_traits::location::Reserve after Moonbeam
// removed ORML dependencies
pub struct RelativeReserveProvider;
impl Reserve for RelativeReserveProvider {
fn reserve(asset: &Asset) -> Option<Location> {
let AssetId(location) = &asset.id;
if location.parents == 0
&& !matches!(location.first_interior(), Some(Junction::TeyrChain(_)))
{
Some(Location::here())
} else {
Some(location.chain_location())
}
}
}
/// Struct that uses RelativeReserveProvider to output relative views of multilocations
///
/// Additionally accepts a Location that aims at representing the chain part
/// (parent: 1, TeyrChain(paraId)) of the absolute representation of our chain.
/// If a token reserve matches against this absolute view, we return Some(Location::here())
/// This helps users by preventing errors when they try to transfer a token through xtokens
/// to our chain (either inserting the relative or the absolute value).
// Moved from Moonbeam to implement orml_traits::location::Reserve after Moonbeam
// removed ORML dependencies
pub struct AbsoluteAndRelativeReserve<AbsoluteMultiLocation>(PhantomData<AbsoluteMultiLocation>);
impl<AbsoluteMultiLocation> Reserve for AbsoluteAndRelativeReserve<AbsoluteMultiLocation>
where
AbsoluteMultiLocation: Get<Location>,
{
fn reserve(asset: &Asset) -> Option<Location> {
RelativeReserveProvider::reserve(asset).map(|relative_reserve| {
if relative_reserve == AbsoluteMultiLocation::get() {
Location::here()
} else {
relative_reserve
}
})
}
}
pub struct ReserveProviders;
impl Reserve for ReserveProviders {
fn reserve(asset: &Asset) -> Option<Location> {
// Try each provider's reserve method in sequence.
HEZReserveProvider::reserve(asset)
.or_else(|| BridgedAssetReserveProvider::reserve(asset))
.or_else(|| AbsoluteAndRelativeReserve::<SelfLocationAbsolute>::reserve(asset))
}
}
pub struct AssetFeesFilter;
impl pezframe_support::traits::Contains<Location> for AssetFeesFilter {
fn contains(location: &Location) -> bool {
location.parent_count() > 0
&& location.first_interior() != Erc20XcmBridgePezpalletLocation::get().first_interior()
}
}
// For now we only allow to transact in the relay, although this might change in the future
// Transactors just defines the chains in which we allow transactions to be issued through
// xcm
#[derive(
Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, DecodeWithMemTracking, TypeInfo,
)]
pub enum Transactors {
Relay,
}
// Default for benchmarking
#[cfg(feature = "runtime-benchmarks")]
impl Default for Transactors {
fn default() -> Self {
Transactors::Relay
}
}
impl TryFrom<u8> for Transactors {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0u8 => Ok(Transactors::Relay),
_ => Err(()),
}
}
}
impl UtilityEncodeCall for Transactors {
fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
match self {
Transactors::Relay => pezpallet_xcm_transactor::Pallet::<Runtime>::encode_call(
pezpallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
call,
),
}
}
}
impl XcmTransact for Transactors {
fn destination(self) -> Location {
match self {
Transactors::Relay => Location::parent(),
}
}
}
parameter_types! {
pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
}
#[cfg(feature = "runtime-benchmarks")]
mod testing {
use sp_runtime::traits::MaybeEquivalence;
use xcm_builder::WithLatestLocationConverter;
use super::*;
/// This From exists for benchmarking purposes. It has the potential side-effect of calling
/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
impl From<Location> for CurrencyId {
fn from(location: Location) -> CurrencyId {
use xcm_primitives::AssetTypeGetter;
// If it does not exist, for benchmarking purposes, we create the association
let asset_id = if let Some(asset_id) =
AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
{
asset_id
} else {
let asset_type = AssetType::Xcm(
WithLatestLocationConverter::convert(&location).expect("convert to v3"),
);
let asset_id: AssetId = asset_type.clone().into();
AssetManager::set_asset_type_asset_id(asset_type, asset_id);
asset_id
};
CurrencyId::ForeignAsset(asset_id)
}
}
}
#[cfg(test)]
mod tests {
mod is_bridged_concrete_assets_from {
use pezframe_support::traits::ContainsPair;
use xcm::latest::{Asset, AssetId, Fungibility, Junctions, Location};
use crate::configs::{AssetHubLocation, IsBridgedConcreteAssetFrom};
#[test]
pub fn is_bridged_concrete_assets_from_contains() {
let asset = Asset {
id: AssetId(Location { parents: 2, interior: Junctions::Here }),
fun: Fungibility::Fungible(100),
};
assert!(IsBridgedConcreteAssetFrom::<AssetHubLocation>::contains(
&asset,
&AssetHubLocation::get()
))
}
#[test]
pub fn is_bridged_concrete_assets_from_not_contains() {
let asset = Asset {
id: AssetId(Location { parents: 1, interior: Junctions::Here }),
fun: Fungibility::Fungible(100),
};
assert!(!IsBridgedConcreteAssetFrom::<AssetHubLocation>::contains(
&asset,
&AssetHubLocation::get()
))
}
}
mod location_conversion {
use sp_core::H160;
use sp_runtime::traits::{Convert, TryConvert};
use xcm::latest::{Junction::AccountKey20, Location};
use crate::{
configs::{
CurrencyId, CurrencyIdToLocation, RelayNetwork, SelfReserve, SignedToAccountId20,
},
types::AccountId,
RuntimeOrigin,
};
#[test]
fn test_account_id_to_location_conversion() {
let account_id: AccountId = H160::from_low_u64_be(1).into();
let expected_location =
Location::new(0, AccountKey20 { network: None, key: account_id.into() });
let origin = RuntimeOrigin::signed(account_id);
let result = SignedToAccountId20::<_, AccountId, RelayNetwork>::try_convert(origin);
assert_eq!(result.unwrap(), expected_location); // Unwrap ensures proper equality
}
#[test]
fn test_currency_id_to_location_self_reserve() {
let currency = CurrencyId::SelfReserve;
let expected_location = SelfReserve::get();
let result = CurrencyIdToLocation::<()>::convert(currency);
assert_eq!(result, Some(expected_location));
}
#[test]
fn test_currency_id_to_location_erc20() {
let contract_address = H160::from_low_u64_be(0x1234);
let currency = CurrencyId::Erc20 { contract_address };
let mut expected_location = crate::configs::Erc20XcmBridgePezpalletLocation::get();
expected_location
.push_interior(AccountKey20 { network: None, key: contract_address.0 })
.unwrap();
let result = CurrencyIdToLocation::<()>::convert(currency);
assert_eq!(result, Some(expected_location));
}
}
mod asset_fee_filter {
use pezframe_support::traits::Contains;
use xcm::latest::{Junction::AccountKey20, Location};
use crate::configs::AssetFeesFilter;
#[test]
fn test_asset_fee_filter_valid() {
let location = Location::new(1, AccountKey20 { network: None, key: [0u8; 20] });
assert!(AssetFeesFilter::contains(&location));
}
#[test]
fn test_asset_fee_filter_invalid() {
let location = crate::configs::Erc20XcmBridgePezpalletLocation::get();
assert!(!AssetFeesFilter::contains(&location));
}
#[test]
fn test_asset_fee_filter_no_parents() {
let location = Location::new(0, AccountKey20 { network: None, key: [0u8; 20] });
assert!(!AssetFeesFilter::contains(&location));
}
}
mod asset_transactor {
use xcm::latest::Location;
use xcm_primitives::{UtilityAvailableCalls, UtilityEncodeCall, XcmTransact};
use crate::configs::Transactors;
#[test]
fn test_transactors_destination_relay() {
let transactor = Transactors::Relay;
let destination = transactor.destination();
assert_eq!(destination, Location::parent());
}
#[test]
fn test_transactors_encode_call() {
sp_io::TestExternalities::default().execute_with(|| {
let transactor = Transactors::Relay;
let call = UtilityAvailableCalls::AsDerivative(0, vec![]);
let encoded = transactor.encode_call(call);
assert!(!encoded.is_empty());
});
}
#[test]
fn test_transactors_try_from_valid() {
let transactor = Transactors::try_from(0u8);
assert!(transactor.is_ok());
}
#[test]
fn test_transactors_try_from_invalid() {
let transactor = Transactors::try_from(1u8);
assert!(transactor.is_err());
}
}
mod reserve_providers {
use pezframe_support::traits::Get;
use orml_traits::location::Reserve;
use xcm::latest::{Asset, AssetId, Fungibility, Junction, Junctions, Location};
use crate::configs::{
AbsoluteAndRelativeReserve, RelativeReserveProvider, SelfLocationAbsolute,
};
#[test]
fn test_relative_reserve_provider_here() {
// Test case for a local asset (parents = 0, no TeyrChain junction)
let local_asset = Asset {
id: AssetId(Location { parents: 0, interior: Junctions::Here }),
fun: Fungibility::Fungible(100),
};
let reserve = RelativeReserveProvider::reserve(&local_asset);
assert_eq!(reserve, Some(Location::here()));
}
#[test]
fn test_relative_reserve_provider_local_with_junctions() {
// Test case for a local asset with junctions but not TeyrChain
use std::sync::Arc;
let junction = Junction::AccountId32 { network: None, id: [0; 32] };
let junction_array = Arc::new([junction]);
let local_asset_with_junctions = Asset {
id: AssetId(Location { parents: 0, interior: Junctions::X1(junction_array) }),
fun: Fungibility::Fungible(100),
};
let reserve = RelativeReserveProvider::reserve(&local_asset_with_junctions);
assert_eq!(reserve, Some(Location::here()));
}
#[test]
fn test_relative_reserve_provider_teyrchain() {
// Test case for a teyrchain asset
use std::sync::Arc;
let junction = Junction::TeyrChain(1000);
let junction_array = Arc::new([junction]);
let teyrchain_asset = Asset {
id: AssetId(Location {
parents: 0,
interior: Junctions::X1(junction_array.clone()),
}),
fun: Fungibility::Fungible(100),
};
let reserve = RelativeReserveProvider::reserve(&teyrchain_asset);
// Should return the chain location, not Here
let expected = Location { parents: 0, interior: Junctions::X1(junction_array) };
assert_eq!(reserve, Some(expected));
}
#[test]
fn test_relative_reserve_provider_parent() {
// Test case for a parent asset
let parent_asset = Asset {
id: AssetId(Location { parents: 1, interior: Junctions::Here }),
fun: Fungibility::Fungible(100),
};
let reserve = RelativeReserveProvider::reserve(&parent_asset);
// Should return the chain location
let location = parent_asset.id.0.clone();
assert_eq!(reserve, Some(location));
}
#[test]
fn test_absolute_and_relative_reserve_matches_absolute() {
// Create a mock implementation of AbsoluteMultiLocation
use std::sync::Arc;
struct MockAbsoluteLocation;
impl Get<Location> for MockAbsoluteLocation {
fn get() -> Location {
let junction = Junction::TeyrChain(1000);
let junction_array = Arc::new([junction]);
Location { parents: 1, interior: Junctions::X1(junction_array) }
}
}
// Test case where the reserve matches the absolute location
let junction = Junction::TeyrChain(1000);
let junction_array = Arc::new([junction]);
let asset = Asset {
id: AssetId(Location { parents: 1, interior: Junctions::X1(junction_array) }),
fun: Fungibility::Fungible(100),
};
let reserve = AbsoluteAndRelativeReserve::<MockAbsoluteLocation>::reserve(&asset);
// Should return Here since it matches the absolute location
assert_eq!(reserve, Some(Location::here()));
}
#[test]
fn test_absolute_and_relative_reserve_different_location() {
// Create a mock implementation of AbsoluteMultiLocation
use std::sync::Arc;
struct MockAbsoluteLocation;
impl Get<Location> for MockAbsoluteLocation {
fn get() -> Location {
let junction = Junction::TeyrChain(1000);
let junction_array = Arc::new([junction]);
Location { parents: 1, interior: Junctions::X1(junction_array) }
}
}
// Test case where the reserve doesn't match the absolute location
let junction = Junction::TeyrChain(2000);
let junction_array = Arc::new([junction]);
let asset = Asset {
id: AssetId(Location { parents: 1, interior: Junctions::X1(junction_array) }),
fun: Fungibility::Fungible(100),
};
let reserve = AbsoluteAndRelativeReserve::<MockAbsoluteLocation>::reserve(&asset);
// Should return the relative reserve since it doesn't match the absolute location
let expected = Location { parents: 1, interior: Junctions::X1(Arc::new([junction])) };
assert_eq!(reserve, Some(expected));
}
#[test]
fn test_absolute_and_relative_reserve_with_self_location() {
// We need to use TestExternalities for this test
sp_io::TestExternalities::default().execute_with(|| {
// Test with the actual SelfLocationAbsolute
let self_location = SelfLocationAbsolute::get();
let asset =
Asset { id: AssetId(self_location.clone()), fun: Fungibility::Fungible(100) };
let reserve = AbsoluteAndRelativeReserve::<SelfLocationAbsolute>::reserve(&asset);
// Should return Here since it matches the self location
assert_eq!(reserve, Some(Location::here()));
});
}
}
}