diff --git a/Cargo.lock b/Cargo.lock index 74a72162a2..518ac087c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,6 +753,8 @@ dependencies = [ name = "bifrost-runtime" version = "0.8.0" dependencies = [ + "bifrost-flexible-fee", + "bifrost-flexible-fee-rpc-runtime-api", "bifrost-runtime-common", "bifrost-vesting", "cumulus-pallet-aura-ext", @@ -11984,7 +11986,7 @@ dependencies = [ [[package]] name = "zenlink-protocol" version = "0.4.2" -source = "git+ssh://git@github.com/bifrost-finance/Zenlink-DEX-Module?branch=polkadot-v0.9.8#8e9e87081b579e950a544f33cd380b38772370a1" +source = "git+ssh://git@github.com/bifrost-finance/Zenlink-DEX-Module?branch=polkadot-v0.9.8#7a6fb92ce19bdb46d10bf12abb1f3cbc8ab50932" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -12004,7 +12006,7 @@ dependencies = [ [[package]] name = "zenlink-protocol-rpc" version = "0.4.2" -source = "git+ssh://git@github.com/bifrost-finance/Zenlink-DEX-Module?branch=polkadot-v0.9.8#8e9e87081b579e950a544f33cd380b38772370a1" +source = "git+ssh://git@github.com/bifrost-finance/Zenlink-DEX-Module?branch=polkadot-v0.9.8#7a6fb92ce19bdb46d10bf12abb1f3cbc8ab50932" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -12021,7 +12023,7 @@ dependencies = [ [[package]] name = "zenlink-protocol-runtime-api" version = "0.4.2" -source = "git+ssh://git@github.com/bifrost-finance/Zenlink-DEX-Module?branch=polkadot-v0.9.8#8e9e87081b579e950a544f33cd380b38772370a1" +source = "git+ssh://git@github.com/bifrost-finance/Zenlink-DEX-Module?branch=polkadot-v0.9.8#7a6fb92ce19bdb46d10bf12abb1f3cbc8ab50932" dependencies = [ "parity-scale-codec", "sp-api", diff --git a/pallets/flexible-fee/src/fee_dealer.rs b/pallets/flexible-fee/src/fee_dealer.rs index 04b09d9da5..9f03a0f3b2 100644 --- a/pallets/flexible-fee/src/fee_dealer.rs +++ b/pallets/flexible-fee/src/fee_dealer.rs @@ -22,17 +22,24 @@ use super::*; use crate::Config; -pub trait FeeDealer { +pub trait FeeDealer { fn ensure_can_charge_fee( who: &AccountId, fee: Balance, reason: WithdrawReasons, ) -> Result<(bool, Balance), DispatchError>; + + fn cal_fee_token_and_amount( + who: &AccountId, + fee: Balance, + ) -> Result<(CurrencyId, Balance), DispatchError>; } pub struct FixedCurrencyFeeRate(PhantomData); -impl FeeDealer> for FixedCurrencyFeeRate { +impl FeeDealer, CurrencyIdOf> + for FixedCurrencyFeeRate +{ /// Make sure there is enough BNC to be deducted if the user has assets in other form of tokens /// rather than BNC. fn ensure_can_charge_fee( @@ -84,4 +91,21 @@ impl FeeDealer> for FixedCurrencyFee Ok((false, fee)) } } + + /// This function is for runtime-api to call + fn cal_fee_token_and_amount( + who: &T::AccountId, + fee: PalletBalanceOf, + ) -> Result<(CurrencyId, PalletBalanceOf), DispatchError> { + // Make sure there are enough BNC to be deducted if the user has assets in other form of + // tokens rather than BNC. + let withdraw_reason = WithdrawReasons::TRANSACTION_PAYMENT; + let (fee_sign, fee_amount) = + T::FeeDealer::ensure_can_charge_fee(who, fee, withdraw_reason)?; + + match fee_sign { + true => Ok((T::AlternativeFeeCurrencyId::get(), fee_amount.into())), + false => Ok((T::NativeCurrencyId::get(), fee_amount.into())), + } + } } diff --git a/pallets/flexible-fee/src/lib.rs b/pallets/flexible-fee/src/lib.rs index f4b491482e..9ff57cfef5 100644 --- a/pallets/flexible-fee/src/lib.rs +++ b/pallets/flexible-fee/src/lib.rs @@ -73,7 +73,8 @@ pub mod pallet { + Copy + MaybeSerializeDeserialize + Into - + From>; + + From> + + Into>; /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; /// Handler for both NativeCurrency and MultiCurrency @@ -87,7 +88,7 @@ pub mod pallet { /// Handler for the unbalanced decrease type OnUnbalanced: OnUnbalanced>; type DexOperator: ExportZenlink; - type FeeDealer: FeeDealer>; + type FeeDealer: FeeDealer, CurrencyIdOf>; #[pallet::constant] type TreasuryAccount: Get; @@ -186,56 +187,6 @@ impl Pallet { } charge_order_list } - - /// This function is for runtime-api to call - pub fn cal_fee_token_and_amount( - who: &T::AccountId, - fee: PalletBalanceOf, - ) -> Result<(CurrencyId, T::Balance), DispatchError> { - let mut fee_token_id_out: CurrencyIdOf = T::NativeCurrencyId::get(); - let mut fee_token_amount_out: T::Balance = T::Balance::from(0 as u32); - - // get the user defined fee charge order list. - let user_fee_charge_order_list = Self::inner_get_user_fee_charge_order_list(who); - let amount_out: AssetBalance = fee.saturated_into(); - let native_asset_id: AssetId = AssetId::try_from(T::NativeCurrencyId::get()) - .map_err(|_| DispatchError::Other("Conversion Error"))?; - - // charge the fee by the order of the above order list. - // first to check whether the user has the asset. If no, pass it. If yes, try to make - // transaction in the DEX in exchange for BNC - for currency_id in user_fee_charge_order_list { - // If it is mainnet currency - if currency_id == T::NativeCurrencyId::get() { - // check native balance if is enough - let native_balance = <::Currency as Currency< - ::AccountId, - >>::free_balance(who); - - if native_balance >= fee.into() { - fee_token_amount_out = fee.into(); - break; - } - } else { - // If it is other assets - let asset_balance = T::MultiCurrency::total_balance(currency_id, who); - let token_asset_id: AssetId = AssetId::try_from(currency_id) - .map_err(|_| DispatchError::Other("Conversion Error"))?; - let path = vec![native_asset_id.clone(), token_asset_id]; - - let amount_vec = T::DexOperator::get_amount_in_by_path(amount_out, &path)?; - let amount_in = amount_vec[0]; - let amount_in_balance = amount_in.saturated_into(); - - if asset_balance >= amount_in_balance { - fee_token_id_out = currency_id; - fee_token_amount_out = amount_in_balance; - break; - } - } - } - Ok((fee_token_id_out, fee_token_amount_out)) - } } /// Default implementation for a Currency and an OnUnbalanced handler. @@ -347,7 +298,7 @@ where } } -impl FeeDealer> for Pallet { +impl FeeDealer, CurrencyIdOf> for Pallet { /// Make sure there are enough BNC to be deducted if the user has assets in other form of tokens /// rather than BNC. fn ensure_can_charge_fee( @@ -429,4 +380,54 @@ impl FeeDealer> for Pallet { } Ok((false, fee)) } + + /// This function is for runtime-api to call + fn cal_fee_token_and_amount( + who: &T::AccountId, + fee: PalletBalanceOf, + ) -> Result<(CurrencyId, PalletBalanceOf), DispatchError> { + let mut fee_token_id_out: CurrencyIdOf = T::NativeCurrencyId::get(); + let mut fee_token_amount_out: T::Balance = T::Balance::from(0 as u32); + + // get the user defined fee charge order list. + let user_fee_charge_order_list = Self::inner_get_user_fee_charge_order_list(who); + let amount_out: AssetBalance = fee.saturated_into(); + let native_asset_id: AssetId = AssetId::try_from(T::NativeCurrencyId::get()) + .map_err(|_| DispatchError::Other("Conversion Error"))?; + + // charge the fee by the order of the above order list. + // first to check whether the user has the asset. If no, pass it. If yes, try to make + // transaction in the DEX in exchange for BNC + for currency_id in user_fee_charge_order_list { + // If it is mainnet currency + if currency_id == T::NativeCurrencyId::get() { + // check native balance if is enough + let native_balance = <::Currency as Currency< + ::AccountId, + >>::free_balance(who); + + if native_balance >= fee.into() { + fee_token_amount_out = fee.into(); + break; + } + } else { + // If it is other assets + let asset_balance = T::MultiCurrency::total_balance(currency_id, who); + let token_asset_id: AssetId = AssetId::try_from(currency_id) + .map_err(|_| DispatchError::Other("Conversion Error"))?; + let path = vec![native_asset_id.clone(), token_asset_id]; + + let amount_vec = T::DexOperator::get_amount_in_by_path(amount_out, &path)?; + let amount_in = amount_vec[0]; + let amount_in_balance = amount_in.saturated_into(); + + if asset_balance >= amount_in_balance { + fee_token_id_out = currency_id; + fee_token_amount_out = amount_in_balance; + break; + } + } + } + Ok((fee_token_id_out, fee_token_amount_out.into())) + } } diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index c3237ebeaf..5f19e28108 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -71,7 +71,7 @@ use xcm_support::Get; /// Constant values used within the runtime. pub mod constants; -use bifrost_flexible_fee::fee_dealer::FixedCurrencyFeeRate; +use bifrost_flexible_fee::fee_dealer::{FeeDealer, FixedCurrencyFeeRate}; use bifrost_runtime_common::xcm_impl::{ BifrostAccountIdToMultiLocation, BifrostAssetMatcher, BifrostCurrencyIdConvert, BifrostFilteredAssets, BifrostXcmTransactFilter, diff --git a/runtime/bifrost/Cargo.toml b/runtime/bifrost/Cargo.toml index 66d67125d6..5993d38248 100644 --- a/runtime/bifrost/Cargo.toml +++ b/runtime/bifrost/Cargo.toml @@ -80,6 +80,8 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", default-feature # Bifrost pallet-vesting = { package = "bifrost-vesting", path = "../../pallets/vesting", default-features = false } bifrost-runtime-common = { package = "bifrost-runtime-common", path = "../common", default-features = false } +bifrost-flexible-fee = { path = "../../pallets/flexible-fee", default-features = false } +bifrost-flexible-fee-rpc-runtime-api = { path = "../../pallets/flexible-fee/rpc/runtime-api", default-features = false } # orml orml-currencies = { version = "0.4.1-dev", default-features = false } @@ -143,6 +145,8 @@ std = [ "orml-traits/std", "orml-tokens/std", "bifrost-runtime-common/std", + "bifrost-flexible-fee/std", + "bifrost-flexible-fee-rpc-runtime-api/std", ] with-tracing = ["frame-executive/with-tracing"] @@ -163,6 +167,7 @@ runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", + "bifrost-flexible-fee/runtime-benchmarks", ] try-runtime = [ diff --git a/runtime/bifrost/src/lib.rs b/runtime/bifrost/src/lib.rs index b13340d822..aa9486eb07 100644 --- a/runtime/bifrost/src/lib.rs +++ b/runtime/bifrost/src/lib.rs @@ -49,7 +49,7 @@ use sp_core::{ pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdConversion, BlakeTwo256, Block as BlockT}, + traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Zero}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; @@ -62,6 +62,7 @@ use static_assertions::const_assert; /// Constant values used within the runtime. pub mod constants; +use bifrost_flexible_fee::fee_dealer::{FeeDealer, FixedCurrencyFeeRate}; use bifrost_runtime_common::xcm_impl::{ BifrostAssetMatcher, BifrostCurrencyIdConvert, BifrostFilteredAssets, BifrostXcmTransactFilter, }; @@ -522,7 +523,7 @@ impl pallet_tips::Config for Runtime { impl pallet_transaction_payment::Config for Runtime { type FeeMultiplierUpdate = (); - type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OnChargeTransaction = FlexibleFee; type TransactionByteFee = TransactionByteFee; type WeightToFee = IdentityFee; } @@ -817,6 +818,30 @@ impl orml_tokens::Config for Runtime { // orml runtime end +// Bifrost modules start +parameter_types! { + pub const AlternativeFeeCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); + pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); +} + +impl bifrost_flexible_fee::Config for Runtime { + type Balance = Balance; + type Currency = Balances; + type DexOperator = (); + // type FeeDealer = FlexibleFee; + type FeeDealer = FixedCurrencyFeeRate; + type Event = Event; + type MultiCurrency = Currencies; + type TreasuryAccount = BifrostTreasuryAccount; + type NativeCurrencyId = NativeCurrencyId; + type AlternativeFeeCurrencyId = AlternativeFeeCurrencyId; + type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; + type OnUnbalanced = Treasury; + type WeightInfo = weights::bifrost_flexible_fee::WeightInfo; +} + +// Bifrost modules end + construct_runtime! { pub enum Runtime where Block = Block, @@ -872,6 +897,9 @@ construct_runtime! { // XTokens: orml_xtokens::{Pallet, Storage, Call, Event} = 70, Tokens: orml_tokens::{Pallet, Call, Storage, Event} = 71, Currencies: orml_currencies::{Pallet, Call, Event} = 72, + + // Bifrost modules + FlexibleFee: bifrost_flexible_fee::{Pallet, Call, Storage, Event} = 100, } } @@ -1028,6 +1056,16 @@ impl_runtime_apis! { } } + impl bifrost_flexible_fee_rpc_runtime_api::FlexibleFeeRuntimeApi for Runtime { + fn get_fee_token_and_amount(who: AccountId, fee: Balance) -> (CurrencyId, Balance) { + let rs = FlexibleFee::cal_fee_token_and_amount(&who, fee); + match rs { + Ok(val) => val, + _ => (CurrencyId::Native(TokenSymbol::BNC), Zero::zero()), + } + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn dispatch_benchmark( @@ -1055,6 +1093,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_treasury, Treasury); add_benchmark!(params, batches, pallet_utility, Utility); add_benchmark!(params, batches, pallet_vesting, Vesting); + add_benchmark!(params, batches, bifrost_flexible_fee, FlexibleFee); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/runtime/bifrost/src/weights/bifrost_flexible_fee.rs b/runtime/bifrost/src/weights/bifrost_flexible_fee.rs new file mode 100644 index 0000000000..2da33f55b5 --- /dev/null +++ b/runtime/bifrost/src/weights/bifrost_flexible_fee.rs @@ -0,0 +1,53 @@ +// 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 . + +//! Autogenerated weights for bifrost_flexible_fee +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-08-11, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asgard-local"), DB CACHE: 128 + +// Executed Command: +// target/release/bifrost +// benchmark +// --chain=asgard-local +// --steps=50 +// --repeat=20 +// --pallet=bifrost_flexible_fee +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --header=./HEADER-GPL3 +// --output=./runtime/asgard/src/weights/bifrost_flexible_fee.rs + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for bifrost_flexible_fee. +pub struct WeightInfo(PhantomData); +impl bifrost_flexible_fee::WeightInfo for WeightInfo { + fn set_user_fee_charge_order() -> Weight { + (6_081_000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} diff --git a/runtime/bifrost/src/weights/mod.rs b/runtime/bifrost/src/weights/mod.rs index 67c91223e8..f628b8dba2 100644 --- a/runtime/bifrost/src/weights/mod.rs +++ b/runtime/bifrost/src/weights/mod.rs @@ -20,6 +20,7 @@ //! A list of the different weight modules for our runtime. +pub mod bifrost_flexible_fee; pub mod frame_system; pub mod orml_tokens; pub mod pallet_balances; diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs index 690dcd2f49..d7de0c2e6b 100644 --- a/runtime/dev/src/lib.rs +++ b/runtime/dev/src/lib.rs @@ -76,7 +76,7 @@ use xcm_support::Get; /// Constant values used within the runtime. pub mod constants; -use bifrost_flexible_fee::fee_dealer::FixedCurrencyFeeRate; +use bifrost_flexible_fee::fee_dealer::{FeeDealer, FixedCurrencyFeeRate}; use bifrost_runtime_common::xcm_impl::{ BifrostAccountIdToMultiLocation, BifrostAssetMatcher, BifrostCurrencyIdConvert, BifrostFilteredAssets, BifrostXcmTransactFilter,