From 51cb4d4f45ed41623153cbec0c6d45cfb459796b Mon Sep 17 00:00:00 2001 From: Shaun Wang Date: Tue, 6 Apr 2021 14:34:05 +1200 Subject: [PATCH 1/2] Xcm support implementations rework. --- xcm-support/src/currency_adapter.rs | 37 ++---- xcm-support/src/lib.rs | 98 +++------------ xcm-support/src/tests.rs | 188 +++++++++++----------------- 3 files changed, 100 insertions(+), 223 deletions(-) diff --git a/xcm-support/src/currency_adapter.rs b/xcm-support/src/currency_adapter.rs index 6b8cf0b8f..5050eadd1 100644 --- a/xcm-support/src/currency_adapter.rs +++ b/xcm-support/src/currency_adapter.rs @@ -2,6 +2,7 @@ use codec::FullCodec; use sp_runtime::traits::{MaybeSerializeDeserialize, SaturatedConversion}; use sp_std::{ cmp::{Eq, PartialEq}, + convert::{TryFrom, TryInto}, fmt::Debug, marker::PhantomData, prelude::*, @@ -11,7 +12,7 @@ use sp_std::{ use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result}; use xcm_executor::traits::{LocationConversion, MatchesFungible, TransactAsset}; -use crate::{CurrencyIdConversion, UnknownAsset as UnknownAssetT}; +use crate::UnknownAsset as UnknownAssetT; /// Asset transaction errors. enum Error { @@ -37,22 +38,13 @@ impl From for XcmError { /// /// If the asset is known, deposit/withdraw will be handled by `MultiCurrency`, /// else by `UnknownAsset` if unknown. -pub struct MultiCurrencyAdapter< - MultiCurrency, - UnknownAsset, - Matcher, - AccountIdConverter, - AccountId, - CurrencyIdConverter, - CurrencyId, ->( +pub struct MultiCurrencyAdapter( PhantomData<( MultiCurrency, UnknownAsset, Matcher, AccountIdConverter, AccountId, - CurrencyIdConverter, CurrencyId, )>, ); @@ -63,27 +55,18 @@ impl< Matcher: MatchesFungible, AccountIdConverter: LocationConversion, AccountId: sp_std::fmt::Debug, - CurrencyIdConverter: CurrencyIdConversion, - CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug, + CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug + TryFrom, > TransactAsset - for MultiCurrencyAdapter< - MultiCurrency, - UnknownAsset, - Matcher, - AccountIdConverter, - AccountId, - CurrencyIdConverter, - CurrencyId, - > + for MultiCurrencyAdapter { fn deposit_asset(asset: &MultiAsset, location: &MultiLocation) -> Result { match ( AccountIdConverter::from_location(location), - CurrencyIdConverter::from_asset(asset), + asset.clone().try_into(), Matcher::matches_fungible(&asset), ) { // known asset - (Some(who), Some(currency_id), Some(amount)) => { + (Some(who), Ok(currency_id), Some(amount)) => { MultiCurrency::deposit(currency_id, &who, amount).map_err(|e| XcmError::FailedToTransactAsset(e.into())) } // unknown asset @@ -95,8 +78,10 @@ impl< UnknownAsset::withdraw(asset, location).or_else(|_| { let who = AccountIdConverter::from_location(location) .ok_or_else(|| XcmError::from(Error::AccountIdConversionFailed))?; - let currency_id = CurrencyIdConverter::from_asset(asset) - .ok_or_else(|| XcmError::from(Error::CurrencyIdConversionFailed))?; + let currency_id = asset + .clone() + .try_into() + .map_err(|_| XcmError::from(Error::CurrencyIdConversionFailed))?; let amount: MultiCurrency::Balance = Matcher::matches_fungible(&asset) .ok_or_else(|| XcmError::from(Error::FailedToMatchFungible))? .saturated_into(); diff --git a/xcm-support/src/lib.rs b/xcm-support/src/lib.rs index 6963f0d5b..d8223f0d9 100644 --- a/xcm-support/src/lib.rs +++ b/xcm-support/src/lib.rs @@ -9,20 +9,12 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::unused_unit)] -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - traits::Get, -}; -use sp_runtime::traits::{CheckedConversion, Convert}; -use sp_std::{ - collections::btree_set::BTreeSet, - convert::{TryFrom, TryInto}, - marker::PhantomData, - prelude::*, -}; +use frame_support::dispatch::{DispatchError, DispatchResult}; +use sp_runtime::traits::CheckedConversion; +use sp_std::{convert::TryFrom, marker::PhantomData, prelude::*}; -use xcm::v0::{Junction, MultiAsset, MultiLocation, Xcm}; -use xcm_executor::traits::{FilterAssetLocation, MatchesFungible, NativeAsset}; +use xcm::v0::{MultiAsset, MultiLocation, Xcm}; +use xcm_executor::traits::{FilterAssetLocation, MatchesFungible}; use orml_traits::location::Reserve; @@ -37,61 +29,24 @@ pub trait XcmHandler { fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult; } -/// Convert `MultiAsset` to `CurrencyId`. -pub trait CurrencyIdConversion { - /// Get `CurrencyId` from `MultiAsset`. Returns `None` if conversion failed. - fn from_asset(asset: &MultiAsset) -> Option; -} - -/// A `MatchesFungible` implementation. It matches relay chain tokens or -/// parachain tokens that could be decoded from a general key. -pub struct IsConcreteWithGeneralKey( - PhantomData<(CurrencyId, FromRelayChainBalance)>, -); -impl MatchesFungible - for IsConcreteWithGeneralKey +/// A `MatchesFungible` implementation. It matches concrete fungible assets +/// whose `id` could be converted into `CurrencyId`. +pub struct IsNativeConcrete(PhantomData); +impl MatchesFungible for IsNativeConcrete where - CurrencyId: TryFrom>, - B: TryFrom, - FromRelayChainBalance: Convert, + CurrencyId: TryFrom, + Amount: TryFrom, { - fn matches_fungible(a: &MultiAsset) -> Option { + fn matches_fungible(a: &MultiAsset) -> Option { if let MultiAsset::ConcreteFungible { id, amount } = a { - if id == &MultiLocation::X1(Junction::Parent) { - // Convert relay chain decimals to local chain - let local_amount = FromRelayChainBalance::convert(*amount); - return CheckedConversion::checked_from(local_amount); - } - if let Some(Junction::GeneralKey(key)) = id.last() { - if TryInto::::try_into(key.clone()).is_ok() { - return CheckedConversion::checked_from(*amount); - } + if CurrencyId::try_from(id.clone()).is_ok() { + return CheckedConversion::checked_from(*amount); } } None } } -/// A `FilterAssetLocation` implementation. Filters native assets and ORML -/// tokens via provided general key to `MultiLocation` pairs. -pub struct NativePalletAssetOr(PhantomData); -impl, MultiLocation)>>> FilterAssetLocation for NativePalletAssetOr { - fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { - if NativeAsset::filter_asset_location(asset, origin) { - return true; - } - - // native orml-tokens with a general key - if let MultiAsset::ConcreteFungible { ref id, .. } = asset { - if let Some(Junction::GeneralKey(key)) = id.last() { - return Pairs::get().contains(&(key.clone(), origin.clone())); - } - } - - false - } -} - /// A `FilterAssetLocation` implementation. Filters multi native assets whose /// reserve is same with `origin`. pub struct MultiNativeAsset; @@ -106,31 +61,6 @@ impl FilterAssetLocation for MultiNativeAsset { } } -/// `CurrencyIdConversion` implementation. Converts relay chain tokens, or -/// parachain tokens that could be decoded from a general key. -pub struct CurrencyIdConverter( - PhantomData, - PhantomData, -); -impl CurrencyIdConversion - for CurrencyIdConverter -where - CurrencyId: TryFrom>, - RelayChainCurrencyId: Get, -{ - fn from_asset(asset: &MultiAsset) -> Option { - if let MultiAsset::ConcreteFungible { id: location, .. } = asset { - if location == &MultiLocation::X1(Junction::Parent) { - return Some(RelayChainCurrencyId::get()); - } - if let Some(Junction::GeneralKey(key)) = location.last() { - return CurrencyId::try_from(key.clone()).ok(); - } - } - None - } -} - /// Handlers unknown asset deposit and withdraw. pub trait UnknownAsset { /// Deposit unknown asset. diff --git a/xcm-support/src/tests.rs b/xcm-support/src/tests.rs index b0348befd..e8f680663 100644 --- a/xcm-support/src/tests.rs +++ b/xcm-support/src/tests.rs @@ -4,8 +4,7 @@ use super::*; -use frame_support::parameter_types; -use sp_runtime::traits::{Convert, Identity}; +use xcm::v0::{Junction::*, MultiAsset::*, MultiLocation::*}; #[derive(Debug, PartialEq, Eq)] pub enum TestCurrencyId { @@ -13,135 +12,98 @@ pub enum TestCurrencyId { TokenB, RelayChainToken, } -impl TryFrom> for TestCurrencyId { + +impl TryFrom for TestCurrencyId { type Error = (); - fn try_from(v: Vec) -> Result { - match v.as_slice() { - [1] => Ok(TestCurrencyId::TokenA), - [2] => Ok(TestCurrencyId::TokenB), - [3] => Ok(TestCurrencyId::RelayChainToken), + fn try_from(l: MultiLocation) -> Result { + use TestCurrencyId::*; + let token_a: Vec = "TokenA".into(); + let token_b: Vec = "TokenB".into(); + match l { + X1(Parent) => Ok(RelayChainToken), + X3(Parent, Parachain { id: 1 }, GeneralKey(k)) if k == token_a => Ok(TokenA), + X3(Parent, Parachain { id: 2 }, GeneralKey(k)) if k == token_b => Ok(TokenB), _ => Err(()), } } } -type IdentityMatch = IsConcreteWithGeneralKey; - -pub struct NativeToRelay; -impl Convert for NativeToRelay { - fn convert(val: u128) -> u128 { - // native is 13 - // relay is 12 - val / 10 - } -} - -type TenToOneMatch = IsConcreteWithGeneralKey; - -parameter_types! { - pub NativeOrmlTokens: BTreeSet<(Vec, MultiLocation)> = { - let mut t = BTreeSet::new(); - t.insert((vec![1], (Junction::Parent, Junction::Parachain { id: 1 }).into())); - t - }; - - pub const RelayChainCurrencyId: TestCurrencyId = TestCurrencyId::RelayChainToken; -} - -type AssetFilter = NativePalletAssetOr; - -type TestCurrencyIdConverter = CurrencyIdConverter; +type MatchesCurrencyId = IsNativeConcrete; #[test] -fn is_concrete_with_general_key_matches_relay_chain_token() { - let relay_chain_asset = MultiAsset::ConcreteFungible { - id: MultiLocation::X1(Junction::Parent), - amount: 10, - }; - assert_eq!(IdentityMatch::matches_fungible(&relay_chain_asset), Some(10)); - assert_eq!(TenToOneMatch::matches_fungible(&relay_chain_asset), Some(1)); -} - -#[test] -fn is_concrete_with_general_key_matches_parachain_token_with_general_key() { - let token_a = MultiAsset::ConcreteFungible { - id: MultiLocation::X3( - Junction::Parent, - Junction::Parachain { id: 1 }, - Junction::GeneralKey(vec![1]), - ), - amount: 10, - }; - let unknown_token = MultiAsset::ConcreteFungible { - id: MultiLocation::X3( - Junction::Parent, - Junction::Parachain { id: 1 }, - Junction::GeneralKey(vec![100]), - ), - amount: 10, - }; - assert_eq!(IdentityMatch::matches_fungible(&token_a), Some(10)); +fn is_native_concrete_matches_native_currencies() { + assert_eq!( + MatchesCurrencyId::matches_fungible(&ConcreteFungible { + id: X1(Parent), + amount: 100 + }), + Some(100), + ); assert_eq!( - >::matches_fungible(&unknown_token), - None, + MatchesCurrencyId::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())), + amount: 100 + }), + Some(100), + ); + assert_eq!( + MatchesCurrencyId::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 2 }, GeneralKey("TokenB".into())), + amount: 100 + }), + Some(100), ); } #[test] -fn native_pallet_asset_or_can_filter_native_asset() { - let token_a = MultiAsset::ConcreteFungible { - id: MultiLocation::X2(Junction::Parent, Junction::Parachain { id: 1 }), - amount: 10, - }; - assert!(AssetFilter::filter_asset_location( - &token_a, - &MultiLocation::X2(Junction::Parent, Junction::Parachain { id: 1 }), - )); +fn is_native_concrete_does_not_matches_non_native_currencies() { + assert!( + >::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 2 }, GeneralKey("TokenC".into())), + amount: 100 + }) + .is_none() + ); + assert!( + >::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenB".into())), + amount: 100 + }) + .is_none() + ); + assert!( + >::matches_fungible(&ConcreteFungible { + id: X1(GeneralKey("TokenB".into())), + amount: 100 + }) + .is_none() + ); } #[test] -fn native_pallet_asset_or_can_filter_orml_tokens() { - let token_a = MultiAsset::ConcreteFungible { - id: MultiLocation::X3( - Junction::Parent, - Junction::Parachain { id: 1 }, - Junction::GeneralKey(vec![1]), - ), - amount: 10, - }; - // origin is different from concrete fungible id, thus it's not native. - assert!(AssetFilter::filter_asset_location( - &token_a, - &MultiLocation::X2(Junction::Parent, Junction::Parachain { id: 1 }), +fn multi_native_asset() { + assert!(MultiNativeAsset::filter_asset_location( + &ConcreteFungible { + id: Parent.into(), + amount: 10, + }, + &Parent.into() + )); + assert!(MultiNativeAsset::filter_asset_location( + &ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())), + amount: 10, + }, + &X2(Parent, Parachain { id: 1 }), )); -} - -#[test] -fn currency_id_converts_relay_chain_token() { - let relay_chain_asset = MultiAsset::ConcreteFungible { - id: MultiLocation::X1(Junction::Parent), - amount: 10, - }; - assert_eq!( - TestCurrencyIdConverter::from_asset(&relay_chain_asset), - Some(TestCurrencyId::RelayChainToken), - ); -} - -#[test] -fn currency_id_converts_parachain_token() { - let token_a = MultiAsset::ConcreteFungible { - id: MultiLocation::X3( - Junction::Parent, - Junction::Parachain { id: 1 }, - Junction::GeneralKey(vec![1]), + MultiNativeAsset::filter_asset_location( + &ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())), + amount: 10, + }, + &X1(Parent), ), - amount: 10, - }; - - assert_eq!( - TestCurrencyIdConverter::from_asset(&token_a), - Some(TestCurrencyId::TokenA), + false ); } From 825b0eb12d901db4c6c11bee94a0ffea3e356cba Mon Sep 17 00:00:00 2001 From: Shaun Wang Date: Tue, 6 Apr 2021 14:52:29 +1200 Subject: [PATCH 2/2] Update xtokens mock. --- xtokens/src/mock.rs | 55 ++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/xtokens/src/mock.rs b/xtokens/src/mock.rs index c23f28436..823ae3629 100644 --- a/xtokens/src/mock.rs +++ b/xtokens/src/mock.rs @@ -5,15 +5,13 @@ use crate as orml_xtokens; use frame_support::parameter_types; use orml_traits::parameter_type_with_key; -use orml_xcm_support::{ - CurrencyIdConverter, IsConcreteWithGeneralKey, MultiCurrencyAdapter, MultiNativeAsset, XcmHandler as XcmHandlerT, -}; +use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset, XcmHandler as XcmHandlerT}; use polkadot_parachain::primitives::Sibling; use serde::{Deserialize, Serialize}; use sp_io::TestExternalities; -use sp_runtime::{traits::Identity, AccountId32}; +use sp_runtime::AccountId32; use sp_std::convert::TryFrom; -use xcm::v0::{Junction, NetworkId}; +use xcm::v0::{Junction, MultiLocation::*, NetworkId}; use xcm_builder::{ AccountId32Aliases, LocationInverter, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SovereignSignedViaLocation, @@ -35,18 +33,6 @@ pub enum CurrencyId { B, } -impl TryFrom> for CurrencyId { - type Error = (); - fn try_from(v: Vec) -> Result { - match v.as_slice() { - b"R" => Ok(CurrencyId::R), - b"A" => Ok(CurrencyId::A), - b"B" => Ok(CurrencyId::B), - _ => Err(()), - } - } -} - impl From for MultiLocation { fn from(id: CurrencyId) -> Self { match id { @@ -67,6 +53,32 @@ impl From for MultiLocation { } } +impl TryFrom for CurrencyId { + type Error = (); + fn try_from(l: MultiLocation) -> Result { + let a: Vec = "A".into(); + let b: Vec = "B".into(); + match l { + X1(Parent) => Ok(CurrencyId::R), + X3(Junction::Parent, Junction::Parachain { id: 1 }, Junction::GeneralKey(k)) if k == a => Ok(CurrencyId::A), + X3(Junction::Parent, Junction::Parachain { id: 2 }, Junction::GeneralKey(k)) if k == b => Ok(CurrencyId::B), + + _ => Err(()), + } + } +} + +impl TryFrom for CurrencyId { + type Error = (); + fn try_from(a: MultiAsset) -> Result { + if let MultiAsset::ConcreteFungible { id, amount: _ } = a { + Self::try_from(id) + } else { + Err(()) + } + } +} + pub type Balance = u128; pub type Amount = i128; @@ -98,10 +110,9 @@ decl_test_parachain! { pub type LocalAssetTransactor = MultiCurrencyAdapter< Tokens, (), - IsConcreteWithGeneralKey, + IsNativeConcrete, LocationConverter, AccountId, - CurrencyIdConverter, CurrencyId, >; @@ -202,10 +213,9 @@ decl_test_parachain! { pub type LocalAssetTransactor = MultiCurrencyAdapter< Tokens, (), - IsConcreteWithGeneralKey, + IsNativeConcrete, LocationConverter, AccountId, - CurrencyIdConverter, CurrencyId, >; @@ -306,10 +316,9 @@ decl_test_parachain! { pub type LocalAssetTransactor = MultiCurrencyAdapter< Tokens, (), - IsConcreteWithGeneralKey, + IsNativeConcrete, LocationConverter, AccountId, - CurrencyIdConverter, CurrencyId, >;