Skip to content
Open
101 changes: 100 additions & 1 deletion xcm-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@
#![allow(clippy::unused_unit)]

use frame_support::dispatch::{DispatchError, DispatchResult};
use frame_support::{ensure, traits::Contains, weights::Weight};

use sp_runtime::traits::{CheckedConversion, Convert};
use sp_std::{convert::TryFrom, marker::PhantomData, prelude::*};

use xcm::latest::prelude::*;
use xcm_executor::traits::{FilterAssetLocation, MatchesFungible};
use xcm_executor::traits::{FilterAssetLocation, MatchesFungible, ShouldExecute};

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 +78,99 @@ 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,
MultiLocation {
parents: 1,
interior: X1(AccountId32 { id, network }),
} if network == Network::get() => id,
_ => return Err(location),
};
Ok(id.into())
}

fn reverse(who: AccountId) -> Result<MultiLocation, AccountId> {
Ok((
1,
AccountId32 {
id: who.into(),
network: Network::get(),
},
)
.into())
}
}

pub struct IsParent;
impl Contains<MultiLocation> for IsParent {
fn contains(l: &MultiLocation) -> bool {
l.contains_parents_only(1)
}
}

/// when Relay an XCM message from a given `interior` location, if the given
/// `interior` is not `Here`, the destination will receive a xcm message
/// beginning with `DescendOrigin` as the first instruction. so the xcm message
/// format must match this order:
/// `DescendOrigin`,`WithdrawAsset`,`BuyExecution`,`Transact`.
pub struct AllowEquivalentParentAccountsFrom<T, Network>(PhantomData<(T, Network)>);
impl<T: Contains<MultiLocation>, Network: Get<NetworkId>> ShouldExecute
for AllowEquivalentParentAccountsFrom<T, Network>
{
fn should_execute<Call>(
origin: &MultiLocation,
message: &mut Xcm<Call>,
max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
ensure!(T::contains(origin), ());
let mut iter = message.0.iter_mut();
let i = iter.next().ok_or(())?;
match i {
DescendOrigin(X1(Junction::AccountId32 {
network: NetworkId::Any,
..
})) => (),
DescendOrigin(X1(Junction::AccountId32 { network, .. })) if network == &Network::get() => (),
_ => return Err(()),
}
let i = iter.next().ok_or(())?;
match i {
WithdrawAsset(..) => (),
_ => return Err(()),
}
let i = iter.next().ok_or(())?;
match i {
BuyExecution {
weight_limit: Limited(ref mut weight),
..
} if *weight >= max_weight => {
*weight = max_weight;
()
}
_ => return Err(()),
}
let i = iter.next().ok_or(())?;
match i {
Transact {
origin_type: OriginKind::SovereignAccount,
..
} => Ok(()),
_ => Err(()),
}
}
}
2 changes: 2 additions & 0 deletions xtokens/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ xcm-simulator = { git = "https://github.com/paritytech/polkadot", branch = "rele
orml-tokens = { path = "../tokens" }
orml-xcm = { path = "../xcm" }

env_logger = "0.8.3"

[features]
default = ["std"]
std = [
Expand Down
41 changes: 37 additions & 4 deletions xtokens/src/mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,41 @@ use serde::{Deserialize, Serialize};
use sp_io::TestExternalities;
use sp_runtime::AccountId32;

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

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 DEFAULT: 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,9 +146,14 @@ 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>;

pub type RelayChainPalletXcm = pallet_xcm::Pallet<relay::Runtime>;
pub type ParachainPalletXcm = pallet_xcm::Pallet<para::Runtime>;

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

Expand All @@ -135,7 +168,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 +186,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
12 changes: 10 additions & 2 deletions xtokens/src/mock/para.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ 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::{
AllowEquivalentParentAccountsFrom, IsNativeConcrete, IsParent, MultiCurrencyAdapter, MultiNativeAsset,
RelaychainAccountId32Aliases,
};

pub type AccountId = AccountId32;

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

pub type XcmOriginToCallOrigin = (
Expand All @@ -140,7 +144,11 @@ pub type LocalAssetTransactor = MultiCurrencyAdapter<
>;

pub type XcmRouter = ParachainXcmRouter<ParachainInfo>;
pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom<Everything>);
pub type Barrier = (
TakeWeightCredit,
AllowTopLevelPaidExecutionFrom<Everything>,
AllowEquivalentParentAccountsFrom<IsParent, RelayNetwork>,
);

/// A trader who believes all tokens are created equal to "weight" of any chain,
/// which is not true, but good enough to mock the fee payment of XCM execution.
Expand Down
Loading