From 546e237e5b7e6465ae5cf2fac7a4090baede946f Mon Sep 17 00:00:00 2001 From: herryho Date: Sun, 12 Sep 2021 14:31:59 +0800 Subject: [PATCH 1/5] done without unit testing --- Cargo.lock | 1 + node/primitives/src/lib.rs | 7 ++ node/primitives/src/xcm.rs | 23 ++++++ pallets/flexible-fee/Cargo.toml | 1 + pallets/flexible-fee/src/lib.rs | 52 ++++++++++-- pallets/flexible-fee/src/misc_fees.rs | 114 ++++++++++++++++++++++++++ runtime/asgard/src/lib.rs | 53 +++++++++++- 7 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 pallets/flexible-fee/src/misc_fees.rs diff --git a/Cargo.lock b/Cargo.lock index abdd5573c3..2c68c4fd95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -694,6 +694,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "impl-trait-for-tuples", "node-primitives", "orml-currencies", "orml-tokens 0.4.1-dev (git+https://github.com/open-web3-stack/open-runtime-module-library?rev=8d5432c3458702a7df14b374bddde43a2a20aa43)", diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index 0fbabed31e..1e7710b135 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -20,6 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use sp_core::{Decode, Encode, RuntimeDebug}; use sp_runtime::{ generic, traits::{BlakeTwo256, IdentifyAccount, Verify}, @@ -125,3 +126,9 @@ pub type LeasePeriod = BlockNumber; /// Index used for the child trie pub type TrieIndex = u32; + +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord)] +pub enum ExtraFeeName { + SalpContribute, + NoExtraFee, +} diff --git a/node/primitives/src/xcm.rs b/node/primitives/src/xcm.rs index 3cdbc4a276..950f7c1355 100644 --- a/node/primitives/src/xcm.rs +++ b/node/primitives/src/xcm.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use core::ops::{Add, Mul}; + use codec::{Decode, Encode}; use sp_std::prelude::*; @@ -29,6 +31,12 @@ pub enum TransferOriginType { pub struct XcmBaseWeight(u64); +impl XcmBaseWeight { + pub fn new(x: u64) -> Self { + XcmBaseWeight(x) + } +} + impl From for XcmBaseWeight { fn from(u: u64) -> Self { XcmBaseWeight(u) @@ -41,6 +49,21 @@ impl From for u64 { } } +impl Add for XcmBaseWeight { + type Output = Self; + fn add(self, other: Self) -> Self::Output { + (self.0 + other.0).into() + } +} + +impl Mul for XcmBaseWeight { + type Output = Self; + + fn mul(self, rhs: u64) -> Self { + XcmBaseWeight::new(self.0 * rhs) + } +} + /// represent the transact type #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode)] pub enum ParachainTransactType { diff --git a/pallets/flexible-fee/Cargo.toml b/pallets/flexible-fee/Cargo.toml index a699b491d7..9decaa2901 100644 --- a/pallets/flexible-fee/Cargo.toml +++ b/pallets/flexible-fee/Cargo.toml @@ -16,6 +16,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v node-primitives = { path = "../../node/primitives", default-features = false } zenlink-protocol = { version = "*", default-features = false } orml-traits = { version = "0.4.1-dev", default-features = false } +impl-trait-for-tuples = "0.2.1" [dev-dependencies] orml-tokens = { version = "0.4.1-dev" } diff --git a/pallets/flexible-fee/src/lib.rs b/pallets/flexible-fee/src/lib.rs index 1a512f4c38..ab3bd69cea 100644 --- a/pallets/flexible-fee/src/lib.rs +++ b/pallets/flexible-fee/src/lib.rs @@ -31,7 +31,7 @@ use frame_support::{ }, }; use frame_system::pallet_prelude::*; -use node_primitives::{CurrencyId, TokenSymbol}; +use node_primitives::{CurrencyId, ExtraFeeName, TokenSymbol}; use orml_traits::MultiCurrency; pub use pallet::*; use pallet_transaction_payment::OnChargeTransaction; @@ -44,11 +44,15 @@ use sp_std::{vec, vec::Vec}; pub use weights::WeightInfo; use zenlink_protocol::{AssetBalance, AssetId, ExportZenlink}; -use crate::fee_dealer::FeeDealer; +use crate::{ + fee_dealer::FeeDealer, + misc_fees::{FeeDeductor, FeeGetter}, +}; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; pub mod fee_dealer; +pub mod misc_fees; mod mock; mod tests; mod weights; @@ -75,6 +79,16 @@ pub mod pallet { type OnUnbalanced: OnUnbalanced>; type DexOperator: ExportZenlink; type FeeDealer: FeeDealer, CurrencyIdOf>; + /// Filter if this transaction needs to be deducted extra fee besides basic transaction fee, + /// and get the name of the fee + type ExtraFeeMatcher: FeeGetter>; + /// In charge of deducting extra fees + type MiscFeeHandler: FeeDeductor< + Self::AccountId, + CurrencyIdOf, + PalletBalanceOf, + CallOf, + >; #[pallet::constant] type TreasuryAccount: Get; @@ -101,6 +115,7 @@ pub mod pallet { pub type PositiveImbalanceOf = <::Currency as Currency< ::AccountId, >>::PositiveImbalance; + pub type CallOf = ::Call; #[pallet::hooks] impl Hooks> for Pallet {} @@ -110,6 +125,7 @@ pub mod pallet { pub enum Event { FlexibleFeeExchanged(CurrencyIdOf, u128), // token and amount FixedRateFeeExchanged(CurrencyIdOf, PalletBalanceOf), + ExtraFeeDeducted(ExtraFeeName, CurrencyIdOf, PalletBalanceOf), } #[pallet::type_value] @@ -187,7 +203,7 @@ where /// Note: The `fee` already includes the `tip`. fn withdraw_fee( who: &T::AccountId, - _call: &T::Call, + call: &T::Call, _info: &DispatchInfoOf, fee: Self::Balance, tip: Self::Balance, @@ -207,13 +223,18 @@ where let (fee_sign, fee_amount) = T::FeeDealer::ensure_can_charge_fee(who, fee, withdraw_reason) .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; + let rs; // if the user has enough BNC for fee if fee_sign == false { - match T::Currency::withdraw(who, fee, withdraw_reason, ExistenceRequirement::AllowDeath) - { + rs = match T::Currency::withdraw( + who, + fee, + withdraw_reason, + ExistenceRequirement::AllowDeath, + ) { Ok(imbalance) => Ok(Some(imbalance)), Err(_msg) => Err(InvalidTransaction::Payment.into()), - } + }; // if the user donsn't enough BNC but has enough KSM } else { // deduct fee currency and increase native currency amount @@ -231,8 +252,25 @@ where // need to return 0 value of imbalance type // this value will be used to see post dispatch refund in BNC // if charge less than actual payment, no refund will be paid - Ok(Some(NegativeImbalanceOf::::zero())) + rs = Ok(Some(NegativeImbalanceOf::::zero())); } + + // See if the this Call needs to pay extra fee + let (fee_name, if_extra_fee) = T::ExtraFeeMatcher::get_fee_info(call.clone()); + if if_extra_fee { + // We define 77 as the error of extra fee deduction failure. + let (extra_fee_currency, extra_fee_amount) = + T::MiscFeeHandler::deduct_fee(who, &T::TreasuryAccount::get(), call).map_err( + |_| TransactionValidityError::Invalid(InvalidTransaction::Custom(77u8)), + )?; + Self::deposit_event(Event::ExtraFeeDeducted( + fee_name, + extra_fee_currency, + extra_fee_amount, + )); + } + + rs } /// Hand the fee and the tip over to the `[OnUnbalanced]` implementation. diff --git a/pallets/flexible-fee/src/misc_fees.rs b/pallets/flexible-fee/src/misc_fees.rs new file mode 100644 index 0000000000..02fb363194 --- /dev/null +++ b/pallets/flexible-fee/src/misc_fees.rs @@ -0,0 +1,114 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2021 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +// The swap pool algorithm implements Balancer protocol +// For more details, refer to https://balancer.finance/whitepaper/ + +use frame_support::{ + traits::Contains, + weights::{Weight, WeightToFeePolynomial}, +}; +use node_primitives::ExtraFeeName; + +use super::*; +use crate::Config; + +pub struct MiscFeeHandler( + PhantomData<(T, FeeCurrency, WeightToFee, WeightHolder, FeeFilter)>, +); + +impl + FeeDeductor, PalletBalanceOf, T::Call> + for MiscFeeHandler +where + FeeCurrency: Get>, + WeightToFee: WeightToFeePolynomial>, + WeightHolder: Get, + FeeFilter: Contains>, +{ + fn deduct_fee( + who: &T::AccountId, + receiver: &T::AccountId, + call: &T::Call, + ) -> Result<(CurrencyIdOf, PalletBalanceOf), DispatchError> { + // If this call matches a specific extra-fee call + if FeeFilter::contains(call) { + let total_fee = WeightToFee::calc(&WeightHolder::get()); + let fee_currency = FeeCurrency::get(); + + ::MultiCurrency::transfer(fee_currency, who, receiver, total_fee)?; + Ok((fee_currency, total_fee)) + } else { + Err(DispatchError::Other("Failed to deduct extra fee.")) + } + } +} + +pub trait FeeDeductor { + fn deduct_fee( + who: &AccountId, + receiver: &AccountId, + call: &Call, + ) -> Result<(CurrencyId, Balance), DispatchError>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl FeeDeductor + for Tuple +{ + fn deduct_fee( + who: &AccountId, + receiver: &AccountId, + call: &Call, + ) -> Result<(CurrencyId, Balance), DispatchError> { + for_tuples!( + #( + if let Ok(result) = Tuple::deduct_fee(who, receiver, call) { + return Ok(result); + } + )* + ); + + return Err(DispatchError::Other("Failed to deduct extra fee.")); + } +} + +pub trait NameGetter { + fn get_name(call: &Call) -> ExtraFeeName; +} + +pub trait FeeGetter { + fn get_fee_info(call: &Call) -> (ExtraFeeName, bool); +} + +pub struct ExtraFeeMatcher( + PhantomData<(T, FeeNameGetter, AggregateExtraFeeFilter)>, +); +impl FeeGetter> + for ExtraFeeMatcher +where + FeeNameGetter: NameGetter>, + AggregateExtraFeeFilter: Contains>, +{ + fn get_fee_info(call: &CallOf) -> (ExtraFeeName, bool) { + let fee_name = FeeNameGetter::get_name(call.clone()); + let if_extra_fee = AggregateExtraFeeFilter::contains(call); + + (fee_name, if_extra_fee) + } +} diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index 48dfa9579b..bcb78e7885 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -31,7 +31,7 @@ use core::convert::TryInto; // A few exports that help ease life for downstream crates. pub use frame_support::{ construct_runtime, match_type, parameter_types, - traits::{Everything, InstanceFilter, IsInVec, LockIdentifier, Randomness}, + traits::{Contains, Everything, InstanceFilter, IsInVec, LockIdentifier, Randomness}, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, DispatchClass, IdentityFee, Weight, @@ -71,7 +71,10 @@ use xcm_support::Get; /// Constant values used within the runtime. pub mod constants; -use bifrost_flexible_fee::fee_dealer::{FeeDealer, FixedCurrencyFeeRate}; +use bifrost_flexible_fee::{ + fee_dealer::{FeeDealer, FixedCurrencyFeeRate}, + misc_fees::{ExtraFeeMatcher, MiscFeeHandler, NameGetter}, +}; use bifrost_runtime_common::{ constants::parachains, create_x2_multilocation, @@ -89,7 +92,7 @@ use frame_support::{ traits::{EnsureOrigin, OnRuntimeUpgrade}, }; use node_primitives::{ - Amount, CurrencyId, Moment, Nonce, ParaId, ParachainDerivedProxyAccountType, + Amount, CurrencyId, ExtraFeeName, Moment, Nonce, ParaId, ParachainDerivedProxyAccountType, ParachainTransactProxyType, ParachainTransactType, TokenSymbol, TransferOriginType, XcmBaseWeight, }; @@ -952,8 +955,44 @@ impl orml_tokens::Config for Runtime { type WeightInfo = (); } +// Aggregate name getter to get fee names if the call needs to pay extra fees. +// If any call need to pay extra fees, it should be added as an item here. +// Used together with AggregateExtraFeeFilter below. +pub struct FeeNameGetter; +impl NameGetter for FeeNameGetter { + fn get_name(c: &Call) -> ExtraFeeName { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => ExtraFeeName::SalpContribute, + _ => ExtraFeeName::NoExtraFee, + } + } +} + +// Aggregate filter to filter if the call needs to pay extra fees +// If any call need to pay extra fees, it should be added as an item here. +pub struct AggregateExtraFeeFilter; +impl Contains for AggregateExtraFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + +pub struct ContributeFeeFilter; +impl Contains for ContributeFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + parameter_types! { pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); + pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT) + ContributionWeight::get() + (2^24 as u64).into(); } impl bifrost_flexible_fee::Config for Runtime { @@ -969,6 +1008,14 @@ impl bifrost_flexible_fee::Config for Runtime { type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; type OnUnbalanced = Treasury; type WeightInfo = weights::bifrost_flexible_fee::WeightInfo; + type ExtraFeeMatcher = ExtraFeeMatcher; + type MiscFeeHandler = MiscFeeHandler< + Runtime, + RelayCurrencyId, + WeightToFee, + SalpWeightHolder, + ContributeFeeFilter, + >; } parameter_types! { From e67bdff6bf6f91af0313321f44f23d7911addc0d Mon Sep 17 00:00:00 2001 From: herryho Date: Sun, 12 Sep 2021 15:48:32 +0800 Subject: [PATCH 2/5] add salp mock to flexible fee module --- Cargo.lock | 5 + node/primitives/src/traits.rs | 6 + pallets/flexible-fee/Cargo.toml | 5 + pallets/flexible-fee/src/lib.rs | 1 - pallets/flexible-fee/src/mock.rs | 257 +++++++++++++++++++++++++++++-- 5 files changed, 264 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c68c4fd95..9f51816370 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,7 @@ dependencies = [ name = "bifrost-flexible-fee" version = "0.8.0" dependencies = [ + "bifrost-salp", "cumulus-primitives-core", "frame-benchmarking", "frame-support", @@ -701,6 +702,7 @@ dependencies = [ "orml-traits 0.4.1-dev (git+https://github.com/open-web3-stack/open-runtime-module-library?rev=8d5432c3458702a7df14b374bddde43a2a20aa43)", "pallet-balances", "pallet-transaction-payment", + "pallet-xcm", "parity-scale-codec", "smallvec 1.6.1", "sp-arithmetic", @@ -708,6 +710,9 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "xcm", + "xcm-builder", + "xcm-support", "zenlink-protocol", ] diff --git a/node/primitives/src/traits.rs b/node/primitives/src/traits.rs index 141daf0c72..61980c9c59 100644 --- a/node/primitives/src/traits.rs +++ b/node/primitives/src/traits.rs @@ -168,3 +168,9 @@ pub trait MinterRewardExt { pub trait BancorHandler { fn add_token(currency_id: super::CurrencyId, amount: Balance) -> DispatchResult; } + +impl BancorHandler for () { + fn add_token(_currency_id: super::CurrencyId, _amount: Balance) -> DispatchResult { + unimplemented!() + } +} diff --git a/pallets/flexible-fee/Cargo.toml b/pallets/flexible-fee/Cargo.toml index 9decaa2901..4dc91bf87d 100644 --- a/pallets/flexible-fee/Cargo.toml +++ b/pallets/flexible-fee/Cargo.toml @@ -26,6 +26,11 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot- sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9" } smallvec = "1.4.1" cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "polkadot-v0.9.9" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.9" } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.9" } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.9"} +xcm-support = { path = "../../xcm-support"} +bifrost-salp = { path = "../salp" } [features] default = ["std"] diff --git a/pallets/flexible-fee/src/lib.rs b/pallets/flexible-fee/src/lib.rs index ab3bd69cea..7a1aece708 100644 --- a/pallets/flexible-fee/src/lib.rs +++ b/pallets/flexible-fee/src/lib.rs @@ -237,7 +237,6 @@ where }; // if the user donsn't enough BNC but has enough KSM } else { - // deduct fee currency and increase native currency amount // This withdraw operation allows death. So it will succeed given the remaining amount // less than the existential deposit. let fee_currency_id = T::AlternativeFeeCurrencyId::get(); diff --git a/pallets/flexible-fee/src/mock.rs b/pallets/flexible-fee/src/mock.rs index 6d404268d6..68a5c2fe0a 100644 --- a/pallets/flexible-fee/src/mock.rs +++ b/pallets/flexible-fee/src/mock.rs @@ -20,34 +20,52 @@ use std::convert::TryInto; +use bifrost_salp::WeightInfo as SalpWeightInfo; // pub use polkadot_parachain::primitives::Id; pub use cumulus_primitives_core::ParaId; #[cfg(feature = "runtime-benchmarks")] use frame_benchmarking::whitelisted_caller; use frame_support::{ parameter_types, - weights::{IdentityFee, WeightToFeeCoefficients, WeightToFeePolynomial}, + sp_runtime::{DispatchError, DispatchResult}, + sp_std::marker::PhantomData, + traits::{Contains, EnsureOrigin}, + weights::{IdentityFee, Weight, WeightToFeeCoefficients, WeightToFeePolynomial}, PalletId, }; use frame_system as system; -use node_primitives::{CurrencyId, TokenSymbol}; +use frame_system::RawOrigin; +use node_primitives::{ + CurrencyId, MessageId, ParachainTransactProxyType, ParachainTransactType, TokenSymbol, + TransferOriginType, XcmBaseWeight, +}; +use orml_traits::{MultiCurrency, XcmTransfer}; use smallvec::smallvec; +use sp_arithmetic::Percent; use sp_core::H256; use sp_runtime::{ + generic, testing::Header, traits::{BlakeTwo256, IdentityLookup, UniqueSaturatedInto}, - AccountId32, Perbill, + AccountId32, Perbill, SaturatedConversion, }; use sp_std::cell::RefCell; +use xcm::{ + opaque::v0::MultiAsset, + v0::{prelude::XcmError, Junction, MultiLocation}, + DoubleEncoded, +}; +use xcm_support::BifrostXcmExecutor; use zenlink_protocol::{LocalAssetHandler, ZenlinkMultiAssets}; use super::*; use crate as flexible_fee; // use node_primitives::Balance; use crate::fee_dealer::FixedCurrencyFeeRate; +use crate::misc_fees::{ExtraFeeMatcher, MiscFeeHandler, NameGetter}; pub type AccountId = AccountId32; -pub type BlockNumber = u64; +pub type BlockNumber = u32; pub type Amount = i128; pub const TREASURY_ACCOUNT: AccountId32 = AccountId32::new([9u8; 32]); @@ -70,11 +88,12 @@ frame_support::construct_runtime!( FlexibleFee: flexible_fee::{Pallet, Call, Storage,Event}, ZenlinkProtocol: zenlink_protocol::{Pallet, Call, Storage, Event}, Currencies: orml_currencies::{Pallet, Call, Storage, Event}, + Salp: bifrost_salp::{Pallet, Call, Storage, Event}, } ); parameter_types! { - pub const BlockHashCount: u64 = 250; + pub const BlockHashCount: u32 = 250; } impl system::Config for Test { @@ -83,14 +102,14 @@ impl system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockHashCount = BlockHashCount; type BlockLength = (); - type BlockNumber = u64; + type BlockNumber = u32; type BlockWeights = (); type Call = Call; type DbWeight = (); type Event = Event; type Hash = H256; type Hashing = BlakeTwo256; - type Header = Header; + type Header = generic::Header; type Index = u64; // needs to be u128 against u64, otherwise the account address will be half cut. type Lookup = IdentityLookup; @@ -105,12 +124,12 @@ impl system::Config for Test { } thread_local! { - static WEIGHT_TO_FEE: RefCell = RefCell::new(1); + static WEIGHT_TO_FEE: RefCell = RefCell::new(1); } pub struct WeightToFee; impl WeightToFeePolynomial for WeightToFee { - type Balance = u128; + type Balance = u64; fn polynomial() -> WeightToFeeCoefficients { smallvec![frame_support::weights::WeightToFeeCoefficient { @@ -171,11 +190,47 @@ impl orml_tokens::Config for Test { type WeightInfo = (); } +// Aggregate name getter to get fee names if the call needs to pay extra fees. +// If any call need to pay extra fees, it should be added as an item here. +// Used together with AggregateExtraFeeFilter below. +pub struct FeeNameGetter; +impl NameGetter for FeeNameGetter { + fn get_name(c: &Call) -> ExtraFeeName { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => ExtraFeeName::SalpContribute, + _ => ExtraFeeName::NoExtraFee, + } + } +} + +// Aggregate filter to filter if the call needs to pay extra fees +// If any call need to pay extra fees, it should be added as an item here. +pub struct AggregateExtraFeeFilter; +impl Contains for AggregateExtraFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + +pub struct ContributeFeeFilter; +impl Contains for ContributeFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + parameter_types! { pub const NativeCurrencyId: CurrencyId = CurrencyId::Native(TokenSymbol::ASG); pub const AlternativeFeeCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); pub const TreasuryAccount: AccountId32 = TREASURY_ACCOUNT; + pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT + ContributionWeight::get()) + (2^24 as u64).into(); } impl crate::Config for Test { @@ -191,6 +246,14 @@ impl crate::Config for Test { type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; type OnUnbalanced = (); type WeightInfo = (); + type ExtraFeeMatcher = ExtraFeeMatcher; + type MiscFeeHandler = MiscFeeHandler< + Test, + AlternativeFeeCurrencyId, + WeightToFee, + SalpWeightHolder, + ContributeFeeFilter, + >; } parameter_types! { @@ -339,3 +402,179 @@ impl ExtBuilder { pub(crate) fn new_test_ext() -> sp_io::TestExternalities { system::GenesisConfig::default().build_storage::().unwrap().into() } + +//************** Salp mock start ***************** + +// To control the result returned by `MockXcmExecutor` +pub(crate) static mut MOCK_XCM_RESULT: (bool, bool) = (true, true); + +// Mock XcmExecutor +pub struct MockXcmExecutor; + +impl BifrostXcmExecutor for MockXcmExecutor { + fn transact_weight(_: u64, _: u32) -> u64 { + return 0; + } + + fn transact_id(_data: &[u8]) -> MessageId { + return [0; 32]; + } + + fn ump_transact( + _origin: MultiLocation, + _call: DoubleEncoded<()>, + _weight: u64, + _relayer: bool, + _nonce: u32, + ) -> Result<[u8; 32], XcmError> { + let result = unsafe { MOCK_XCM_RESULT.0 }; + + match result { + true => Ok([0; 32]), + false => Err(XcmError::Undefined), + } + } + + fn ump_transacts( + _origin: MultiLocation, + _call: Vec>, + _weight: u64, + _relayer: bool, + ) -> Result { + let result = unsafe { MOCK_XCM_RESULT.0 }; + + match result { + true => Ok([0; 32]), + false => Err(xcm::v0::Error::Undefined), + } + } + + fn ump_transfer_asset( + _origin: MultiLocation, + _dest: MultiLocation, + _amount: u128, + _relay: bool, + _nonce: u32, + ) -> Result { + let result = unsafe { MOCK_XCM_RESULT.1 }; + + match result { + true => Ok([0; 32]), + false => Err(xcm::v0::Error::Undefined), + } + } +} + +pub const ALICE: AccountId = AccountId::new([0u8; 32]); + +parameter_types! { + pub const MinContribution: Balance = 10; + pub const BifrostCrowdloanId: PalletId = PalletId(*b"bf/salp#"); + pub const RemoveKeysLimit: u32 = 50; + pub const SlotLength: BlockNumber = 8u32 as BlockNumber; + pub const LeasePeriod: BlockNumber = 8u32 as BlockNumber; + pub const VSBondValidPeriod: BlockNumber = 8u32 as BlockNumber; + pub const ReleaseCycle: BlockNumber = 8u32 as BlockNumber; + pub const ReleaseRatio: Percent = Percent::from_percent(50); + pub const XcmTransferOrigin: TransferOriginType = TransferOriginType::FromRelayChain; + pub BaseXcmWeight:u64 = 1_000_000_000 as u64; + pub ContributionWeight:u64 = 1_000_000_000 as u64; + pub AddProxyWeight:u64 = 1_000_000_000 as u64; + pub PrimaryAccount: AccountId = ALICE; + pub ConfirmMuitiSigAccount: AccountId = ALICE; + pub RelaychainSovereignSubAccount: MultiLocation = MultiLocation::X1(Junction::Parent); + pub SalpTransactProxyType: ParachainTransactProxyType = ParachainTransactProxyType::Derived; + pub SalpTransactType: ParachainTransactType = ParachainTransactType::Xcm; + pub const RelayCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); +} + +pub const XCM_WEIGHT: u64 = 1_000_000_000; + +pub struct EnsureConfirmAsMultiSig; +impl EnsureOrigin for EnsureConfirmAsMultiSig { + type Success = AccountId; + + fn try_origin(o: Origin) -> Result { + Into::, Origin>>::into(o).and_then(|o| match o { + RawOrigin::Signed(who) => Ok(who), + RawOrigin::Root => Ok(Default::default()), + r => Err(Origin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> Origin { + Origin::from(RawOrigin::Signed(ALICE)) + } +} + +pub struct MockXTokens; + +impl XcmTransfer for MockXTokens { + fn transfer( + _who: AccountId, + _currency_id: CurrencyId, + _amount: Balance, + _dest: MultiLocation, + _dest_weight: Weight, + ) -> DispatchResult { + Ok(()) + } + + fn transfer_multi_asset( + _who: AccountId, + _asset: MultiAsset, + _dest: MultiLocation, + _dest_weight: Weight, + ) -> DispatchResult { + Ok(()) + } +} + +pub struct MockSalpWeightInfo; +impl SalpWeightInfo for MockSalpWeightInfo { + fn contribute() -> Weight { + 0 + } + + fn unlock() -> Weight { + 0 + } + + fn redeem() -> Weight { + 0 + } + + fn refund() -> Weight { + 0 + } +} + +impl bifrost_salp::Config for Test { + type BancorPool = (); + type BifrostXcmExecutor = MockXcmExecutor; + type Event = Event; + type LeasePeriod = LeasePeriod; + type MinContribution = MinContribution; + type MultiCurrency = Tokens; + type PalletId = BifrostCrowdloanId; + type RelayChainToken = RelayCurrencyId; + type ReleaseCycle = ReleaseCycle; + type ReleaseRatio = ReleaseRatio; + type RemoveKeysLimit = RemoveKeysLimit; + type SlotLength = SlotLength; + type VSBondValidPeriod = VSBondValidPeriod; + type XcmTransferOrigin = XcmTransferOrigin; + type WeightInfo = MockSalpWeightInfo; + type SelfParaId = SelfParaId; + type BaseXcmWeight = BaseXcmWeight; + type ContributionWeight = ContributionWeight; + type EnsureConfirmAsMultiSig = EnsureConfirmAsMultiSig; + type AddProxyWeight = AddProxyWeight; + type XcmTransfer = MockXTokens; + type SovereignSubAccountLocation = RelaychainSovereignSubAccount; + type TransactProxyType = SalpTransactProxyType; + type TransactType = SalpTransactType; +} + +//************** Salp mock end ***************** From ef45f617b3e0e3b0e7faca7204d79bfc32c545f6 Mon Sep 17 00:00:00 2001 From: herryho Date: Sun, 12 Sep 2021 17:16:53 +0800 Subject: [PATCH 3/5] charge salp fee in flexible fee module done with unit testing --- pallets/flexible-fee/src/mock.rs | 22 +++++++++++------- pallets/flexible-fee/src/tests.rs | 38 +++++++++++++++++++++++++++++++ runtime/asgard/src/lib.rs | 2 +- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/pallets/flexible-fee/src/mock.rs b/pallets/flexible-fee/src/mock.rs index 68a5c2fe0a..c69259d710 100644 --- a/pallets/flexible-fee/src/mock.rs +++ b/pallets/flexible-fee/src/mock.rs @@ -30,7 +30,10 @@ use frame_support::{ sp_runtime::{DispatchError, DispatchResult}, sp_std::marker::PhantomData, traits::{Contains, EnsureOrigin}, - weights::{IdentityFee, Weight, WeightToFeeCoefficients, WeightToFeePolynomial}, + weights::{ + constants::ExtrinsicBaseWeight, IdentityFee, Weight, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, PalletId, }; use frame_system as system; @@ -61,8 +64,10 @@ use zenlink_protocol::{LocalAssetHandler, ZenlinkMultiAssets}; use super::*; use crate as flexible_fee; // use node_primitives::Balance; -use crate::fee_dealer::FixedCurrencyFeeRate; -use crate::misc_fees::{ExtraFeeMatcher, MiscFeeHandler, NameGetter}; +use crate::{ + fee_dealer::FixedCurrencyFeeRate, + misc_fees::{ExtraFeeMatcher, MiscFeeHandler, NameGetter}, +}; pub type AccountId = AccountId32; pub type BlockNumber = u32; @@ -129,14 +134,15 @@ thread_local! { pub struct WeightToFee; impl WeightToFeePolynomial for WeightToFee { - type Balance = u64; - + type Balance = Balance; fn polynomial() -> WeightToFeeCoefficients { + let p = 1_000_000_000_000 / 30_000; // RELAY_CENTS + let q = 10 * Balance::from(ExtrinsicBaseWeight::get()); // ExtrinsicBaseWeight = 125 * 1_000_000_000_000 / 1000 / 1000 = 125_000_000 smallvec![frame_support::weights::WeightToFeeCoefficient { degree: 1, - coeff_frac: Perbill::zero(), - coeff_integer: WEIGHT_TO_FEE.with(|v| *v.borrow()), negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, }] } } @@ -230,7 +236,7 @@ parameter_types! { pub const AlternativeFeeCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); pub const TreasuryAccount: AccountId32 = TREASURY_ACCOUNT; - pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT + ContributionWeight::get()) + (2^24 as u64).into(); + pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT + ContributionWeight::get()) + u64::pow(2, 24).into(); } impl crate::Config for Test { diff --git a/pallets/flexible-fee/src/tests.rs b/pallets/flexible-fee/src/tests.rs index 2889b07641..332b2d7b1b 100644 --- a/pallets/flexible-fee/src/tests.rs +++ b/pallets/flexible-fee/src/tests.rs @@ -436,3 +436,41 @@ fn correct_and_deposit_fee_should_work_v2() { ); }); } + +#[test] +fn deduct_salp_fee_should_work() { + new_test_ext().execute_with(|| { + // deposit some money for Charlie + assert_ok!(Currencies::deposit(CURRENCY_ID_0, &CHARLIE, 200)); // Native token + assert_ok!(Currencies::deposit(CURRENCY_ID_4, &CHARLIE, 200_000_000)); // Token KSM + + // prepare call variable + let para_id = 2001; + let value = 1_000_000_000_000; + + let call = Call::Salp(bifrost_salp::Call::contribute(para_id, value)); + + // prepare info variable + let extra = (); + let xt = TestXt::new(call.clone(), Some((0u64, extra))); + let info = xt.get_dispatch_info(); + + // 99 inclusion fee and a tip of 8 + assert_ok!(FlexibleFee::withdraw_fee(&CHARLIE, &call, &info, 107, 8)); + + assert_eq!(::Currency::free_balance(&CHARLIE), 93); + // fee is: 133780717 + assert_eq!( + ::MultiCurrency::free_balance(CURRENCY_ID_4, &CHARLIE), + 66219283 + ); + // treasury account has the fee + assert_eq!( + ::MultiCurrency::free_balance( + CURRENCY_ID_4, + &::TreasuryAccount::get() + ), + 133780717 + ); + }); +} diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index bcb78e7885..d82e69effd 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -992,7 +992,7 @@ impl Contains for ContributeFeeFilter { parameter_types! { pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); - pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT) + ContributionWeight::get() + (2^24 as u64).into(); + pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT) + ContributionWeight::get() + u64::pow(2, 24).into(); } impl bifrost_flexible_fee::Config for Runtime { From 5254eeb661af0f0d6aceb72e10990af8c7589513 Mon Sep 17 00:00:00 2001 From: herryho Date: Sun, 12 Sep 2021 17:30:28 +0800 Subject: [PATCH 4/5] add salp fee to bifrost runtime --- runtime/bifrost/src/lib.rs | 52 ++++++++++++++++++++++++++++++++++++-- runtime/dev/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/runtime/bifrost/src/lib.rs b/runtime/bifrost/src/lib.rs index 6762b67383..421398603b 100644 --- a/runtime/bifrost/src/lib.rs +++ b/runtime/bifrost/src/lib.rs @@ -62,7 +62,10 @@ use static_assertions::const_assert; /// Constant values used within the runtime. pub mod constants; -use bifrost_flexible_fee::fee_dealer::{FeeDealer, FixedCurrencyFeeRate}; +use bifrost_flexible_fee::{ + fee_dealer::{FeeDealer, FixedCurrencyFeeRate}, + misc_fees::{ExtraFeeMatcher, MiscFeeHandler, NameGetter}, +}; use bifrost_runtime_common::{ constants::parachains, create_x2_multilocation, @@ -83,7 +86,7 @@ use frame_support::{ use frame_system::{EnsureOneOf, EnsureRoot, RawOrigin}; use hex_literal::hex; use node_primitives::{ - Amount, CurrencyId, Moment, Nonce, ParaId, ParachainDerivedProxyAccountType, + Amount, CurrencyId, ExtraFeeName, Moment, Nonce, ParaId, ParachainDerivedProxyAccountType, ParachainTransactProxyType, ParachainTransactType, TokenSymbol, TransferOriginType, XcmBaseWeight, }; @@ -907,8 +910,45 @@ impl orml_xcm::Config for Runtime { // orml runtime end // Bifrost modules start + +// Aggregate name getter to get fee names if the call needs to pay extra fees. +// If any call need to pay extra fees, it should be added as an item here. +// Used together with AggregateExtraFeeFilter below. +pub struct FeeNameGetter; +impl NameGetter for FeeNameGetter { + fn get_name(c: &Call) -> ExtraFeeName { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => ExtraFeeName::SalpContribute, + _ => ExtraFeeName::NoExtraFee, + } + } +} + +// Aggregate filter to filter if the call needs to pay extra fees +// If any call need to pay extra fees, it should be added as an item here. +pub struct AggregateExtraFeeFilter; +impl Contains for AggregateExtraFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + +pub struct ContributeFeeFilter; +impl Contains for ContributeFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + parameter_types! { pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); + pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT) + ContributionWeight::get() + u64::pow(2, 24).into(); } impl bifrost_flexible_fee::Config for Runtime { @@ -924,6 +964,14 @@ impl bifrost_flexible_fee::Config for Runtime { type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; type OnUnbalanced = Treasury; type WeightInfo = weights::bifrost_flexible_fee::WeightInfo; + type ExtraFeeMatcher = ExtraFeeMatcher; + type MiscFeeHandler = MiscFeeHandler< + Runtime, + RelayCurrencyId, + WeightToFee, + SalpWeightHolder, + ContributeFeeFilter, + >; } pub struct EnsureConfirmAsMultiSig; diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs index ae70e6177b..2e0c37d63c 100644 --- a/runtime/dev/src/lib.rs +++ b/runtime/dev/src/lib.rs @@ -79,7 +79,10 @@ use xcm_support::Get; /// Constant values used within the runtime. pub mod constants; -use bifrost_flexible_fee::fee_dealer::{FeeDealer, FixedCurrencyFeeRate}; +use bifrost_flexible_fee::{ + fee_dealer::{FeeDealer, FixedCurrencyFeeRate}, + misc_fees::{ExtraFeeMatcher, MiscFeeHandler, NameGetter}, +}; use bifrost_runtime_common::{ constants::parachains, create_x2_multilocation, @@ -98,7 +101,7 @@ use frame_support::{ traits::{EnsureOrigin, KeyOwnerProofSystem}, }; use node_primitives::{ - Amount, CurrencyId, Moment, Nonce, ParachainDerivedProxyAccountType, + Amount, CurrencyId, ExtraFeeName, Moment, Nonce, ParachainDerivedProxyAccountType, ParachainTransactProxyType, ParachainTransactType, TokenSymbol, TransferOriginType, XcmBaseWeight, }; @@ -922,8 +925,44 @@ impl orml_tokens::Config for Runtime { type WeightInfo = (); } +// Aggregate name getter to get fee names if the call needs to pay extra fees. +// If any call need to pay extra fees, it should be added as an item here. +// Used together with AggregateExtraFeeFilter below. +pub struct FeeNameGetter; +impl NameGetter for FeeNameGetter { + fn get_name(c: &Call) -> ExtraFeeName { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => ExtraFeeName::SalpContribute, + _ => ExtraFeeName::NoExtraFee, + } + } +} + +// Aggregate filter to filter if the call needs to pay extra fees +// If any call need to pay extra fees, it should be added as an item here. +pub struct AggregateExtraFeeFilter; +impl Contains for AggregateExtraFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + +pub struct ContributeFeeFilter; +impl Contains for ContributeFeeFilter { + fn contains(c: &Call) -> bool { + match *c { + Call::Salp(bifrost_salp::Call::contribute(..)) => true, + _ => false, + } + } +} + parameter_types! { pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); + pub SalpWeightHolder: XcmBaseWeight = XcmBaseWeight::from(4 * XCM_WEIGHT) + ContributionWeight::get() + u64::pow(2, 24).into(); } impl bifrost_flexible_fee::Config for Runtime { @@ -939,6 +978,14 @@ impl bifrost_flexible_fee::Config for Runtime { type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; type OnUnbalanced = Treasury; type WeightInfo = weights::bifrost_flexible_fee::WeightInfo; + type ExtraFeeMatcher = ExtraFeeMatcher; + type MiscFeeHandler = MiscFeeHandler< + Runtime, + RelayCurrencyId, + WeightToFee, + SalpWeightHolder, + ContributeFeeFilter, + >; } parameter_types! { From 4b35a2804ac55daa6c16030bb4d97ae7231ed02b Mon Sep 17 00:00:00 2001 From: herryho Date: Sun, 12 Sep 2021 17:53:45 +0800 Subject: [PATCH 5/5] fixt-tests --- pallets/flexible-fee/src/mock.rs | 23 ++----------------- pallets/salp/src/lib.rs | 39 +++++++++++++++++++++++++------- runtime/bifrost/src/lib.rs | 4 +--- runtime/dev/src/lib.rs | 2 +- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/pallets/flexible-fee/src/mock.rs b/pallets/flexible-fee/src/mock.rs index c69259d710..ce46024aa5 100644 --- a/pallets/flexible-fee/src/mock.rs +++ b/pallets/flexible-fee/src/mock.rs @@ -20,7 +20,6 @@ use std::convert::TryInto; -use bifrost_salp::WeightInfo as SalpWeightInfo; // pub use polkadot_parachain::primitives::Id; pub use cumulus_primitives_core::ParaId; #[cfg(feature = "runtime-benchmarks")] @@ -537,25 +536,6 @@ impl XcmTransfer for MockXTokens { } } -pub struct MockSalpWeightInfo; -impl SalpWeightInfo for MockSalpWeightInfo { - fn contribute() -> Weight { - 0 - } - - fn unlock() -> Weight { - 0 - } - - fn redeem() -> Weight { - 0 - } - - fn refund() -> Weight { - 0 - } -} - impl bifrost_salp::Config for Test { type BancorPool = (); type BifrostXcmExecutor = MockXcmExecutor; @@ -571,11 +551,12 @@ impl bifrost_salp::Config for Test { type SlotLength = SlotLength; type VSBondValidPeriod = VSBondValidPeriod; type XcmTransferOrigin = XcmTransferOrigin; - type WeightInfo = MockSalpWeightInfo; + type WeightInfo = (); type SelfParaId = SelfParaId; type BaseXcmWeight = BaseXcmWeight; type ContributionWeight = ContributionWeight; type EnsureConfirmAsMultiSig = EnsureConfirmAsMultiSig; + type EnsureConfirmAsGovernance = EnsureConfirmAsMultiSig; type AddProxyWeight = AddProxyWeight; type XcmTransfer = MockXTokens; type SovereignSubAccountLocation = RelaychainSovereignSubAccount; diff --git a/pallets/salp/src/lib.rs b/pallets/salp/src/lib.rs index b4849fb4b0..0b9948e9e6 100644 --- a/pallets/salp/src/lib.rs +++ b/pallets/salp/src/lib.rs @@ -39,14 +39,6 @@ use orml_traits::MultiCurrency; pub use pallet::*; use sp_std::convert::TryFrom; -pub trait WeightInfo { - fn contribute() -> Weight; - fn unlock() -> Weight; - fn batch_unlock(k: u32) -> Weight; - fn refund() -> Weight; - fn redeem() -> Weight; -} - #[allow(type_alias_bounds)] pub type AccountIdOf = ::AccountId; @@ -1141,3 +1133,34 @@ pub mod pallet { } } } + +pub trait WeightInfo { + fn contribute() -> Weight; + fn unlock() -> Weight; + fn batch_unlock(k: u32) -> Weight; + fn refund() -> Weight; + fn redeem() -> Weight; +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn contribute() -> Weight { + 50_000_000 as Weight + } + + fn unlock() -> Weight { + 50_000_000 as Weight + } + + fn batch_unlock(_k: u32) -> Weight { + 50_000_000 as Weight + } + + fn refund() -> Weight { + 50_000_000 as Weight + } + + fn redeem() -> Weight { + 50_000_000 as Weight + } +} diff --git a/runtime/bifrost/src/lib.rs b/runtime/bifrost/src/lib.rs index 421398603b..17250525ce 100644 --- a/runtime/bifrost/src/lib.rs +++ b/runtime/bifrost/src/lib.rs @@ -99,9 +99,7 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use sp_arithmetic::Percent; use sp_runtime::traits::ConvertInto; -use xcm::v0::{ - BodyId, Junction, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId, -}; +use xcm::v0::{BodyId, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId}; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter, diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs index 2e0c37d63c..ce509c6f69 100644 --- a/runtime/dev/src/lib.rs +++ b/runtime/dev/src/lib.rs @@ -31,7 +31,7 @@ use core::convert::TryInto; // A few exports that help ease life for downstream crates. pub use frame_support::{ construct_runtime, match_type, parameter_types, - traits::{Everything, InstanceFilter, IsInVec, LockIdentifier, Randomness}, + traits::{Contains, Everything, InstanceFilter, IsInVec, LockIdentifier, Randomness}, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, DispatchClass, IdentityFee, Weight,