mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 02:21:04 +00:00
XCM: Land xcm-handler and xcm-executor (#1771)
Co-authored-by: Gavin Wood <gavin@parity.io>
This commit is contained in:
Generated
+15
@@ -10033,6 +10033,21 @@ dependencies = [
|
||||
"parity-scale-codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcm-executor"
|
||||
version = "0.8.22"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"impl-trait-for-tuples",
|
||||
"parity-scale-codec",
|
||||
"sp-arithmetic",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"xcm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yamux"
|
||||
version = "0.8.0"
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ members = [
|
||||
"service",
|
||||
"validation",
|
||||
"xcm",
|
||||
|
||||
"xcm/xcm-executor",
|
||||
"node/collation-generation",
|
||||
"node/core/av-store",
|
||||
"node/core/backing",
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
name = "xcm-executor"
|
||||
description = "An abstract and configurable XCM message executor."
|
||||
version = "0.8.22"
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
|
||||
impl-trait-for-tuples = "0.1.3"
|
||||
|
||||
xcm = { path = "..", default-features = false }
|
||||
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
sp-arithmetic = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-support/std",
|
||||
"sp-std/std",
|
||||
"sp-io/std",
|
||||
"sp-arithmetic/std",
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
]
|
||||
@@ -0,0 +1,241 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::{prelude::*, mem::swap, collections::{btree_map::BTreeMap, btree_set::BTreeSet}};
|
||||
use xcm::v0::{MultiAsset, MultiLocation, AssetInstance};
|
||||
use sp_runtime::RuntimeDebug;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug)]
|
||||
pub enum AssetId {
|
||||
Concrete(MultiLocation),
|
||||
Abstract(Vec<u8>),
|
||||
}
|
||||
|
||||
impl AssetId {
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
|
||||
if let AssetId::Concrete(ref mut l) = self {
|
||||
l.prepend_with(prepend.clone()).map_err(|_| ())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, RuntimeDebug)]
|
||||
pub struct Assets {
|
||||
pub fungible: BTreeMap<AssetId, u128>,
|
||||
pub non_fungible: BTreeSet<(AssetId, AssetInstance)>,
|
||||
}
|
||||
|
||||
impl From<Vec<MultiAsset>> for Assets {
|
||||
fn from(assets: Vec<MultiAsset>) -> Assets {
|
||||
let mut result = Self::default();
|
||||
for asset in assets.into_iter() {
|
||||
result.saturating_subsume(asset)
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Assets> for Vec<MultiAsset> {
|
||||
fn from(a: Assets) -> Self {
|
||||
a.into_assets_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Assets {
|
||||
pub fn into_assets_iter(self) -> impl Iterator<Item=MultiAsset> {
|
||||
let fungible = self.fungible.into_iter()
|
||||
.map(|(id, amount)| match id {
|
||||
AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount },
|
||||
AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount },
|
||||
});
|
||||
let non_fungible = self.non_fungible.into_iter()
|
||||
.map(|(id, instance)| match id {
|
||||
AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance },
|
||||
AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance },
|
||||
});
|
||||
fungible.chain(non_fungible)
|
||||
}
|
||||
|
||||
pub fn assets_iter<'a>(&'a self) -> impl Iterator<Item=MultiAsset> + 'a {
|
||||
let fungible = self.fungible.iter()
|
||||
.map(|(id, &amount)| match id.clone() {
|
||||
AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount },
|
||||
AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount },
|
||||
});
|
||||
let non_fungible = self.non_fungible.iter()
|
||||
.map(|&(ref class, ref instance)| match class.clone() {
|
||||
AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance: instance.clone() },
|
||||
AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance: instance.clone() },
|
||||
});
|
||||
fungible.chain(non_fungible)
|
||||
}
|
||||
|
||||
/// Modify `self` to include `MultiAsset`, saturating if necessary.
|
||||
pub fn saturating_subsume(&mut self, asset: MultiAsset) {
|
||||
match asset {
|
||||
MultiAsset::ConcreteFungible { id, amount } => {
|
||||
self.fungible
|
||||
.entry(AssetId::Concrete(id))
|
||||
.and_modify(|e| *e = e.saturating_add(amount))
|
||||
.or_insert(amount);
|
||||
}
|
||||
MultiAsset::AbstractFungible { id, amount } => {
|
||||
self.fungible
|
||||
.entry(AssetId::Abstract(id))
|
||||
.and_modify(|e| *e = e.saturating_add(amount))
|
||||
.or_insert(amount);
|
||||
}
|
||||
MultiAsset::ConcreteNonFungible { class, instance} => {
|
||||
self.non_fungible.insert((AssetId::Concrete(class), instance));
|
||||
}
|
||||
MultiAsset::AbstractNonFungible { class, instance} => {
|
||||
self.non_fungible.insert((AssetId::Abstract(class), instance));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn saturating_subsume_fungible(&mut self, id: AssetId, amount: u128) {
|
||||
self.fungible
|
||||
.entry(id)
|
||||
.and_modify(|e| *e = e.saturating_add(amount))
|
||||
.or_insert(amount);
|
||||
}
|
||||
|
||||
pub fn saturating_subsume_non_fungible(&mut self, class: AssetId, instance: AssetInstance) {
|
||||
self.non_fungible.insert((class, instance));
|
||||
}
|
||||
|
||||
/// Alter any concretely identified assets according to the given `MultiLocation`.
|
||||
///
|
||||
/// WARNING: For now we consider this infallible and swallow any errors. It is thus the caller's responsibility to
|
||||
/// ensure that any internal asset IDs are able to be prepended without overflow.
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) {
|
||||
let mut fungible = Default::default();
|
||||
sp_std::mem::swap(&mut self.fungible, &mut fungible);
|
||||
self.fungible = fungible.into_iter()
|
||||
.map(|(mut id, amount)| { let _ = id.reanchor(prepend); (id, amount) })
|
||||
.collect();
|
||||
let mut non_fungible = Default::default();
|
||||
sp_std::mem::swap(&mut self.non_fungible, &mut non_fungible);
|
||||
self.non_fungible = non_fungible.into_iter()
|
||||
.map(|(mut class, inst)| { let _ = class.reanchor(prepend); (class, inst) })
|
||||
.collect();
|
||||
}
|
||||
|
||||
/// Return the assets in `self`, but (asset-wise) of no greater value than `assets`.
|
||||
///
|
||||
/// Result is undefined if `assets` includes elements which match to the same asset more than once.
|
||||
pub fn min<'a, I: Iterator<Item=&'a MultiAsset>>(&self, assets: I) -> Self {
|
||||
let mut result = Assets::default();
|
||||
for asset in assets.into_iter() {
|
||||
match asset {
|
||||
MultiAsset::None => (),
|
||||
MultiAsset::All => return self.clone(),
|
||||
x @ MultiAsset::ConcreteFungible { .. } | x @ MultiAsset::AbstractFungible { .. } => {
|
||||
let (id, amount) = match x {
|
||||
MultiAsset::ConcreteFungible { id, amount } => (AssetId::Concrete(id.clone()), *amount),
|
||||
MultiAsset::AbstractFungible { id, amount } => (AssetId::Abstract(id.clone()), *amount),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if let Some(v) = self.fungible.get(&id) {
|
||||
result.saturating_subsume_fungible(id, amount.max(*v));
|
||||
}
|
||||
}
|
||||
x @ MultiAsset::ConcreteNonFungible { .. } | x @ MultiAsset::AbstractNonFungible { .. } => {
|
||||
let (class, instance) = match x {
|
||||
MultiAsset::ConcreteNonFungible { class, instance } => (AssetId::Concrete(class.clone()), instance.clone()),
|
||||
MultiAsset::AbstractNonFungible { class, instance } => (AssetId::Abstract(class.clone()), instance.clone()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let item = (class, instance);
|
||||
if self.non_fungible.contains(&item) {
|
||||
result.non_fungible.insert(item);
|
||||
}
|
||||
}
|
||||
// TODO: implement partial wildcards.
|
||||
_ => (),
|
||||
// MultiAsset::AllFungible
|
||||
// | MultiAsset::AllNonFungible
|
||||
// | MultiAsset::AllAbstractFungible { id }
|
||||
// | MultiAsset::AllAbstractNonFungible { class }
|
||||
// | MultiAsset::AllConcreteFungible { id }
|
||||
// | MultiAsset::AllConcreteNonFungible { class } => (),
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Take all possible assets up to `assets` from `self`, mutating `self` and returning the
|
||||
/// assets taken.
|
||||
///
|
||||
/// Wildcards work.
|
||||
pub fn saturating_take(&mut self, assets: Vec<MultiAsset>) -> Assets {
|
||||
let mut result = Assets::default();
|
||||
for asset in assets.into_iter() {
|
||||
match asset {
|
||||
MultiAsset::None => (),
|
||||
MultiAsset::All => return self.swapped(Assets::default()),
|
||||
x @ MultiAsset::ConcreteFungible {..} | x @ MultiAsset::AbstractFungible {..} => {
|
||||
let (id, amount) = match x {
|
||||
MultiAsset::ConcreteFungible { id, amount } => (AssetId::Concrete(id), amount),
|
||||
MultiAsset::AbstractFungible { id, amount } => (AssetId::Abstract(id), amount),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// remove the maxmimum possible up to id/amount from self, add the removed onto
|
||||
// result
|
||||
self.fungible.entry(id.clone())
|
||||
.and_modify(|e| if *e >= amount {
|
||||
result.saturating_subsume_fungible(id, amount);
|
||||
*e = *e - amount;
|
||||
} else {
|
||||
result.saturating_subsume_fungible(id, *e);
|
||||
*e = 0
|
||||
});
|
||||
}
|
||||
x @ MultiAsset::ConcreteNonFungible {..} | x @ MultiAsset::AbstractNonFungible {..} => {
|
||||
let (class, instance) = match x {
|
||||
MultiAsset::ConcreteNonFungible { class, instance } => (AssetId::Concrete(class), instance),
|
||||
MultiAsset::AbstractNonFungible { class, instance } => (AssetId::Abstract(class), instance),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// remove the maxmimum possible up to id/amount from self, add the removed onto
|
||||
// result
|
||||
if let Some(entry) = self.non_fungible.take(&(class, instance)) {
|
||||
self.non_fungible.insert(entry);
|
||||
}
|
||||
}
|
||||
// TODO: implement partial wildcards.
|
||||
_ => {
|
||||
Default::default()
|
||||
}
|
||||
// MultiAsset::AllFungible
|
||||
// | MultiAsset::AllNonFungible
|
||||
// | MultiAsset::AllAbstractFungible { id }
|
||||
// | MultiAsset::AllAbstractNonFungible { class }
|
||||
// | MultiAsset::AllConcreteFungible { id }
|
||||
// | MultiAsset::AllConcreteNonFungible { class } => (),
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn swapped(&mut self, mut with: Assets) -> Self {
|
||||
swap(&mut *self, &mut with);
|
||||
with
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use xcm::v0::SendXcm;
|
||||
use frame_support::dispatch::{Dispatchable, Parameter};
|
||||
use crate::traits::{TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation};
|
||||
|
||||
/// The trait to parametrize the `XcmExecutor`.
|
||||
pub trait Config {
|
||||
/// The outer call dispatch type.
|
||||
type Call: Parameter + Dispatchable;
|
||||
|
||||
/// How to send an onward XCM message.
|
||||
type XcmSender: SendXcm;
|
||||
|
||||
/// How to withdraw and deposit an asset.
|
||||
type AssetTransactor: TransactAsset;
|
||||
|
||||
/// How to get a call origin from a `OriginKind` value.
|
||||
type OriginConverter: ConvertOrigin<<Self::Call as Dispatchable>::Origin>;
|
||||
|
||||
/// Combinations of (Location, Asset) pairs which we unilateral trust as reserves.
|
||||
type IsReserve: FilterAssetLocation;
|
||||
|
||||
/// Combinations of (Location, Asset) pairs which we bilateral trust as teleporters.
|
||||
type IsTeleporter: FilterAssetLocation;
|
||||
|
||||
/// Means of inverting a location.
|
||||
type LocationInverter: InvertLocation;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::{prelude::*, marker::PhantomData, convert::TryInto};
|
||||
use frame_support::{ensure, dispatch::Dispatchable};
|
||||
use codec::Decode;
|
||||
use xcm::v0::{
|
||||
Xcm, Order, ExecuteXcm, SendXcm, Error as XcmError, Result as XcmResult,
|
||||
MultiLocation, MultiAsset, Junction,
|
||||
};
|
||||
|
||||
pub mod traits;
|
||||
mod assets;
|
||||
mod config;
|
||||
|
||||
use traits::{TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation};
|
||||
pub use assets::{Assets, AssetId};
|
||||
pub use config::Config;
|
||||
|
||||
pub struct XcmExecutor<Config>(PhantomData<Config>);
|
||||
|
||||
impl<Config: config::Config> ExecuteXcm for XcmExecutor<Config> {
|
||||
fn execute_xcm(origin: MultiLocation, msg: Xcm) -> XcmResult {
|
||||
let (mut holding, effects) = match (origin.clone(), msg) {
|
||||
(origin, Xcm::RelayedFrom { superorigin, inner }) => {
|
||||
// We ensure that it doesn't contain any `Parent` Junctions which would imply a privilege escalation.
|
||||
let mut new_origin = origin;
|
||||
for j in superorigin.into_iter() {
|
||||
ensure!(j.is_sub_consensus(), XcmError::EscalationOfPrivilege);
|
||||
new_origin.push(j).map_err(|_| XcmError::MultiLocationFull)?;
|
||||
}
|
||||
return Self::execute_xcm(
|
||||
new_origin,
|
||||
(*inner).try_into().map_err(|_| XcmError::UnhandledXcmVersion)?
|
||||
)
|
||||
}
|
||||
(origin, Xcm::WithdrawAsset { assets, effects }) => {
|
||||
// Take `assets` from the origin account (on-chain) and place in holding.
|
||||
let mut holding = Assets::default();
|
||||
for asset in assets {
|
||||
let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?;
|
||||
holding.saturating_subsume(withdrawn);
|
||||
}
|
||||
(holding, effects)
|
||||
}
|
||||
(origin, Xcm::ReserveAssetDeposit { assets, effects }) => {
|
||||
// check whether we trust origin to be our reserve location for this asset.
|
||||
if assets.iter().all(|asset| Config::IsReserve::filter_asset_location(asset, &origin)) {
|
||||
// We only trust the origin to send us assets that they identify as their
|
||||
// sovereign assets.
|
||||
(Assets::from(assets), effects)
|
||||
} else {
|
||||
Err(XcmError::UntrustedReserveLocation)?
|
||||
}
|
||||
}
|
||||
(origin, Xcm::TeleportAsset { assets, effects }) => {
|
||||
// check whether we trust origin to teleport this asset to us via config trait.
|
||||
// TODO: should de-wildcard `assets` before passing in.
|
||||
frame_support::debug::print!("Teleport from {:?}", origin);
|
||||
if assets.iter().all(|asset| Config::IsTeleporter::filter_asset_location(asset, &origin)) {
|
||||
// We only trust the origin to send us assets that they identify as their
|
||||
// sovereign assets.
|
||||
(Assets::from(assets), effects)
|
||||
} else {
|
||||
Err(XcmError::UntrustedTeleportLocation)?
|
||||
}
|
||||
}
|
||||
(origin, Xcm::Transact { origin_type, call }) => {
|
||||
// We assume that the Relay-chain is allowed to use transact on this parachain.
|
||||
|
||||
// TODO: Weight fees should be paid.
|
||||
|
||||
// TODO: allow this to be configurable in the trait.
|
||||
// TODO: allow the trait to issue filters for the relay-chain
|
||||
let message_call = Config::Call::decode(&mut &call[..]).map_err(|_| XcmError::FailedToDecode)?;
|
||||
let dispatch_origin = Config::OriginConverter::convert_origin(origin, origin_type)
|
||||
.map_err(|_| XcmError::BadOrigin)?;
|
||||
let _ok = message_call.dispatch(dispatch_origin).is_ok();
|
||||
// Not much to do with the result as it is. It's up to the parachain to ensure that the
|
||||
// message makes sense.
|
||||
return Ok(());
|
||||
}
|
||||
(origin, Xcm::RelayTo { dest: MultiLocation::X1(Junction::Parachain { id }), inner }) => {
|
||||
let msg = Xcm::RelayedFrom { superorigin: origin, inner }.into();
|
||||
return Config::XcmSender::send_xcm(Junction::Parachain { id }.into(), msg)
|
||||
},
|
||||
_ => Err(XcmError::UnhandledXcmMessage)?, // Unhandled XCM message.
|
||||
};
|
||||
|
||||
// TODO: stuff that should happen after holding is populated but before effects,
|
||||
// including depositing fees for effects from holding account.
|
||||
|
||||
for effect in effects.into_iter() {
|
||||
let _ = Self::execute_effects(&origin, &mut holding, effect)?;
|
||||
}
|
||||
|
||||
// TODO: stuff that should happen after effects including refunding unused fees.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Config: config::Config> XcmExecutor<Config> {
|
||||
fn reanchored(mut assets: Assets, dest: &MultiLocation) -> Vec<MultiAsset> {
|
||||
let inv_dest = Config::LocationInverter::invert_location(&dest);
|
||||
assets.reanchor(&inv_dest);
|
||||
assets.into_assets_iter().collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn execute_effects(_origin: &MultiLocation, holding: &mut Assets, effect: Order) -> XcmResult {
|
||||
match effect {
|
||||
Order::DepositAsset { assets, dest } => {
|
||||
let deposited = holding.saturating_take(assets);
|
||||
for asset in deposited.into_assets_iter() {
|
||||
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
Order::DepositReserveAsset { assets, dest, effects } => {
|
||||
let deposited = holding.saturating_take(assets);
|
||||
for asset in deposited.assets_iter() {
|
||||
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
|
||||
}
|
||||
let assets = Self::reanchored(deposited, &dest);
|
||||
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })
|
||||
},
|
||||
Order::InitiateReserveWithdraw { assets, reserve, effects} => {
|
||||
let assets = Self::reanchored(holding.saturating_take(assets), &reserve);
|
||||
Config::XcmSender::send_xcm(reserve, Xcm::WithdrawAsset { assets, effects })
|
||||
}
|
||||
Order::InitiateTeleport { assets, dest, effects} => {
|
||||
let assets = Self::reanchored(holding.saturating_take(assets), &dest);
|
||||
Config::XcmSender::send_xcm(dest, Xcm::TeleportAsset { assets, effects })
|
||||
}
|
||||
Order::QueryHolding { query_id, dest, assets } => {
|
||||
let assets = Self::reanchored(holding.min(assets.iter()), &dest);
|
||||
Config::XcmSender::send_xcm(dest, Xcm::Balances { query_id, assets })
|
||||
}
|
||||
_ => Err(XcmError::UnhandledEffect)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::{result::Result, marker::PhantomData, convert::TryFrom};
|
||||
use sp_runtime::traits::CheckedConversion;
|
||||
use xcm::v0::{Error as XcmError, Result as XcmResult, MultiAsset, MultiLocation, OriginKind};
|
||||
use frame_support::traits::Get;
|
||||
|
||||
pub trait FilterAssetLocation {
|
||||
/// A filter to distinguish between asset/location pairs.
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool;
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl FilterAssetLocation for Tuple {
|
||||
fn filter_asset_location(what: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
for_tuples!( #(
|
||||
if Tuple::filter_asset_location(what, origin) { return true }
|
||||
)* );
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeAsset;
|
||||
impl FilterAssetLocation for NativeAsset {
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
matches!(asset, MultiAsset::ConcreteFungible { ref id, .. } if id == origin)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Case<T>(PhantomData<T>);
|
||||
impl<T: Get<(MultiAsset, MultiLocation)>> FilterAssetLocation for Case<T> {
|
||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||
let (a, o) = T::get();
|
||||
&a == asset && &o == origin
|
||||
}
|
||||
}
|
||||
|
||||
/// Facility for asset transacting.
|
||||
///
|
||||
/// This should work with as many asset/location combinations as possible. Locations to support may include non-
|
||||
/// account locations such as a `MultiLocation::X1(Junction::Parachain)`. Different chains may handle them in
|
||||
/// different ways.
|
||||
pub trait TransactAsset {
|
||||
/// Deposit the `what` asset into the account of `who`.
|
||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult;
|
||||
|
||||
/// Withdraw the given asset from the consensus system. Return the actual asset withdrawn. In
|
||||
/// the case of `what` being a wildcard, this may be something more specific.
|
||||
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result<MultiAsset, XcmError>;
|
||||
|
||||
/// Move an `asset` `from` one location in `to` another location.
|
||||
///
|
||||
/// Undefined if from account doesn't own this asset.
|
||||
fn transfer_asset(asset: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result<MultiAsset, XcmError> {
|
||||
let withdrawn = Self::withdraw_asset(asset, from)?;
|
||||
Self::deposit_asset(&withdrawn, to)?;
|
||||
Ok(withdrawn)
|
||||
}
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl TransactAsset for Tuple {
|
||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult {
|
||||
for_tuples!( #(
|
||||
match Tuple::deposit_asset(what, who) { o @ Ok(_) => return o, _ => () }
|
||||
)* );
|
||||
Err(XcmError::Unimplemented)
|
||||
}
|
||||
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result<MultiAsset, XcmError> {
|
||||
for_tuples!( #(
|
||||
match Tuple::withdraw_asset(what, who) { o @ Ok(_) => return o, _ => () }
|
||||
)* );
|
||||
Err(XcmError::Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait MatchesFungible<Balance> {
|
||||
fn matches_fungible(a: &MultiAsset) -> Option<Balance>;
|
||||
}
|
||||
pub struct IsConcrete<T>(PhantomData<T>);
|
||||
impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> {
|
||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||
match a {
|
||||
MultiAsset::ConcreteFungible { id, amount } if id == &T::get() =>
|
||||
CheckedConversion::checked_from(*amount),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct IsAbstract<T>(PhantomData<T>);
|
||||
impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> {
|
||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||
match a {
|
||||
MultiAsset::AbstractFungible { id, amount } if &id[..] == T::get() =>
|
||||
CheckedConversion::checked_from(*amount),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<B: From<u128>, X: MatchesFungible<B>, Y: MatchesFungible<B>> MatchesFungible<B> for (X, Y) {
|
||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||
X::matches_fungible(a).or_else(|| Y::matches_fungible(a))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LocationConversion<AccountId> {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId>;
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId>;
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl<AccountId> LocationConversion<AccountId> for Tuple {
|
||||
fn from_location(location: &MultiLocation) -> Option<AccountId> {
|
||||
for_tuples!( #(
|
||||
if let Some(result) = Tuple::from_location(location) { return Some(result) }
|
||||
)* );
|
||||
None
|
||||
}
|
||||
fn try_into_location(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
for_tuples!( #(
|
||||
let who = match Tuple::try_into_location(who) { Err(w) => w, r => return r };
|
||||
)* );
|
||||
Err(who)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ConvertOrigin<Origin> {
|
||||
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<Origin, MultiLocation>;
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl<O> ConvertOrigin<O> for Tuple {
|
||||
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<O, MultiLocation> {
|
||||
for_tuples!( #(
|
||||
let origin = match Tuple::convert_origin(origin, kind) { Err(o) => o, r => return r };
|
||||
)* );
|
||||
Err(origin)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InvertLocation {
|
||||
fn invert_location(l: &MultiLocation) -> MultiLocation;
|
||||
}
|
||||
Reference in New Issue
Block a user