Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 26 additions & 2 deletions pallets/flexible-fee/src/fee_dealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,24 @@
use super::*;
use crate::Config;

pub trait FeeDealer<AccountId, Balance> {
pub trait FeeDealer<AccountId, Balance, CurrencyId> {
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<T: Config>(PhantomData<T>);

impl<T: Config> FeeDealer<T::AccountId, PalletBalanceOf<T>> for FixedCurrencyFeeRate<T> {
impl<T: Config> FeeDealer<T::AccountId, PalletBalanceOf<T>, CurrencyIdOf<T>>
for FixedCurrencyFeeRate<T>
{
/// 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(
Expand Down Expand Up @@ -84,4 +91,21 @@ impl<T: Config> FeeDealer<T::AccountId, PalletBalanceOf<T>> for FixedCurrencyFee
Ok((false, fee))
}
}

/// This function is for runtime-api to call
fn cal_fee_token_and_amount(
who: &T::AccountId,
fee: PalletBalanceOf<T>,
) -> Result<(CurrencyId, PalletBalanceOf<T>), 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())),
}
}
}
107 changes: 54 additions & 53 deletions pallets/flexible-fee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ pub mod pallet {
+ Copy
+ MaybeSerializeDeserialize
+ Into<u128>
+ From<PalletBalanceOf<Self>>;
+ From<PalletBalanceOf<Self>>
+ Into<PalletBalanceOf<Self>>;
/// Weight information for the extrinsics in this module.
type WeightInfo: WeightInfo;
/// Handler for both NativeCurrency and MultiCurrency
Expand All @@ -87,7 +88,7 @@ pub mod pallet {
/// Handler for the unbalanced decrease
type OnUnbalanced: OnUnbalanced<NegativeImbalanceOf<Self>>;
type DexOperator: ExportZenlink<Self::AccountId>;
type FeeDealer: FeeDealer<Self::AccountId, PalletBalanceOf<Self>>;
type FeeDealer: FeeDealer<Self::AccountId, PalletBalanceOf<Self>, CurrencyIdOf<Self>>;

#[pallet::constant]
type TreasuryAccount: Get<Self::AccountId>;
Expand Down Expand Up @@ -186,56 +187,6 @@ impl<T: Config> Pallet<T> {
}
charge_order_list
}

/// This function is for runtime-api to call
pub fn cal_fee_token_and_amount(
who: &T::AccountId,
fee: PalletBalanceOf<T>,
) -> Result<(CurrencyId, T::Balance), DispatchError> {
let mut fee_token_id_out: CurrencyIdOf<T> = 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 = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::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.
Expand Down Expand Up @@ -347,7 +298,7 @@ where
}
}

impl<T: Config> FeeDealer<T::AccountId, PalletBalanceOf<T>> for Pallet<T> {
impl<T: Config> FeeDealer<T::AccountId, PalletBalanceOf<T>, CurrencyIdOf<T>> for Pallet<T> {
/// 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(
Expand Down Expand Up @@ -429,4 +380,54 @@ impl<T: Config> FeeDealer<T::AccountId, PalletBalanceOf<T>> for Pallet<T> {
}
Ok((false, fee))
}

/// This function is for runtime-api to call
fn cal_fee_token_and_amount(
who: &T::AccountId,
fee: PalletBalanceOf<T>,
) -> Result<(CurrencyId, PalletBalanceOf<T>), DispatchError> {
let mut fee_token_id_out: CurrencyIdOf<T> = 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 = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::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()))
}
}
2 changes: 1 addition & 1 deletion runtime/asgard/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions runtime/bifrost/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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"]

Expand All @@ -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 = [
Expand Down
43 changes: 41 additions & 2 deletions runtime/bifrost/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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,
};
Expand Down Expand Up @@ -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<Balances, ()>;
type OnChargeTransaction = FlexibleFee;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = IdentityFee<Balance>;
}
Expand Down Expand Up @@ -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<Runtime>;
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<Runtime>;
}

// Bifrost modules end

construct_runtime! {
pub enum Runtime where
Block = Block,
Expand Down Expand Up @@ -872,6 +897,9 @@ construct_runtime! {
// XTokens: orml_xtokens::{Pallet, Storage, Call, Event<T>} = 70,
Tokens: orml_tokens::{Pallet, Call, Storage, Event<T>} = 71,
Currencies: orml_currencies::{Pallet, Call, Event<T>} = 72,

// Bifrost modules
FlexibleFee: bifrost_flexible_fee::{Pallet, Call, Storage, Event<T>} = 100,
}
}

Expand Down Expand Up @@ -1028,6 +1056,16 @@ impl_runtime_apis! {
}
}

impl bifrost_flexible_fee_rpc_runtime_api::FlexibleFeeRuntimeApi<Block, AccountId> 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<Block> for Runtime {
fn dispatch_benchmark(
Expand Down Expand Up @@ -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)
Expand Down
Loading