Skip to content

NonReserve case support teleport #786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 29, 2022
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
72 changes: 52 additions & 20 deletions xtokens/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#![allow(clippy::from_over_into)]
#![allow(clippy::unused_unit)]
#![allow(clippy::large_enum_variant)]
#![allow(clippy::too_many_arguments)]

use frame_support::{
log,
Expand Down Expand Up @@ -561,6 +562,9 @@ pub mod module {
assets_to_fee_reserve.push(asset_to_fee_reserve.clone());

// First xcm sent to fee reserve chain and routed to dest chain.
// We can use `MinXcmFee` configuration to decide which target parachain use
// teleport. But as current there's only one case which is Parachain send back
// asset to Statemine/t, So we set `use_teleport` to always `true` in this case.
Self::execute_and_send_reserve_kind_xcm(
origin_location.clone(),
assets_to_fee_reserve,
Expand All @@ -569,6 +573,7 @@ pub mod module {
&dest,
Some(T::SelfLocation::get()),
dest_weight,
true,
)?;

// Second xcm send to dest chain.
Expand All @@ -580,6 +585,7 @@ pub mod module {
&dest,
None,
dest_weight,
false,
)?;
} else {
Self::execute_and_send_reserve_kind_xcm(
Expand All @@ -590,6 +596,7 @@ pub mod module {
&dest,
None,
dest_weight,
false,
)?;
}

Expand All @@ -613,6 +620,7 @@ pub mod module {
dest: &MultiLocation,
maybe_recipient_override: Option<MultiLocation>,
dest_weight: Weight,
use_teleport: bool,
) -> DispatchResult {
let (transfer_kind, dest, reserve, recipient) = Self::transfer_kind(reserve, dest)?;
let recipient = match maybe_recipient_override {
Expand All @@ -623,7 +631,9 @@ pub mod module {
let mut msg = match transfer_kind {
SelfReserveAsset => Self::transfer_self_reserve_asset(assets, fee, dest, recipient, dest_weight)?,
ToReserve => Self::transfer_to_reserve(assets, fee, dest, recipient, dest_weight)?,
ToNonReserve => Self::transfer_to_non_reserve(assets, fee, reserve, dest, recipient, dest_weight)?,
ToNonReserve => {
Self::transfer_to_non_reserve(assets, fee, reserve, dest, recipient, dest_weight, use_teleport)?
}
};

let weight = T::Weigher::weight(&mut msg).map_err(|()| Error::<T>::UnweighableMessage)?;
Expand Down Expand Up @@ -681,6 +691,7 @@ pub mod module {
dest: MultiLocation,
recipient: MultiLocation,
dest_weight: Weight,
use_teleport: bool,
) -> Result<Xcm<T::Call>, DispatchError> {
let mut reanchored_dest = dest.clone();
if reserve == MultiLocation::parent() {
Expand All @@ -695,25 +706,46 @@ pub mod module {
}
}

Ok(Xcm(vec![
WithdrawAsset(assets.clone()),
InitiateReserveWithdraw {
assets: All.into(),
reserve: reserve.clone(),
xcm: Xcm(vec![
Self::buy_execution(half(&fee), &reserve, dest_weight)?,
DepositReserveAsset {
assets: All.into(),
max_assets: assets.len() as u32,
dest: reanchored_dest,
xcm: Xcm(vec![
Self::buy_execution(half(&fee), &dest, dest_weight)?,
Self::deposit_asset(recipient, assets.len() as u32),
]),
},
]),
},
]))
if !use_teleport {
Ok(Xcm(vec![
WithdrawAsset(assets.clone()),
InitiateReserveWithdraw {
assets: All.into(),
reserve: reserve.clone(),
xcm: Xcm(vec![
Self::buy_execution(half(&fee), &reserve, dest_weight)?,
DepositReserveAsset {
assets: All.into(),
max_assets: assets.len() as u32,
dest: reanchored_dest,
xcm: Xcm(vec![
Self::buy_execution(half(&fee), &dest, dest_weight)?,
Self::deposit_asset(recipient, assets.len() as u32),
]),
},
]),
},
]))
} else {
Ok(Xcm(vec![
WithdrawAsset(assets.clone()),
InitiateReserveWithdraw {
assets: All.into(),
reserve: reserve.clone(),
xcm: Xcm(vec![
Self::buy_execution(half(&fee), &reserve, dest_weight)?,
InitiateTeleport {
assets: All.into(),
dest: reanchored_dest,
xcm: Xcm(vec![
Self::buy_execution(half(&fee), &dest, dest_weight)?,
Self::deposit_asset(recipient, assets.len() as u32),
]),
},
]),
},
]))
}
}

fn deposit_asset(recipient: MultiLocation, max_assets: u32) -> Instruction<()> {
Expand Down
87 changes: 83 additions & 4 deletions xtokens/src/mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use sp_io::TestExternalities;
use sp_runtime::AccountId32;
use xcm_executor::traits::WeightTrader;
use xcm_executor::Assets;

use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};

pub mod para;
pub mod para_relative_view;
pub mod para_teleport;
pub mod relay;
pub mod teleport_currency_adapter;

pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]);
pub const BOB: AccountId32 = AccountId32::new([1u8; 32]);
Expand All @@ -32,6 +36,8 @@ pub enum CurrencyId {
B1,
/// Parachain B B2 token
B2,
/// Parachain C token
C,
/// Parachain D token
D,
}
Expand All @@ -46,6 +52,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::C => Some((Parent, Parachain(3), GeneralKey("C".into())).into()),
CurrencyId::D => Some((Parent, Parachain(4), GeneralKey("D".into())).into()),
}
}
Expand All @@ -57,6 +64,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 c: Vec<u8> = "C".into();
let d: Vec<u8> = "D".into();
if l == MultiLocation::parent() {
return Some(CurrencyId::R);
Expand All @@ -68,6 +76,7 @@ impl Convert<MultiLocation, Option<CurrencyId>> for CurrencyIdConvert {
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 == b2 => Some(CurrencyId::B2),
X2(Parachain(3), GeneralKey(k)) if k == c => Some(CurrencyId::C),
X2(Parachain(4), GeneralKey(k)) if k == d => Some(CurrencyId::D),
_ => None,
},
Expand All @@ -77,6 +86,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 == c => Some(CurrencyId::C),
X1(GeneralKey(k)) if k == d => Some(CurrencyId::D),
_ => None,
},
Expand Down Expand Up @@ -121,10 +131,10 @@ decl_test_parachain! {

decl_test_parachain! {
pub struct ParaC {
Runtime = para::Runtime,
XcmpMessageHandler = para::XcmpQueue,
DmpMessageHandler = para::DmpQueue,
new_ext = para_ext(3),
Runtime = para_teleport::Runtime,
XcmpMessageHandler = para_teleport::XcmpQueue,
DmpMessageHandler = para_teleport::DmpQueue,
new_ext = para_teleport_ext(3),
}
}

Expand Down Expand Up @@ -166,6 +176,8 @@ 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 type ParaTeleportTokens = orml_tokens::Pallet<para_teleport::Runtime>;

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

Expand All @@ -190,6 +202,30 @@ pub fn para_ext(para_id: u32) -> TestExternalities {
ext
}

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

let mut t = frame_system::GenesisConfig::default()
.build_storage::<Runtime>()
.unwrap();

let parachain_info_config = parachain_info::GenesisConfig {
parachain_id: para_id.into(),
};
<parachain_info::GenesisConfig as GenesisBuild<Runtime, _>>::assimilate_storage(&parachain_info_config, &mut t)
.unwrap();

orml_tokens::GenesisConfig::<Runtime> {
balances: vec![(ALICE, CurrencyId::R, 1_000)],
}
.assimilate_storage(&mut t)
.unwrap();

let mut ext = TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}

pub fn relay_ext() -> sp_io::TestExternalities {
use relay::{Runtime, System};

Expand All @@ -207,3 +243,46 @@ pub fn relay_ext() -> sp_io::TestExternalities {
ext.execute_with(|| System::set_block_number(1));
ext
}

/// 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.
///
/// This mock will always trade `n` amount of weight to `n` amount of tokens.
pub struct AllTokensAreCreatedEqualToWeight(MultiLocation);
impl WeightTrader for AllTokensAreCreatedEqualToWeight {
fn new() -> Self {
Self(MultiLocation::parent())
}

fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
let asset_id = payment
.fungible
.iter()
.next()
.expect("Payment must be something; qed")
.0;
let required = MultiAsset {
id: asset_id.clone(),
fun: Fungible(weight as u128),
};

if let MultiAsset {
fun: _,
id: Concrete(ref id),
} = &required
{
self.0 = id.clone();
}

let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?;
Ok(unused)
}

fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
if weight.is_zero() {
None
} else {
Some((self.0.clone(), weight as u128).into())
}
}
}
54 changes: 6 additions & 48 deletions xtokens/src/mock/para.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use frame_system::EnsureRoot;
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{Convert, IdentityLookup, Zero},
traits::{Convert, IdentityLookup},
AccountId32,
};

Expand All @@ -20,11 +20,12 @@ use polkadot_parachain::primitives::Sibling;
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, LocationInverter,
ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
};
use xcm_executor::{traits::WeightTrader, Assets, Config, XcmExecutor};
use xcm_executor::{Config, XcmExecutor};

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

Expand Down Expand Up @@ -133,57 +134,14 @@ pub type LocalAssetTransactor = MultiCurrencyAdapter<
pub type XcmRouter = ParachainXcmRouter<ParachainInfo>;
pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom<Everything>);

/// 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.
///
/// This mock will always trade `n` amount of weight to `n` amount of tokens.
pub struct AllTokensAreCreatedEqualToWeight(MultiLocation);
impl WeightTrader for AllTokensAreCreatedEqualToWeight {
fn new() -> Self {
Self(MultiLocation::parent())
}

fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
let asset_id = payment
.fungible
.iter()
.next()
.expect("Payment must be something; qed")
.0;
let required = MultiAsset {
id: asset_id.clone(),
fun: Fungible(weight as u128),
};

if let MultiAsset {
fun: _,
id: Concrete(ref id),
} = &required
{
self.0 = id.clone();
}

let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?;
Ok(unused)
}

fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
if weight.is_zero() {
None
} else {
Some((self.0.clone(), weight as u128).into())
}
}
}

pub struct XcmConfig;
impl Config for XcmConfig {
type Call = Call;
type XcmSender = XcmRouter;
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = XcmOriginToCallOrigin;
type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
type IsTeleporter = ();
type IsTeleporter = NativeAsset;
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<ConstU64<10>, Call, ConstU32<100>>;
Expand Down Expand Up @@ -277,7 +235,7 @@ parameter_type_with_key! {
pub ParachainMinFee: |location: MultiLocation| -> Option<u128> {
#[allow(clippy::match_ref_pats)] // false positive
match (location.parents, location.first_interior()) {
(1, Some(Parachain(2))) => Some(40),
(1, Some(Parachain(3))) => Some(40),
_ => None,
}
};
Expand Down
Loading