diff --git a/pallets/flexible-fee/src/fee_dealer.rs b/pallets/flexible-fee/src/fee_dealer.rs index 8d09f9f71f..8a263315aa 100644 --- a/pallets/flexible-fee/src/fee_dealer.rs +++ b/pallets/flexible-fee/src/fee_dealer.rs @@ -37,41 +37,63 @@ pub trait FeeDealer { pub struct FixedCurrencyFeeRate(PhantomData); impl FeeDealer> for FixedCurrencyFeeRate { - /// Make sure there are enough BNC to be deducted if the user has assets in other form of tokens + /// 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( who: &T::AccountId, fee: PalletBalanceOf, - _reason: WithdrawReasons, + reason: WithdrawReasons, ) -> DispatchResult { - let fee_currency_id: CurrencyId = T::AlternativeFeeCurrencyId::get(); - let (fee_currency_base, native_currency_base): (u32, u32) = - T::AltFeeCurrencyExchangeRate::get(); - - let fee_currency_balance = T::MultiCurrency::free_balance(fee_currency_id, who); - - let consume_fee_currency_amount = - fee.saturating_mul(fee_currency_base.into()) / native_currency_base.into(); - ensure!( - consume_fee_currency_amount <= - PalletBalanceOf::::unique_saturated_from(fee_currency_balance.into()), - Error::::NotEnoughBalance - ); - - // 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. - T::MultiCurrency::withdraw( - fee_currency_id, - who, - T::Balance::from(consume_fee_currency_amount), - )?; - T::MultiCurrency::deposit(T::NativeCurrencyId::get(), who, T::Balance::from(fee))?; - - crate::Pallet::::deposit_event(Event::FixedRateFeeExchanged( - fee_currency_id, - consume_fee_currency_amount, - )); + // First, check if the user has enough BNC balance to be deducted.assert_eq! + let existential_deposit = <::Currency as Currency< + ::AccountId, + >>::minimum_balance(); + // check native balance if is enough + let native_is_enough = <::Currency as Currency< + ::AccountId, + >>::free_balance(who) + .checked_sub(&(fee + existential_deposit.into())) + .map_or(false, |new_free_balance| { + <::Currency as Currency< + ::AccountId, + >>::ensure_can_withdraw( + who, fee, reason, new_free_balance + ) + .is_ok() + }); + + if !native_is_enough { + // If the user doesn't has enough BNC, then we will use KSM as the fee currency. + let fee_currency_id: CurrencyId = T::AlternativeFeeCurrencyId::get(); + let (fee_currency_base, native_currency_base): (u32, u32) = + T::AltFeeCurrencyExchangeRate::get(); + + let fee_currency_balance = T::MultiCurrency::free_balance(fee_currency_id, who); + + let consume_fee_currency_amount = + fee.saturating_mul(fee_currency_base.into()) / native_currency_base.into(); + ensure!( + consume_fee_currency_amount <= + PalletBalanceOf::::unique_saturated_from(fee_currency_balance.into()), + Error::::NotEnoughBalance + ); + + // 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. + T::MultiCurrency::withdraw( + fee_currency_id, + who, + T::Balance::from(consume_fee_currency_amount), + )?; + T::MultiCurrency::deposit(T::NativeCurrencyId::get(), who, T::Balance::from(fee))?; + + crate::Pallet::::deposit_event(Event::FixedRateFeeExchanged( + fee_currency_id, + consume_fee_currency_amount, + )); + } + Ok(()) } } diff --git a/pallets/flexible-fee/src/tests.rs b/pallets/flexible-fee/src/tests.rs index 4377a48e1e..2626c4698a 100644 --- a/pallets/flexible-fee/src/tests.rs +++ b/pallets/flexible-fee/src/tests.rs @@ -355,6 +355,7 @@ fn ensure_can_charge_fee_v2_should_work() { // #[ignore = "This should be used with mock config type FeeDealer = FixedCurrencyFeeRate."] fn withdraw_fee_should_work_v2() { new_test_ext().execute_with(|| { + assert_ok!(Currencies::deposit(CurrencyId::Native(TokenSymbol::ASG), &CHARLIE, 108)); assert_ok!(Currencies::deposit(CurrencyId::Token(TokenSymbol::KSM), &CHARLIE, 2)); // prepare call variable @@ -368,14 +369,15 @@ fn withdraw_fee_should_work_v2() { let xt = TestXt::new(call.clone(), Some((0u64, extra))); let info = xt.get_dispatch_info(); - // println!("info: {:?}", info); - - assert_eq!(::Currency::free_balance(&CHARLIE), 0); + // In the first time, we can charge transaction fee by native token. + assert_ok!(FlexibleFee::withdraw_fee(&CHARLIE, &call, &info, 107, 8)); + assert_eq!(::Currency::free_balance(&CHARLIE), 1); - // 99 inclusion fee and a tip of 8 + // In the second time, we charge transaction fee by KSM. + // 99 inclusion fee + a tip of 8 assert_ok!(FlexibleFee::withdraw_fee(&CHARLIE, &call, &info, 107, 8)); - assert_eq!(::Currency::free_balance(&CHARLIE), 0); + assert_eq!(::Currency::free_balance(&CHARLIE), 1); }); } diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index 3a0a48f0d8..d3e7e1d935 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -632,7 +632,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; } diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs index c274ece108..5b655484b0 100644 --- a/runtime/dev/src/lib.rs +++ b/runtime/dev/src/lib.rs @@ -633,7 +633,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; }