Skip to content
22 changes: 22 additions & 0 deletions xcm-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use xcm_executor::traits::{FilterAssetLocation, MatchesFungible};
use orml_traits::location::Reserve;

pub use currency_adapter::MultiCurrencyAdapter;
use frame_support::pallet_prelude::Get;

mod currency_adapter;

Expand Down Expand Up @@ -75,3 +76,24 @@ impl UnknownAsset for () {
Err(DispatchError::Other(NO_UNKNOWN_ASSET_IMPL))
}
}

/// Extracts the `AccountId32` from the passed `location` if the network matches.
pub struct RelaychainAccountId32Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl<Network: Get<NetworkId>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
xcm_executor::traits::Convert<MultiLocation, AccountId> for RelaychainAccountId32Aliases<Network, AccountId>
{
fn convert(location: MultiLocation) -> Result<AccountId, MultiLocation> {
let id = match location {
MultiLocation {
parents: 1,
interior: X1(AccountId32 { id, network: NetworkId::Any }),
} => id,
_ => return Err(location),
};
Ok(id.into())
}

fn reverse(who: AccountId) -> Result<MultiLocation, AccountId> {
Ok(AccountId32 { id: who.into(), network: Network::get() }.into())
}
}
38 changes: 34 additions & 4 deletions xtokens/src/mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,40 @@ use sp_io::TestExternalities;
use sp_runtime::AccountId32;

use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};
use cumulus_primitives_core::ParaId;
use polkadot_parachain::primitives::{AccountIdConversion, Sibling};

pub mod para;
pub mod relay;
pub mod tests;

pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]);
pub const BOB: AccountId32 = AccountId32::new([1u8; 32]);
pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
pub const BOB: AccountId32 = AccountId32::new([2u8; 32]);
pub const GOD: AccountId32 = AccountId32::new([0u8; 32]);
pub const INITIAL_BALANCE: u128 = 1_000;

pub fn para_a_account() -> AccountId32 {
ParaId::from(1).into_account()
}

pub fn para_b_account() -> AccountId32 {
ParaId::from(2).into_account()
}

pub fn sibling_a_account() -> AccountId32 {
use sp_runtime::traits::AccountIdConversion;
Sibling::from(1).into_account()
}

pub fn sibling_b_account() -> AccountId32 {
use sp_runtime::traits::AccountIdConversion;
Sibling::from(2).into_account()
}

pub fn sibling_c_account() -> AccountId32 {
use sp_runtime::traits::AccountIdConversion;
Sibling::from(3).into_account()
}

#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -118,6 +146,8 @@ decl_test_network! {
}

pub type RelayBalances = pallet_balances::Pallet<relay::Runtime>;
pub type ParaBalances = pallet_balances::Pallet<para::Runtime>;

pub type ParaTokens = orml_tokens::Pallet<para::Runtime>;
pub type ParaXTokens = orml_xtokens::Pallet<para::Runtime>;

Expand All @@ -135,7 +165,7 @@ pub fn para_ext(para_id: u32) -> TestExternalities {
.unwrap();

orml_tokens::GenesisConfig::<Runtime> {
balances: vec![(ALICE, CurrencyId::R, 1_000)],
balances: vec![(ALICE, CurrencyId::R, INITIAL_BALANCE)],
}
.assimilate_storage(&mut t)
.unwrap();
Expand All @@ -153,7 +183,7 @@ pub fn relay_ext() -> sp_io::TestExternalities {
.unwrap();

pallet_balances::GenesisConfig::<Runtime> {
balances: vec![(ALICE, 1_000)],
balances: vec![(ALICE, INITIAL_BALANCE)],
}
.assimilate_storage(&mut t)
.unwrap();
Expand Down
3 changes: 2 additions & 1 deletion xtokens/src/mock/para.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use xcm_builder::{
use xcm_executor::{traits::WeightTrader, Assets, Config, XcmExecutor};

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

pub type AccountId = AccountId32;

Expand Down Expand Up @@ -114,6 +114,7 @@ pub type LocationToAccountId = (
ParentIsDefault<AccountId>,
SiblingParachainConvertsVia<Sibling, AccountId>,
AccountId32Aliases<RelayNetwork, AccountId>,
RelaychainAccountId32Aliases<RelayNetwork, AccountId>,
);

pub type XcmOriginToCallOrigin = (
Expand Down
204 changes: 204 additions & 0 deletions xtokens/src/mock/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#![cfg(test)]

use super::*;
use super::para::AccountIdToMultiLocation;
use orml_traits::{MultiCurrency};
use xcm_simulator::TestExt;
use xcm_builder::{IsConcrete};
use crate::mock::relay::KsmLocation;
use xcm_executor::traits::MatchesFungible;
use crate::mock::para::RelayLocation;

#[test]
fn test_init_balance() {
Relay::execute_with(|| {
assert_eq!(RelayBalances::free_balance(&ALICE), INITIAL_BALANCE);
assert_eq!(RelayBalances::free_balance(&BOB), 0);
assert_eq!(RelayBalances::free_balance(&para_a_account()), 0);
assert_eq!(RelayBalances::free_balance(&para_b_account()), 0);
});

ParaA::execute_with(|| {
assert_eq!(ParaTokens::free_balance(CurrencyId::R, &ALICE), INITIAL_BALANCE);
assert_eq!(ParaTokens::free_balance(CurrencyId::R, &BOB), 0);

assert_eq!(ParaTokens::free_balance(CurrencyId::A, &ALICE), 0);
assert_eq!(ParaTokens::free_balance(CurrencyId::B, &ALICE), 0);

assert_eq!(ParaBalances::free_balance(&ALICE), 0);
assert_eq!(ParaBalances::free_balance(&BOB), 0);
assert_eq!(ParaBalances::free_balance(&sibling_b_account()), 0);
assert_eq!(ParaBalances::free_balance(&sibling_c_account()), 0);
});

ParaB::execute_with(|| {
assert_eq!(ParaTokens::free_balance(CurrencyId::R, &ALICE), INITIAL_BALANCE);
assert_eq!(ParaTokens::free_balance(CurrencyId::R, &BOB), 0);
});
}

#[test]
fn test_asset_matches_fungible() {
// use raw way: VersionedMultiAssets -> MultiAssets -> Vec<MultiAsset>
// `KsmLocation` in `relay.rs` is `Here`
let assets: VersionedMultiAssets = (Here, 100u128).into();
let assets: MultiAssets = assets.try_into().unwrap();
let assets: Vec<MultiAsset> = assets.drain();
for asset in assets {
let assets: u128 = IsConcrete::<KsmLocation>::matches_fungible(&asset.clone()).unwrap_or_default();
assert_eq!(assets, 100u128);
}

// use convenient way, `KsmLocation` in `relay.rs` is `Here`
let asset: MultiAsset = (Here, 100u128).into();
let amount: u128 = IsConcrete::<KsmLocation>::matches_fungible(&asset.clone()).unwrap_or_default();
assert_eq!(amount, 100u128);

// the first parameter is not equal to `Here`, which not match `KsmLocation`, so asset match result is `0`
let asset: MultiAsset = (X1(Parachain(1)), 100u128).into();
let assets: u128 = IsConcrete::<KsmLocation>::matches_fungible(&asset.clone()).unwrap_or_default();
assert_eq!(assets, 0);

// `RelayLocation` in `para.rs` is `Parent`
let asset: MultiAsset = (Parent, 100u128).into();
let assets: u128 = IsConcrete::<RelayLocation>::matches_fungible(&asset.clone()).unwrap_or_default();
assert_eq!(assets, 100);
}

#[test]
fn test_account_location_convert() {
let account = Junction::AccountId32 {
network: NetworkId::Any,
id: ALICE.into()
};

let origin_location = AccountIdToMultiLocation::convert(ALICE);
let junction: Junctions = origin_location.try_into().unwrap();
assert_eq!(junction, X1(account.clone()));

let parent: MultiLocation = Parent.into();
assert_eq!(parent.parents, 1);
assert_eq!(parent.interior, Here);
assert_eq!(parent.contains_parents_only(1), true);

let destination: MultiLocation = MultiLocation::new(
1,
X2(
Parachain(2),
account.clone(),
)
).into();
assert_eq!(destination.parents, 1);
assert_eq!(destination.interior, X2(Parachain(2), account.clone()));

let destination: MultiLocation = (
Parent,
Parachain(2),
account.clone(),
).into();
assert_eq!(destination.parents, 1);
assert_eq!(destination.interior, X2(Parachain(2), account.clone()));

let destination: MultiLocation = (
Parent,
account.clone()
).into();
assert_eq!(destination.parents, 1);
assert_eq!(destination.interior, X1(account.clone()));

let destination: MultiLocation = (
Parachain(2),
account.clone()
).into();
assert_eq!(destination.parents, 0);
assert_eq!(destination.interior, X2(Parachain(2), account.clone()));

let junction = X1(account.clone());
let mut destination: MultiLocation = Parent.into();
destination.append_with(junction).unwrap();
assert_eq!(destination.parents, 1);
assert_eq!(destination.interior, X1(account.clone()));
}

#[test]
fn test_parachain_convert_location_to_account() {
use xcm_executor::traits::Convert;

// ParentIsDefault
let parent: MultiLocation = Parent.into();
let account = para::LocationToAccountId::convert(parent);
assert_eq!(account, Ok(GOD));

// SiblingParachainConvertsVia
let destination: MultiLocation = (
Parent,
Parachain(1),
).into();
let account = para::LocationToAccountId::convert(destination);
assert_eq!(account, Ok(sibling_a_account()));

let alice = Junction::AccountId32 {
network: NetworkId::Any,
id: ALICE.into()
};

// AccountId32Aliases
let destination: MultiLocation = (
alice.clone()
).into();
let account = para::LocationToAccountId::convert(destination);
assert_eq!(account, Ok(ALICE));

// RelaychainAccountId32Aliases
let destination: MultiLocation = (
Parent,
alice.clone()
).into();
let account = para::LocationToAccountId::convert(destination);
assert_eq!(account, Ok(ALICE));

// Error case
let destination: MultiLocation = (
Parent,
Parachain(1),
alice.clone()
).into();
let account = para::LocationToAccountId::convert(destination.clone());
assert_eq!(account, Err(destination));
}

#[test]
fn test_relaychain_convert_location_to_account() {
use xcm_executor::traits::Convert;

// ChildParachainConvertsVia
let destination: MultiLocation = (
Parachain(1),
).into();
let account = relay::SovereignAccountOf::convert(destination);
assert_eq!(account, Ok(para_a_account()));

let alice = Junction::AccountId32 {
network: NetworkId::Any,
id: ALICE.into()
};

let alice_unknown_network = Junction::AccountId32 {
network: NetworkId::Polkadot,
id: ALICE.into()
};

// AccountId32Aliases
let destination: MultiLocation = (
alice.clone()
).into();
let account = relay::SovereignAccountOf::convert(destination);
assert_eq!(account, Ok(ALICE));

// AccountId32Aliases with unknown-network location
let destination: MultiLocation = (
alice_unknown_network.clone()
).into();
let account = relay::SovereignAccountOf::convert(destination.clone());
assert_eq!(account, Err(destination));
}
27 changes: 0 additions & 27 deletions xtokens/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
#![cfg(test)]

use super::*;
use codec::Encode;
use cumulus_primitives_core::ParaId;
use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency};
use mock::*;
use orml_traits::{ConcreteFungibleAsset, MultiCurrency};
use polkadot_parachain::primitives::{AccountIdConversion, Sibling};
use sp_runtime::AccountId32;
use xcm_simulator::TestExt;

fn para_a_account() -> AccountId32 {
ParaId::from(1).into_account()
}

fn para_b_account() -> AccountId32 {
ParaId::from(2).into_account()
}

fn sibling_a_account() -> AccountId32 {
use sp_runtime::traits::AccountIdConversion;
Sibling::from(1).into_account()
}

fn sibling_b_account() -> AccountId32 {
use sp_runtime::traits::AccountIdConversion;
Sibling::from(2).into_account()
}

fn sibling_c_account() -> AccountId32 {
use sp_runtime::traits::AccountIdConversion;
Sibling::from(3).into_account()
}

// Not used in any unit tests, but it's super helpful for debugging. Let's
// keep it here.
#[allow(dead_code)]
Expand Down