diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 21a9b2d641..959869e595 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -11134,6 +11134,7 @@ name = "xcm-builder" version = "0.8.30" dependencies = [ "frame-support", + "impl-trait-for-tuples", "parity-scale-codec", "polkadot-parachain", "sp-arithmetic", diff --git a/polkadot/xcm/src/v0/multi_location.rs b/polkadot/xcm/src/v0/multi_location.rs index ba3ef8c827..f2ac7f891e 100644 --- a/polkadot/xcm/src/v0/multi_location.rs +++ b/polkadot/xcm/src/v0/multi_location.rs @@ -1,4 +1,4 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. +// Copyright 2020-2021 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Substrate is free software: you can redistribute it and/or modify @@ -48,14 +48,22 @@ use crate::VersionedMultiLocation; pub enum MultiLocation { /// The interpreting consensus system. Null, - /// A relative path comprising one junction. + /// A relative path comprising 1 junction. X1(Junction), - /// A relative path comprising two junctions. + /// A relative path comprising 2 junctions. X2(Junction, Junction), - /// A relative path comprising three junctions. + /// A relative path comprising 3 junctions. X3(Junction, Junction, Junction), - /// A relative path comprising four junctions. + /// A relative path comprising 4 junctions. X4(Junction, Junction, Junction, Junction), + /// A relative path comprising 5 junctions. + X5(Junction, Junction, Junction, Junction, Junction), + /// A relative path comprising 6 junctions. + X6(Junction, Junction, Junction, Junction, Junction, Junction), + /// A relative path comprising 7 junctions. + X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction), + /// A relative path comprising 8 junctions. + X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction), } impl From for MultiLocation { @@ -89,6 +97,26 @@ impl From<(Junction, Junction, Junction, Junction)> for MultiLocation { MultiLocation::X4(x.0, x.1, x.2, x.3) } } +impl From<(Junction, Junction, Junction, Junction, Junction)> for MultiLocation { + fn from(x: (Junction, Junction, Junction, Junction, Junction)) -> Self { + MultiLocation::X5(x.0, x.1, x.2, x.3, x.4) + } +} +impl From<(Junction, Junction, Junction, Junction, Junction, Junction)> for MultiLocation { + fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction)) -> Self { + MultiLocation::X6(x.0, x.1, x.2, x.3, x.4, x.5) + } +} +impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction)> for MultiLocation { + fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction)) -> Self { + MultiLocation::X7(x.0, x.1, x.2, x.3, x.4, x.5, x.6) + } +} +impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction)> for MultiLocation { + fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction)) -> Self { + MultiLocation::X8(x.0, x.1, x.2, x.3, x.4, x.5, x.6, x.7) + } +} impl From<[Junction; 0]> for MultiLocation { fn from(_: [Junction; 0]) -> Self { @@ -119,6 +147,30 @@ impl From<[Junction; 4]> for MultiLocation { MultiLocation::X4(x0, x1, x2, x3) } } +impl From<[Junction; 5]> for MultiLocation { + fn from(x: [Junction; 5]) -> Self { + let [x0, x1, x2, x3, x4] = x; + MultiLocation::X5(x0, x1, x2, x3, x4) + } +} +impl From<[Junction; 6]> for MultiLocation { + fn from(x: [Junction; 6]) -> Self { + let [x0, x1, x2, x3, x4, x5] = x; + MultiLocation::X6(x0, x1, x2, x3, x4, x5) + } +} +impl From<[Junction; 7]> for MultiLocation { + fn from(x: [Junction; 7]) -> Self { + let [x0, x1, x2, x3, x4, x5, x6] = x; + MultiLocation::X7(x0, x1, x2, x3, x4, x5, x6) + } +} +impl From<[Junction; 8]> for MultiLocation { + fn from(x: [Junction; 8]) -> Self { + let [x0, x1, x2, x3, x4, x5, x6, x7] = x; + MultiLocation::X8(x0, x1, x2, x3, x4, x5, x6, x7) + } +} pub struct MultiLocationIterator(MultiLocation); impl Iterator for MultiLocationIterator { @@ -164,6 +216,10 @@ impl MultiLocation { MultiLocation::X2(ref a, ..) => Some(a), MultiLocation::X3(ref a, ..) => Some(a), MultiLocation::X4(ref a, ..) => Some(a), + MultiLocation::X5(ref a, ..) => Some(a), + MultiLocation::X6(ref a, ..) => Some(a), + MultiLocation::X7(ref a, ..) => Some(a), + MultiLocation::X8(ref a, ..) => Some(a), } } @@ -175,6 +231,10 @@ impl MultiLocation { MultiLocation::X2(.., ref a) => Some(a), MultiLocation::X3(.., ref a) => Some(a), MultiLocation::X4(.., ref a) => Some(a), + MultiLocation::X5(.., ref a) => Some(a), + MultiLocation::X6(.., ref a) => Some(a), + MultiLocation::X7(.., ref a) => Some(a), + MultiLocation::X8(.., ref a) => Some(a), } } @@ -187,6 +247,10 @@ impl MultiLocation { MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)), MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)), MultiLocation::X4(a, b, c ,d) => (MultiLocation::X3(b, c, d), Some(a)), + MultiLocation::X5(a, b, c ,d, e) => (MultiLocation::X4(b, c, d, e), Some(a)), + MultiLocation::X6(a, b, c ,d, e, f) => (MultiLocation::X5(b, c, d, e, f), Some(a)), + MultiLocation::X7(a, b, c ,d, e, f, g) => (MultiLocation::X6(b, c, d, e, f, g), Some(a)), + MultiLocation::X8(a, b, c ,d, e, f, g, h) => (MultiLocation::X7(b, c, d, e, f, g, h), Some(a)), } } @@ -199,6 +263,10 @@ impl MultiLocation { MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)), MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)), MultiLocation::X4(a, b, c ,d) => (MultiLocation::X3(a, b, c), Some(d)), + MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(a, b, c, d), Some(e)), + MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(a, b, c, d, e), Some(f)), + MultiLocation::X7(a, b, c, d, e, f, g) => (MultiLocation::X6(a, b, c, d, e, f), Some(g)), + MultiLocation::X8(a, b, c, d, e, f, g, h) => (MultiLocation::X7(a, b, c, d, e, f, g), Some(h)), } } @@ -228,6 +296,10 @@ impl MultiLocation { MultiLocation::X1(a) => MultiLocation::X2(a, new), MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new), MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new), + MultiLocation::X4(a, b, c, d) => MultiLocation::X5(a, b, c, d, new), + MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(a, b, c, d, e, new), + MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(a, b, c, d, e, f, new), + MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(a, b, c, d, e, f, g, new), s => Err(s)?, }) } @@ -240,6 +312,10 @@ impl MultiLocation { MultiLocation::X1(a) => MultiLocation::X2(new, a), MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b), MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c), + MultiLocation::X4(a, b, c, d) => MultiLocation::X5(new, a, b, c, d), + MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(new, a, b, c, d, e), + MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(new, a, b, c, d, e, f), + MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(new, a, b, c, d, e, f, g), s => Err(s)?, }) } @@ -252,6 +328,10 @@ impl MultiLocation { MultiLocation::X2(..) => 2, MultiLocation::X3(..) => 3, MultiLocation::X4(..) => 4, + MultiLocation::X5(..) => 5, + MultiLocation::X6(..) => 6, + MultiLocation::X7(..) => 7, + MultiLocation::X8(..) => 8, } } @@ -262,12 +342,38 @@ impl MultiLocation { (0, MultiLocation::X2(ref a, ..)) => a, (0, MultiLocation::X3(ref a, ..)) => a, (0, MultiLocation::X4(ref a, ..)) => a, + (0, MultiLocation::X5(ref a, ..)) => a, + (0, MultiLocation::X6(ref a, ..)) => a, + (0, MultiLocation::X7(ref a, ..)) => a, + (0, MultiLocation::X8(ref a, ..)) => a, (1, MultiLocation::X2(_, ref a)) => a, (1, MultiLocation::X3(_, ref a, ..)) => a, (1, MultiLocation::X4(_, ref a, ..)) => a, + (1, MultiLocation::X5(_, ref a, ..)) => a, + (1, MultiLocation::X6(_, ref a, ..)) => a, + (1, MultiLocation::X7(_, ref a, ..)) => a, + (1, MultiLocation::X8(_, ref a, ..)) => a, (2, MultiLocation::X3(_, _, ref a)) => a, (2, MultiLocation::X4(_, _, ref a, ..)) => a, + (2, MultiLocation::X5(_, _, ref a, ..)) => a, + (2, MultiLocation::X6(_, _, ref a, ..)) => a, + (2, MultiLocation::X7(_, _, ref a, ..)) => a, + (2, MultiLocation::X8(_, _, ref a, ..)) => a, (3, MultiLocation::X4(_, _, _, ref a)) => a, + (3, MultiLocation::X5(_, _, _, ref a, ..)) => a, + (3, MultiLocation::X6(_, _, _, ref a, ..)) => a, + (3, MultiLocation::X7(_, _, _, ref a, ..)) => a, + (3, MultiLocation::X8(_, _, _, ref a, ..)) => a, + (4, MultiLocation::X5(_, _, _, _, ref a)) => a, + (4, MultiLocation::X6(_, _, _, _, ref a, ..)) => a, + (4, MultiLocation::X7(_, _, _, _, ref a, ..)) => a, + (4, MultiLocation::X8(_, _, _, _, ref a, ..)) => a, + (5, MultiLocation::X6(_, _, _, _, _, ref a)) => a, + (5, MultiLocation::X7(_, _, _, _, _, ref a, ..)) => a, + (5, MultiLocation::X8(_, _, _, _, _, ref a, ..)) => a, + (6, MultiLocation::X7(_, _, _, _, _, _, ref a)) => a, + (6, MultiLocation::X8(_, _, _, _, _, _, ref a, ..)) => a, + (7, MultiLocation::X8(_, _, _, _, _, _, _, ref a)) => a, _ => return None, }) } @@ -280,12 +386,38 @@ impl MultiLocation { (0, MultiLocation::X2(ref mut a, ..)) => a, (0, MultiLocation::X3(ref mut a, ..)) => a, (0, MultiLocation::X4(ref mut a, ..)) => a, + (0, MultiLocation::X5(ref mut a, ..)) => a, + (0, MultiLocation::X6(ref mut a, ..)) => a, + (0, MultiLocation::X7(ref mut a, ..)) => a, + (0, MultiLocation::X8(ref mut a, ..)) => a, (1, MultiLocation::X2(_, ref mut a)) => a, (1, MultiLocation::X3(_, ref mut a, ..)) => a, (1, MultiLocation::X4(_, ref mut a, ..)) => a, + (1, MultiLocation::X5(_, ref mut a, ..)) => a, + (1, MultiLocation::X6(_, ref mut a, ..)) => a, + (1, MultiLocation::X7(_, ref mut a, ..)) => a, + (1, MultiLocation::X8(_, ref mut a, ..)) => a, (2, MultiLocation::X3(_, _, ref mut a)) => a, (2, MultiLocation::X4(_, _, ref mut a, ..)) => a, + (2, MultiLocation::X5(_, _, ref mut a, ..)) => a, + (2, MultiLocation::X6(_, _, ref mut a, ..)) => a, + (2, MultiLocation::X7(_, _, ref mut a, ..)) => a, + (2, MultiLocation::X8(_, _, ref mut a, ..)) => a, (3, MultiLocation::X4(_, _, _, ref mut a)) => a, + (3, MultiLocation::X5(_, _, _, ref mut a, ..)) => a, + (3, MultiLocation::X6(_, _, _, ref mut a, ..)) => a, + (3, MultiLocation::X7(_, _, _, ref mut a, ..)) => a, + (3, MultiLocation::X8(_, _, _, ref mut a, ..)) => a, + (4, MultiLocation::X5(_, _, _, _, ref mut a)) => a, + (4, MultiLocation::X6(_, _, _, _, ref mut a, ..)) => a, + (4, MultiLocation::X7(_, _, _, _, ref mut a, ..)) => a, + (4, MultiLocation::X8(_, _, _, _, ref mut a, ..)) => a, + (5, MultiLocation::X6(_, _, _, _, _, ref mut a)) => a, + (5, MultiLocation::X7(_, _, _, _, _, ref mut a, ..)) => a, + (5, MultiLocation::X8(_, _, _, _, _, ref mut a, ..)) => a, + (6, MultiLocation::X7(_, _, _, _, _, _, ref mut a)) => a, + (6, MultiLocation::X8(_, _, _, _, _, _, ref mut a, ..)) => a, + (7, MultiLocation::X8(_, _, _, _, _, _, _, ref mut a)) => a, _ => return None, }) } @@ -334,12 +466,71 @@ impl MultiLocation { /// Returns the number of `Parent` junctions at the beginning of `self`. pub fn parent_count(&self) -> usize { match self { + MultiLocation::X8( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, + Junction::Parent, Junction::Parent, Junction::Parent + ) => 8, + + MultiLocation::X8( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, + Junction::Parent, Junction::Parent, .. + ) => 7, + MultiLocation::X7( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, + Junction::Parent, Junction::Parent + ) => 7, + + MultiLocation::X8( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, + Junction::Parent, .. + ) => 6, + MultiLocation::X7( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, + Junction::Parent, .. + ) => 6, + MultiLocation::X6( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, + Junction::Parent + ) => 6, + + MultiLocation::X8( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, .. + ) => 5, + MultiLocation::X7( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, .. + ) => 5, + MultiLocation::X6( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, .. + ) => 5, + MultiLocation::X5( + Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent + ) => 5, + + MultiLocation::X8(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, + MultiLocation::X7(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, + MultiLocation::X6(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, + MultiLocation::X5(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, MultiLocation::X4(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent) => 4, + + MultiLocation::X8(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, + MultiLocation::X7(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, + MultiLocation::X6(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, + MultiLocation::X5(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X4(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X3(Junction::Parent, Junction::Parent, Junction::Parent) => 3, + + MultiLocation::X8(Junction::Parent, Junction::Parent, ..) => 2, + MultiLocation::X7(Junction::Parent, Junction::Parent, ..) => 2, + MultiLocation::X6(Junction::Parent, Junction::Parent, ..) => 2, + MultiLocation::X5(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X4(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X3(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X2(Junction::Parent, Junction::Parent) => 2, + + MultiLocation::X8(Junction::Parent, ..) => 1, + MultiLocation::X7(Junction::Parent, ..) => 1, + MultiLocation::X6(Junction::Parent, ..) => 1, + MultiLocation::X5(Junction::Parent, ..) => 1, MultiLocation::X4(Junction::Parent, ..) => 1, MultiLocation::X3(Junction::Parent, ..) => 1, MultiLocation::X2(Junction::Parent, ..) => 1, diff --git a/polkadot/xcm/xcm-builder/Cargo.toml b/polkadot/xcm/xcm-builder/Cargo.toml index 6cf2959d9d..d3a5316f61 100644 --- a/polkadot/xcm/xcm-builder/Cargo.toml +++ b/polkadot/xcm/xcm-builder/Cargo.toml @@ -6,6 +6,7 @@ description = "Tools & types for building with XCM and its executor." version = "0.8.30" [dependencies] +impl-trait-for-tuples = "0.2.0" parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } xcm = { path = "..", default-features = false } xcm-executor = { path = "../xcm-executor", default-features = false } diff --git a/polkadot/xcm/xcm-builder/src/currency_adapter.rs b/polkadot/xcm/xcm-builder/src/currency_adapter.rs index 7883d5959d..1a3b1f5499 100644 --- a/polkadot/xcm/xcm-builder/src/currency_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/currency_adapter.rs @@ -16,7 +16,7 @@ use sp_std::{result, convert::TryInto, marker::PhantomData}; use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation}; -use sp_arithmetic::traits::SaturatedConversion; +use sp_runtime::traits::SaturatedConversion; use frame_support::traits::{ExistenceRequirement::AllowDeath, WithdrawReasons}; use xcm_executor::traits::{MatchesFungible, LocationConversion, TransactAsset}; @@ -24,7 +24,7 @@ use xcm_executor::traits::{MatchesFungible, LocationConversion, TransactAsset}; enum Error { /// Asset not found. AssetNotFound, - /// `MultiLocation` to `AccountId` Conversion failed. + /// `MultiLocation` to `AccountId` conversion failed. AccountIdConversionFailed, /// `u128` amount to currency `Balance` conversion failed. AmountToBalanceConversionFailed, @@ -43,10 +43,7 @@ impl From for XcmError { } pub struct CurrencyAdapter( - PhantomData, - PhantomData, - PhantomData, - PhantomData, + PhantomData<(Currency, Matcher, AccountIdConverter, AccountId)> ); impl< @@ -59,13 +56,13 @@ impl< fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result { // Check we handle this asset. let amount: u128 = Matcher::matches_fungible(&what) - .ok_or(XcmError::from(Error::AssetNotFound))? + .ok_or(Error::AssetNotFound)? .saturated_into(); let who = AccountIdConverter::from_location(who) - .ok_or(XcmError::from(Error::AccountIdConversionFailed))?; + .ok_or(Error::AccountIdConversionFailed)?; let balance_amount = amount .try_into() - .map_err(|_| XcmError::from(Error::AmountToBalanceConversionFailed))?; + .map_err(|_| Error::AmountToBalanceConversionFailed)?; let _imbalance = Currency::deposit_creating(&who, balance_amount); Ok(()) } @@ -75,14 +72,14 @@ impl< who: &MultiLocation ) -> result::Result { // Check we handle this asset. - let amount: u128 = Matcher::matches_fungible(&what) - .ok_or(XcmError::from(Error::AssetNotFound))? + let amount: u128 = Matcher::matches_fungible(what) + .ok_or(Error::AssetNotFound)? .saturated_into(); let who = AccountIdConverter::from_location(who) - .ok_or(XcmError::from(Error::AccountIdConversionFailed))?; + .ok_or(Error::AccountIdConversionFailed)?; let balance_amount = amount .try_into() - .map_err(|_| XcmError::from(Error::AmountToBalanceConversionFailed))?; + .map_err(|_| Error::AmountToBalanceConversionFailed)?; Currency::withdraw(&who, balance_amount, WithdrawReasons::TRANSFER, AllowDeath) .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; Ok(what.clone()) diff --git a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs new file mode 100644 index 0000000000..f3acaae206 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs @@ -0,0 +1,267 @@ +// 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 . + +use sp_std::{prelude::*, result, convert::TryFrom, marker::PhantomData, borrow::Borrow}; +use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation, Junction}; +use frame_support::traits::{Get, tokens::fungibles::Mutate as Fungibles}; +use xcm_executor::traits::{LocationConversion, TransactAsset}; + +/// Asset transaction errors. +pub enum Error { + /// Asset not found. + AssetNotFound, + /// `MultiLocation` to `AccountId` conversion failed. + AccountIdConversionFailed, + /// `u128` amount to currency `Balance` conversion failed. + AmountToBalanceConversionFailed, + /// `MultiLocation` to `AssetId` conversion failed. + AssetIdConversionFailed, +} + +impl From for XcmError { + fn from(e: Error) -> Self { + match e { + Error::AssetNotFound => XcmError::FailedToTransactAsset("AssetNotFound"), + Error::AccountIdConversionFailed => + XcmError::FailedToTransactAsset("AccountIdConversionFailed"), + Error::AmountToBalanceConversionFailed => + XcmError::FailedToTransactAsset("AmountToBalanceConversionFailed"), + Error::AssetIdConversionFailed => + XcmError::FailedToTransactAsset("AssetIdConversionFailed"), + } + } +} + +/// Generic third-party conversion trait. Use this when you don't want to force the user to use default +/// impls of `From` and `Into` for the types you wish to convert between. +/// +/// One of `convert`/`convert_ref` and `reverse`/`reverse_ref` MUST be implemented. If possible, implement +/// `convert_ref`, since this will never result in a clone. Use `convert` when you definitely need to consume +/// the source value. +pub trait Convert { + /// Convert from `value` (of type `A`) into an equivalent value of type `B`, `Err` if not possible. + fn convert(value: A) -> result::Result { Self::convert_ref(&value).map_err(|_| value) } + fn convert_ref(value: impl Borrow) -> result::Result { + Self::convert(value.borrow().clone()).map_err(|_| ()) + } + /// Convert from `value` (of type `B`) into an equivalent value of type `A`, `Err` if not possible. + fn reverse(value: B) -> result::Result { Self::reverse_ref(&value).map_err(|_| value) } + fn reverse_ref(value: impl Borrow) -> result::Result { + Self::reverse(value.borrow().clone()).map_err(|_| ()) + } +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl Convert for Tuple { + fn convert(value: A) -> result::Result { + for_tuples!( #( + let value = match Tuple::convert(value) { + Ok(result) => return Ok(result), + Err(v) => v, + }; + )* ); + Err(value) + } + fn reverse(value: B) -> result::Result { + for_tuples!( #( + let value = match Tuple::reverse(value) { + Ok(result) => return Ok(result), + Err(v) => v, + }; + )* ); + Err(value) + } + fn convert_ref(value: impl Borrow) -> result::Result { + let value = value.borrow(); + for_tuples!( #( + match Tuple::convert_ref(value) { + Ok(result) => return Ok(result), + Err(_) => (), + } + )* ); + Err(()) + } + fn reverse_ref(value: impl Borrow) -> result::Result { + let value = value.borrow(); + for_tuples!( #( + match Tuple::reverse_ref(value.clone()) { + Ok(result) => return Ok(result), + Err(_) => (), + } + )* ); + Err(()) + } +} + +/// Simple pass-through which implements `BytesConversion` while not doing any conversion. +pub struct Identity; +impl Convert for Identity { + fn convert(value: T) -> result::Result { Ok(value) } + fn reverse(value: T) -> result::Result { Ok(value) } +} + +/// Implementation of `Convert` trait using `TryFrom`. +pub struct JustTry; +impl + Clone, Dest: TryFrom + Clone> Convert for JustTry { + fn convert(value: Source) -> result::Result { + Dest::try_from(value.clone()).map_err(|_| value) + } + fn reverse(value: Dest) -> result::Result { + Source::try_from(value.clone()).map_err(|_| value) + } +} + +use parity_scale_codec::{Encode, Decode}; +/// Implementation of `Convert<_, Vec>` using the parity scale codec. +pub struct Encoded; +impl Convert> for Encoded { + fn convert_ref(value: impl Borrow) -> result::Result, ()> { Ok(value.borrow().encode()) } + fn reverse_ref(bytes: impl Borrow>) -> result::Result { + T::decode(&mut &bytes.borrow()[..]).map_err(|_| ()) + } +} + +/// Implementation of `Convert, _>` using the parity scale codec. +pub struct Decoded; +impl Convert, T> for Decoded { + fn convert_ref(bytes: impl Borrow>) -> result::Result { + T::decode(&mut &bytes.borrow()[..]).map_err(|_| ()) + } + fn reverse_ref(value: impl Borrow) -> result::Result, ()> { Ok(value.borrow().encode()) } +} + +/// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be TryFrom/TryInto) +/// into a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will +/// typically be a `PalletInstance` junction. +pub struct AsPrefixedGeneralIndex(PhantomData<(Prefix, AssetId, ConvertAssetId)>); +impl< + Prefix: Get, + AssetId: Clone, + ConvertAssetId: Convert, +> Convert for AsPrefixedGeneralIndex { + fn convert_ref(id: impl Borrow) -> result::Result { + let prefix = Prefix::get(); + let id = id.borrow(); + if !prefix.iter().enumerate().all(|(index, item)| id.at(index) == Some(item)) { + return Err(()) + } + match id.at(prefix.len()) { + Some(Junction::GeneralIndex { id }) => ConvertAssetId::convert_ref(id), + _ => Err(()), + } + } + fn reverse_ref(what: impl Borrow) -> result::Result { + let mut location = Prefix::get(); + let id = ConvertAssetId::reverse_ref(what)?; + location.push(Junction::GeneralIndex { id }).map_err(|_| ())?; + Ok(location) + } +} + +pub trait MatchesFungibles { + fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl< + AssetId: Clone, + Balance: Clone, +> MatchesFungibles for Tuple { + fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> { + for_tuples!( #( + match Tuple::matches_fungibles(a) { o @ Ok(_) => return o, _ => () } + )* ); + Err(Error::AssetNotFound) + } +} + +pub struct ConvertedConcreteAssetId( + PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)> +); +impl< + AssetId: Clone, + Balance: Clone, + ConvertAssetId: Convert, + ConvertBalance: Convert, +> MatchesFungibles for + ConvertedConcreteAssetId +{ + fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> { + let (id, amount) = match a { + MultiAsset::ConcreteFungible { id, amount } => (id, amount), + _ => return Err(Error::AssetNotFound), + }; + let what = ConvertAssetId::convert_ref(id).map_err(|_| Error::AssetIdConversionFailed)?; + let amount = ConvertBalance::convert_ref(amount).map_err(|_| Error::AmountToBalanceConversionFailed)?; + Ok((what, amount)) + } +} + +pub struct ConvertedAbstractAssetId( + PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)> +); +impl< + AssetId: Clone, + Balance: Clone, + ConvertAssetId: Convert, AssetId>, + ConvertBalance: Convert, +> MatchesFungibles for + ConvertedAbstractAssetId +{ + fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> { + let (id, amount) = match a { + MultiAsset::AbstractFungible { id, amount } => (id, amount), + _ => return Err(Error::AssetNotFound), + }; + let what = ConvertAssetId::convert_ref(id).map_err(|_| Error::AssetIdConversionFailed)?; + let amount = ConvertBalance::convert_ref(amount).map_err(|_| Error::AmountToBalanceConversionFailed)?; + Ok((what, amount)) + } +} + +pub struct FungiblesAdapter( + PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)> +); + +impl< + Assets: Fungibles, + Matcher: MatchesFungibles, + AccountIdConverter: LocationConversion, + AccountId, // can't get away without it since Currency is generic over it. +> TransactAsset for FungiblesAdapter { + + fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result { + // Check we handle this asset. + let (asset_id, amount) = Matcher::matches_fungibles(what)?; + let who = AccountIdConverter::from_location(who) + .ok_or(Error::AccountIdConversionFailed)?; + Assets::deposit(asset_id, &who, amount) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + + fn withdraw_asset( + what: &MultiAsset, + who: &MultiLocation + ) -> result::Result { + // Check we handle this asset. + let (asset_id, amount) = Matcher::matches_fungibles(what)?; + let who = AccountIdConverter::from_location(who) + .ok_or(Error::AccountIdConversionFailed)?; + Assets::withdraw(asset_id, &who, amount) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + Ok(what.clone()) + } +} diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index ea54f81ce3..7813c4799d 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -28,7 +28,9 @@ pub use origin_conversion::{ }; mod currency_adapter; +mod fungibles_adapter; pub use currency_adapter::CurrencyAdapter; +pub use fungibles_adapter::FungiblesAdapter; use sp_std::marker::PhantomData; use xcm_executor::traits::InvertLocation; diff --git a/polkadot/xcm/xcm-executor/src/traits.rs b/polkadot/xcm/xcm-executor/src/traits.rs index a5fc2040f6..bae501731a 100644 --- a/polkadot/xcm/xcm-executor/src/traits.rs +++ b/polkadot/xcm/xcm-executor/src/traits.rs @@ -117,15 +117,22 @@ impl, B: TryFrom> MatchesFungible for IsAbstract< } } } +// TODO: impl for tuples impl, X: MatchesFungible, Y: MatchesFungible> MatchesFungible for (X, Y) { fn matches_fungible(a: &MultiAsset) -> Option { X::matches_fungible(a).or_else(|| Y::matches_fungible(a)) } } -pub trait LocationConversion { - fn from_location(location: &MultiLocation) -> Option; - fn try_into_location(who: AccountId) -> Result; +// TODO: change to use Convert trait. +/// Attempt to convert a location into some value of type `T`, or vice-versa. +pub trait LocationConversion { + /// Convert `location` into `Some` value of `T`, or `None` if not possible. + // TODO: consider returning Result instead. + fn from_location(location: &MultiLocation) -> Option; + /// Convert some value `value` into a `location`, `Err`oring with the original `value` if not possible. + // TODO: consider renaming `into_location` + fn try_into_location(value: T) -> Result; } #[impl_trait_for_tuples::impl_for_tuples(30)]