Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions traits/src/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,40 @@ impl Parse for MultiLocation {

pub trait Reserve {
/// Returns assets reserve location.
fn reserve(&self) -> Option<MultiLocation>;
fn reserve(asset: &MultiAsset) -> Option<MultiLocation>;
}

impl Reserve for MultiAsset {
fn reserve(&self) -> Option<MultiLocation> {
if let Concrete(location) = &self.id {
// Provide reserve in absolute path view
pub struct AbsoluteReserveProvider;

impl Reserve for AbsoluteReserveProvider {
fn reserve(asset: &MultiAsset) -> Option<MultiLocation> {
if let Concrete(location) = &asset.id {
location.chain_part()
} else {
None
}
}
}

// Provide reserve in relative path view
// Self tokens are represeneted as Here
pub struct RelativeReserveProvider;

impl Reserve for RelativeReserveProvider {
fn reserve(asset: &MultiAsset) -> Option<MultiLocation> {
if let Concrete(location) = &asset.id {
if location.parents == 0 && !is_chain_junction(location.first_interior()) {
Some(MultiLocation::here())
} else {
location.chain_part()
}
} else {
None
}
}
}

pub trait RelativeLocations {
fn sibling_parachain_general_key(para_id: u32, general_key: Vec<u8>) -> MultiLocation;
}
Expand All @@ -79,33 +100,49 @@ mod tests {
#[test]
fn parent_as_reserve_chain() {
assert_eq!(
concrete_fungible(MultiLocation::new(1, X1(GENERAL_INDEX))).reserve(),
AbsoluteReserveProvider::reserve(&concrete_fungible(MultiLocation::new(1, X1(GENERAL_INDEX)))),
Some(MultiLocation::parent())
);
assert_eq!(
RelativeReserveProvider::reserve(&concrete_fungible(MultiLocation::new(1, X1(GENERAL_INDEX)))),
Some(MultiLocation::parent())
);
}

#[test]
fn sibling_parachain_as_reserve_chain() {
assert_eq!(
concrete_fungible(MultiLocation::new(1, X2(PARACHAIN, GENERAL_INDEX))).reserve(),
AbsoluteReserveProvider::reserve(&concrete_fungible(MultiLocation::new(1, X2(PARACHAIN, GENERAL_INDEX)))),
Some(MultiLocation::new(1, X1(PARACHAIN)))
);
assert_eq!(
RelativeReserveProvider::reserve(&concrete_fungible(MultiLocation::new(1, X2(PARACHAIN, GENERAL_INDEX)))),
Some(MultiLocation::new(1, X1(PARACHAIN)))
);
}

#[test]
fn child_parachain_as_reserve_chain() {
assert_eq!(
concrete_fungible(MultiLocation::new(0, X2(PARACHAIN, GENERAL_INDEX))).reserve(),
AbsoluteReserveProvider::reserve(&concrete_fungible(MultiLocation::new(0, X2(PARACHAIN, GENERAL_INDEX)))),
Some(PARACHAIN.into())
);
assert_eq!(
RelativeReserveProvider::reserve(&concrete_fungible(MultiLocation::new(0, X2(PARACHAIN, GENERAL_INDEX)))),
Some(PARACHAIN.into())
);
}

#[test]
fn no_reserve_chain() {
fn no_reserve_chain_for_absolute_self_for_relative() {
assert_eq!(
concrete_fungible(MultiLocation::new(0, X1(GeneralKey("DOT".into())))).reserve(),
AbsoluteReserveProvider::reserve(&concrete_fungible(MultiLocation::new(0, X1(GeneralKey("DOT".into()))))),
None
);
assert_eq!(
RelativeReserveProvider::reserve(&concrete_fungible(MultiLocation::new(0, X1(GeneralKey("DOT".into()))))),
Some(MultiLocation::here())
);
}

#[test]
Expand Down
9 changes: 6 additions & 3 deletions xcm-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ where

/// A `FilterAssetLocation` implementation. Filters multi native assets whose
/// reserve is same with `origin`.
pub struct MultiNativeAsset;
impl FilterAssetLocation for MultiNativeAsset {
pub struct MultiNativeAsset<ReserveProvider>(PhantomData<ReserveProvider>);
impl<ReserveProvider> FilterAssetLocation for MultiNativeAsset<ReserveProvider>
where
ReserveProvider: Reserve,
{
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
if let Some(ref reserve) = asset.reserve() {
if let Some(ref reserve) = ReserveProvider::reserve(asset) {
if reserve == origin {
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions xcm-support/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use super::*;

use orml_traits::{location::RelativeLocations, ConcreteFungibleAsset};
use orml_traits::{location::AbsoluteReserveProvider, location::RelativeLocations, ConcreteFungibleAsset};

#[derive(Debug, PartialEq, Eq)]
pub enum TestCurrencyId {
Expand Down Expand Up @@ -82,18 +82,18 @@ fn is_native_concrete_does_not_matches_non_native_currencies() {

#[test]
fn multi_native_asset() {
assert!(MultiNativeAsset::filter_asset_location(
assert!(MultiNativeAsset::<AbsoluteReserveProvider>::filter_asset_location(
&MultiAsset {
fun: Fungible(10),
id: Concrete(MultiLocation::parent())
},
&Parent.into()
));
assert!(MultiNativeAsset::filter_asset_location(
assert!(MultiNativeAsset::<AbsoluteReserveProvider>::filter_asset_location(
&MultiAsset::sibling_parachain_asset(1, "TokenA".into(), 100),
&MultiLocation::new(1, X1(Parachain(1))),
));
assert!(!MultiNativeAsset::filter_asset_location(
assert!(!MultiNativeAsset::<AbsoluteReserveProvider>::filter_asset_location(
&MultiAsset::sibling_parachain_asset(1, "TokenA".into(), 100),
&MultiLocation::parent(),
));
Expand Down
17 changes: 11 additions & 6 deletions xtokens/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ pub mod module {
/// The maximum number of distinct assets allowed to be transferred in a
/// single helper extrinsic.
type MaxAssetsForTransfer: Get<usize>;

/// The way to retreave the reserve of a MultiAsset. This can be
/// configured to accept absolute or relative paths for self tokens
type ReserveProvider: Reserve;
}

#[pallet::event]
Expand Down Expand Up @@ -516,18 +520,18 @@ pub mod module {
);
// `assets` includes fee, the reserve location is decided by non fee asset
if (fee != *asset && non_fee_reserve.is_none()) || asset_len == 1 {
non_fee_reserve = asset.reserve();
non_fee_reserve = T::ReserveProvider::reserve(asset);
}
// make sure all non fee assets share the same reserve
if non_fee_reserve.is_some() {
ensure!(
non_fee_reserve == asset.reserve(),
non_fee_reserve == T::ReserveProvider::reserve(asset),
Error::<T>::DistinctReserveForAssetAndFee
);
}
}

let fee_reserve = fee.reserve();
let fee_reserve = T::ReserveProvider::reserve(&fee);
if fee_reserve != non_fee_reserve {
// Current only support `ToReserve` with relay-chain asset as fee. other case
// like `NonReserve` or `SelfReserve` with relay-chain fee is not support.
Expand Down Expand Up @@ -762,7 +766,6 @@ pub mod module {

let self_location = T::SelfLocation::get();
ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);

let reserve = reserve.ok_or(Error::<T>::AssetHasNoReserve)?;
let transfer_kind = if reserve == self_location {
SelfReserveAsset
Expand All @@ -782,7 +785,9 @@ pub mod module {
let asset: Result<MultiAsset, _> = asset.clone().try_into();
let dest = dest.clone().try_into();
if let (Ok(asset), Ok(dest)) = (asset, dest) {
if let Ok((transfer_kind, dest, _, reserve)) = Self::transfer_kind(asset.reserve(), &dest) {
if let Ok((transfer_kind, dest, _, reserve)) =
Self::transfer_kind(T::ReserveProvider::reserve(&asset), &dest)
{
let mut msg = match transfer_kind {
SelfReserveAsset => Xcm(vec![
WithdrawAsset(MultiAssets::from(asset)),
Expand Down Expand Up @@ -891,7 +896,7 @@ pub mod module {
0
};
let asset = assets.get(reserve_idx);
asset.and_then(|a| a.reserve())
asset.and_then(T::ReserveProvider::reserve)
}
}

Expand Down
24 changes: 23 additions & 1 deletion xtokens/src/mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use sp_runtime::AccountId32;
use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};

pub mod para;
pub mod para_relative_view;
pub mod relay;

pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]);
Expand All @@ -31,6 +32,8 @@ pub enum CurrencyId {
B1,
/// Parachain B B2 token
B2,
/// Parachain D token
D,
}

pub struct CurrencyIdConvert;
Expand All @@ -43,6 +46,7 @@ impl Convert<CurrencyId, Option<MultiLocation>> for CurrencyIdConvert {
CurrencyId::B => Some((Parent, Parachain(2), GeneralKey("B".into())).into()),
CurrencyId::B1 => Some((Parent, Parachain(2), GeneralKey("B1".into())).into()),
CurrencyId::B2 => Some((Parent, Parachain(2), GeneralKey("B2".into())).into()),
CurrencyId::D => Some((Parent, Parachain(4), GeneralKey("D".into())).into()),
}
}
}
Expand All @@ -53,6 +57,7 @@ impl Convert<MultiLocation, Option<CurrencyId>> for CurrencyIdConvert {
let b: Vec<u8> = "B".into();
let b1: Vec<u8> = "B1".into();
let b2: Vec<u8> = "B2".into();
let d: Vec<u8> = "D".into();
if l == MultiLocation::parent() {
return Some(CurrencyId::R);
}
Expand All @@ -62,7 +67,8 @@ impl Convert<MultiLocation, Option<CurrencyId>> for CurrencyIdConvert {
X2(Parachain(1), GeneralKey(k)) if k == a1 => Some(CurrencyId::A1),
X2(Parachain(2), GeneralKey(k)) if k == b => Some(CurrencyId::B),
X2(Parachain(2), GeneralKey(k)) if k == b1 => Some(CurrencyId::B1),
X2(Parachain(2), GeneralKey(k)) if k == b1 => Some(CurrencyId::B2),
X2(Parachain(2), GeneralKey(k)) if k == b2 => Some(CurrencyId::B2),
X2(Parachain(4), GeneralKey(k)) if k == d => Some(CurrencyId::D),
_ => None,
},
MultiLocation { parents, interior } if parents == 0 => match interior {
Expand All @@ -71,6 +77,7 @@ impl Convert<MultiLocation, Option<CurrencyId>> for CurrencyIdConvert {
X1(GeneralKey(k)) if k == a1 => Some(CurrencyId::A1),
X1(GeneralKey(k)) if k == b1 => Some(CurrencyId::B1),
X1(GeneralKey(k)) if k == b2 => Some(CurrencyId::B2),
X1(GeneralKey(k)) if k == d => Some(CurrencyId::D),
_ => None,
},
_ => None,
Expand Down Expand Up @@ -121,6 +128,17 @@ decl_test_parachain! {
}
}

// This parachain is identical to the others but using relative view for self
// tokens
decl_test_parachain! {
pub struct ParaD {
Runtime = para_relative_view::Runtime,
XcmpMessageHandler = para::XcmpQueue,
DmpMessageHandler = para::DmpQueue,
new_ext = para_ext(4),
}
}

decl_test_relay_chain! {
pub struct Relay {
Runtime = relay::Runtime,
Expand All @@ -136,6 +154,7 @@ decl_test_network! {
(1, ParaA),
(2, ParaB),
(3, ParaC),
(4, ParaD),
],
}
}
Expand All @@ -144,6 +163,9 @@ pub type RelayBalances = pallet_balances::Pallet<relay::Runtime>;
pub type ParaTokens = orml_tokens::Pallet<para::Runtime>;
pub type ParaXTokens = orml_xtokens::Pallet<para::Runtime>;

pub type ParaRelativeTokens = orml_tokens::Pallet<para_relative_view::Runtime>;
pub type ParaRelativeXTokens = orml_xtokens::Pallet<para_relative_view::Runtime>;

pub fn para_ext(para_id: u32) -> TestExternalities {
use para::{Runtime, System};

Expand Down
6 changes: 4 additions & 2 deletions xtokens/src/mock/para.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use xcm_builder::{
};
use xcm_executor::{traits::WeightTrader, Assets, Config, XcmExecutor};

use orml_traits::parameter_type_with_key;
use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key};
use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset};

pub type AccountId = AccountId32;
Expand Down Expand Up @@ -193,7 +193,7 @@ impl Config for XcmConfig {
type XcmSender = XcmRouter;
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = XcmOriginToCallOrigin;
type IsReserve = MultiNativeAsset;
type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
type IsTeleporter = ();
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
Expand Down Expand Up @@ -279,6 +279,7 @@ match_type! {
MultiLocation { parents: 1, interior: X2(Parachain(1), Junction::AccountId32 { .. }) } |
MultiLocation { parents: 1, interior: X2(Parachain(2), Junction::AccountId32 { .. }) } |
MultiLocation { parents: 1, interior: X2(Parachain(3), Junction::AccountId32 { .. }) } |
MultiLocation { parents: 1, interior: X2(Parachain(4), Junction::AccountId32 { .. }) } |
MultiLocation { parents: 1, interior: X2(Parachain(100), Junction::AccountId32 { .. }) }
};
}
Expand Down Expand Up @@ -307,6 +308,7 @@ impl orml_xtokens::Config for Runtime {
type BaseXcmWeight = BaseXcmWeight;
type LocationInverter = LocationInverter<Ancestry>;
type MaxAssetsForTransfer = MaxAssetsForTransfer;
type ReserveProvider = AbsoluteReserveProvider;
}

impl orml_xcm::Config for Runtime {
Expand Down
Loading