diff --git a/Cargo.lock b/Cargo.lock index ca05acc9f..bc752b729 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "Inflector" version = "0.11.4" @@ -252,6 +254,7 @@ dependencies = [ "xcm", "xcm-builder", "xcm-executor", + "xcm-support", "zenlink-protocol", "zenlink-protocol-runtime-api", ] @@ -5229,7 +5232,7 @@ dependencies = [ "sp-timestamp", "sp-transaction-pool", "substrate-prometheus-endpoint 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-prometheus-endpoint 0.9.0 (git+https://github.com/paritytech/substrate)", + "substrate-prometheus-endpoint 0.9.0 (git+https://github.com/paritytech/substrate?branch=master)", "zenlink-protocol-rpc", ] @@ -11332,7 +11335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59" dependencies = [ "cfg-if 0.1.10", - "rand 0.7.3", + "rand 0.3.23", "static_assertions", ] @@ -12180,6 +12183,23 @@ dependencies = [ "xcm", ] +[[package]] +name = "xcm-support" +version = "0.4.1-dev" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "node-primitives", + "orml-traits", + "parity-scale-codec", + "polkadot-parachain", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "yamux" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 9e4443131..7f73266ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "pallets/voucher", "pallets/charge-transaction-fee", "pallets/vsbond-auction", + "xcm-support", ] # The list of dependencies below (which can be both direct and indirect dependencies) are crates diff --git a/node/runtime/asgard/Cargo.toml b/node/runtime/asgard/Cargo.toml index f0f5c19d1..6554c7177 100644 --- a/node/runtime/asgard/Cargo.toml +++ b/node/runtime/asgard/Cargo.toml @@ -78,7 +78,7 @@ orml-currencies = { version = "0.4.1-dev", default-features = false } orml-tokens = { version = "0.4.1-dev", default-features = false } orml-traits = { version = "0.4.1-dev", default-features = false } #orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", default-features = false, branch = "master" } -#orml-xcm-support = { git = "https://github.com/open-web3-stack/open-runtime-module-library", default-features = false, branch = "master" } +xcm-support = { path = "../../../xcm-support", default-features = false } zenlink-protocol = { version = "0.4.0", default-features = false } zenlink-protocol-runtime-api = { version = "0.4.0", default-features = false } @@ -144,7 +144,7 @@ std = [ "orml-traits/std", "orml-tokens/std", # "orml-xtokens/std", -# "orml-xcm-support/std", + "xcm-support/std", "zenlink-protocol/std", "zenlink-protocol-runtime-api/std", ] diff --git a/node/runtime/asgard/src/lib.rs b/node/runtime/asgard/src/lib.rs index ced37cc1a..5a89c2706 100644 --- a/node/runtime/asgard/src/lib.rs +++ b/node/runtime/asgard/src/lib.rs @@ -68,7 +68,7 @@ use polkadot_parachain::primitives::Sibling; use xcm::v0::{BodyId, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId, Xcm}; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -80,6 +80,8 @@ use frame_system::{EnsureRoot, EnsureOneOf}; // orml imports use orml_currencies::BasicCurrencyAdapter; use orml_traits::MultiCurrency; +use xcm_support::{BifrostAssetMatcher, BifrostCurrencyAdapter, BifrostCurrencyIdConvert, BifrostXcmTransactFilter, BifrostFilteredAssets}; + // zenlink imports use zenlink_protocol::{ZenlinkMultiAssets, LocalAssetHandler, MultiAssetsHandler, make_x2_location, AssetId, AssetBalance, PairInfo}; @@ -400,7 +402,7 @@ pub type LocationToAccountId = ( // Sibling parachain origins convert to AccountId via the `ParaId::into`. SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, + AccountId32Aliases, ); /// Means for transacting assets on this chain. @@ -436,7 +438,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( ParentAsSuperuser, // Native signed account converter; this just converts an `AccountId32` origin into a normal // `Origin::Signed` origin of the same 32-byte value. - SignedAccountId32AsNative, + SignedAccountId32AsNative, // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. XcmPassthrough, ); @@ -457,10 +459,20 @@ match_type! { pub type Barrier = ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom>, - AllowUnpaidExecutionFrom, // ^^^ Parent & its unit plurality gets free execution + AllowUnpaidExecutionFrom, + BifrostXcmTransactFilter>, ); +pub type BifrostAssetTransactor = BifrostCurrencyAdapter< + Assets, + BifrostAssetMatcher, + AccountId, + LocationToAccountId, + CurrencyId, + BifrostCurrencyIdConvert, +>; + pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; @@ -468,8 +480,8 @@ impl Config for XcmConfig { // How to withdraw and deposit an asset. type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = NativeAsset; - type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of ROC + type IsReserve = BifrostFilteredAssets; + type IsTeleporter = BifrostFilteredAssets; type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; diff --git a/node/runtime/bifrost/src/lib.rs b/node/runtime/bifrost/src/lib.rs index 86fa8cbab..74c909454 100644 --- a/node/runtime/bifrost/src/lib.rs +++ b/node/runtime/bifrost/src/lib.rs @@ -59,7 +59,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; /// Constant values used within the runtime. pub mod constants; use constants::{currency::*, time::*}; -use node_primitives::Moment; +use node_primitives::{Moment}; // XCM imports use polkadot_parachain::primitives::Sibling; @@ -457,55 +457,11 @@ match_type! { }; } -/// Transparent XcmTransact Barrier for sybil demo. Polkadot will probably come up with a -/// better solution for this. Currently, they have not setup a barrier config for `XcmTransact` -pub struct AllowXcmTransactFrom(PhantomData); -impl> ShouldExecute for AllowXcmTransactFrom { - fn should_execute( - _origin: &MultiLocation, - _top_level: bool, - message: &Xcm, - _shallow_weight: Weight, - _weight_credit: &mut Weight, - ) -> Result<(), ()> { - match message { - Xcm::Transact { origin_type: _ , require_weight_at_most: _, call: _ } => Ok(()), - _ => Err(()) - } - } -} - pub type Barrier = ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom>, // ^^^ Parent & its unit plurality gets free execution AllowUnpaidExecutionFrom, - AllowXcmTransactFrom>, -); - -pub struct CrosschainConcreteAsset; -impl FilterAssetLocation for CrosschainConcreteAsset { - fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { - match asset { - MultiAsset::ConcreteFungible {..} => { - match origin { - Null | X1(Plurality { .. }) => true, - X1(AccountId32 { .. }) => true, - X1(Parent { .. }) => true, - X1(Parachain { .. }) => true, - X2(Parachain{..}, _ ) => true, - X2(Parent{..}, _ ) => true, - _ => false - } - }, - _ => false - } - } -} - -pub type ReserveAsset = ( - NativeAsset, - CrosschainConcreteAsset, ); pub struct XcmConfig; @@ -515,8 +471,8 @@ impl Config for XcmConfig { // How to withdraw and deposit an asset. type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = ReserveAsset; - type IsTeleporter = ReserveAsset; // <- should be enough to allow teleportation of ROC + type IsReserve = NativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of ROC type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; diff --git a/xcm-support/Cargo.toml b/xcm-support/Cargo.toml new file mode 100644 index 000000000..165057d4c --- /dev/null +++ b/xcm-support/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "xcm-support" +description = "Supporting module for XCM integration." +license = "Apache-2.0" +version = "0.4.1-dev" +authors = ["Bifrost Developers"] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.3", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.3", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.3", default-features = false } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.3" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "polkadot-v0.9.3" } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.3", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.3" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.3", default-features = false } +orml-traits = { version = "0.4.1-dev", default-features = false } +node-primitives = { default-features = false, path = "../node/primitives" } + +[features] +default = ["std"] +std = [ + "sp-std/std", + "sp-runtime/std", + "frame-support/std", + "xcm/std", + "xcm-executor/std", + "orml-traits/std", +] diff --git a/xcm-support/README.md b/xcm-support/README.md new file mode 100644 index 000000000..e432760a7 --- /dev/null +++ b/xcm-support/README.md @@ -0,0 +1,6 @@ +# XCM Support Module + +## Overview + +The XCM support module provides supporting traits, types and implementations, +to support cross-chain message(XCM) integration with ORML modules. diff --git a/xcm-support/src/lib.rs b/xcm-support/src/lib.rs new file mode 100644 index 000000000..bcd4d48d0 --- /dev/null +++ b/xcm-support/src/lib.rs @@ -0,0 +1,218 @@ +// Copyright 2019-2021 Liebi Technologies. +// This file is part of Bifrost. + +// Bifrost 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. + +// Bifrost 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 Bifrost. If not, see . + +//! # XCM Support Module. +//! +//! ## Overview +//! +//! The XCM support module provides supporting traits, types and +//! implementations, to support cross-chain message(XCM) integration with ORML +//! modules. + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unused_unit)] + +use frame_support::dispatch::{Weight}; +use sp_runtime::traits::{CheckedConversion, Convert}; +use sp_std::{convert::TryFrom, marker::PhantomData, prelude::*}; +use codec::FullCodec; +use sp_runtime::traits::{MaybeSerializeDeserialize}; +use sp_std::{ + cmp::{Eq, PartialEq}, + fmt::Debug +}; + +use xcm::v0::{NetworkId, Xcm, MultiLocation, MultiAsset, Junction}; +use xcm_executor::traits::{FilterAssetLocation, MatchesFungible, ShouldExecute, Convert as xcmConvert, TransactAsset}; +use frame_support::traits::{Contains}; +use node_primitives::{CurrencyId, TokenSymbol, AccountId}; +use polkadot_parachain::primitives::Sibling; + +use xcm::v0::{ + MultiLocation::{X1,X2,Null}, +}; +use xcm_builder::{ParentIsDefault, SiblingParachainConvertsVia, AccountId32Aliases, NativeAsset}; +use xcm::v0::prelude::{XcmResult, XcmError}; + +/// Bifrost Asset Matcher +pub struct BifrostAssetMatcher(PhantomData<(CurrencyId, CurrencyIdConvert)>); +impl MatchesFungible for BifrostAssetMatcher +where + CurrencyIdConvert: Convert>, + Amount: TryFrom, +{ + fn matches_fungible(a: &MultiAsset) -> Option { + if let MultiAsset::ConcreteFungible { id, amount } = a { + if CurrencyIdConvert::convert(id.clone()).is_some() { + return CheckedConversion::checked_from(*amount); + } + } + None + } +} + +/// Bifrost Location Convert +pub type BifrostLocationConvert = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsDefault, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Bifrost Currency Convert +pub struct BifrostCurrencyIdConvert; + +impl Convert> for BifrostCurrencyIdConvert { + fn convert(l: MultiLocation) -> Option { + #[allow(unused_variables)] { + let para_id: u32; + match l { + X2(Junction::Parent, Junction::Parachain(para_id)) => { + Some(CurrencyId::Token(TokenSymbol::KSM)) + } + _ => None, + } + } + } +} + +impl xcmConvert> for BifrostCurrencyIdConvert { + fn convert(a: MultiAsset) -> Result, MultiAsset> { + if let MultiAsset::ConcreteFungible { id, amount: _ } = a { + return Ok(>>::convert(id)); + } + Err(MultiAsset::None) + } +} + +/// Bifrost Xcm Transact Filter +pub struct BifrostXcmTransactFilter(PhantomData); +impl> ShouldExecute for BifrostXcmTransactFilter { + fn should_execute( + _origin: &MultiLocation, + _top_level: bool, + message: &Xcm, + _shallow_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + match message { + Xcm::Transact { origin_type: _ , require_weight_at_most: _, call: _ } => Ok(()), + _ => Err(()) + } + } +} + + +/// Bifrost Filtered Assets +pub struct BifrostFilterAsset; +impl FilterAssetLocation for BifrostFilterAsset { + fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { + match asset { + MultiAsset::ConcreteFungible {..} => { + match origin { + Null | X1(Junction::Plurality { .. }) => true, + X1(Junction::AccountId32 { .. }) => true, + X1(Junction::Parent { .. }) => true, + X1(Junction::Parachain { .. }) => true, + X2(Junction::Parachain{..}, _ ) => true, + X2(Junction::Parent{..}, _ ) => true, + _ => false + } + }, + _ => false + } + } +} + +pub type BifrostFilteredAssets = ( + NativeAsset, + BifrostFilterAsset, +); + +/// The `TransactAsset` implementation, to handle `MultiAsset` deposit/withdraw. +/// +/// If the asset is known, deposit/withdraw will be handled by `MultiCurrency`, +/// else by `UnknownAsset` if unknown. +pub struct BifrostCurrencyAdapter< + MultiCurrency, + Matcher, + AccountId, + AccountIdConvert, + CurrencyId, + CurrencyIdConvert, +>( + PhantomData<( + MultiCurrency, + Matcher, + AccountId, + AccountIdConvert, + CurrencyId, + CurrencyIdConvert, + )>, +); + +impl< + MultiCurrency: orml_traits::MultiCurrency, + Matcher: MatchesFungible, + AccountId: sp_std::fmt::Debug + sp_std::clone::Clone, + AccountIdConvert: xcmConvert, + CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug, + CurrencyIdConvert: xcmConvert>, +> TransactAsset +for BifrostCurrencyAdapter< + MultiCurrency, + Matcher, + AccountId, + AccountIdConvert, + CurrencyId, + CurrencyIdConvert, +> +{ + fn deposit_asset(asset: &MultiAsset, location: &MultiLocation) -> XcmResult { + match ( + AccountIdConvert::convert(location.clone()), + CurrencyIdConvert::convert(asset.clone()), + Matcher::matches_fungible(&asset), + ) { + // known asset + (who, currency_id, Some(amount)) => { + #[allow(unused_must_use)] { + MultiCurrency::deposit(currency_id.unwrap().unwrap(), &who.unwrap(), amount).map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + } + _ => Err(XcmError::AssetNotFound) + } + } + + fn withdraw_asset(asset: &MultiAsset, location: &MultiLocation) -> Result { + match ( + AccountIdConvert::convert(location.clone()), + CurrencyIdConvert::convert(asset.clone()), + Matcher::matches_fungible(&asset), + ) { + // known asset + (who, currency_id, Some(amount)) => { + #[allow(unused_must_use)] { + MultiCurrency::withdraw(currency_id.unwrap().unwrap(), &who.unwrap(), amount).map_err(|e| XcmError::FailedToTransactAsset(e.into())); + Ok(xcm_executor::Assets::new()) + } + } + _ => Err(XcmError::AssetNotFound) + } + } +}