-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
1,209 additions
and
1,342 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// This file is part of Bifrost. | ||
|
||
// Copyright (C) Liebi Technologies PTE. 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 <https://www.gnu.org/licenses/>. | ||
|
||
use crate::{Config, Error, Pallet, UniversalFeeCurrencyOrderList, UserDefaultFeeCurrency}; | ||
use bifrost_primitives::{AccountFeeCurrency, BalanceCmp, CurrencyId, WETH}; | ||
use frame_support::traits::{ | ||
fungibles::Inspect, | ||
tokens::{Fortitude, Preservation}, | ||
}; | ||
use sp_arithmetic::traits::UniqueSaturatedInto; | ||
use sp_core::U256; | ||
use sp_std::cmp::Ordering; | ||
|
||
/// Provides account's fee payment asset or default fee asset ( Native asset ) | ||
impl<T: Config> AccountFeeCurrency<T::AccountId> for Pallet<T> { | ||
type Error = Error<T>; | ||
|
||
/// Determines the appropriate currency to be used for paying transaction fees based on a | ||
/// prioritized order: | ||
/// 1. User's default fee currency (`UserDefaultFeeCurrency`) | ||
/// 2. WETH | ||
/// 3. Currencies in the `UniversalFeeCurrencyOrderList` | ||
/// | ||
/// The method first checks if the balance of the highest-priority currency is sufficient to | ||
/// cover the fee.If the balance is insufficient, it iterates through the list of currencies in | ||
/// priority order.If no currency has a sufficient balance, it returns the currency with the | ||
/// highest balance. | ||
fn get_fee_currency(account: &T::AccountId, fee: U256) -> Result<CurrencyId, Error<T>> { | ||
let fee: u128 = fee.unique_saturated_into(); | ||
let priority_currency = UserDefaultFeeCurrency::<T>::get(account); | ||
let mut currency_list = UniversalFeeCurrencyOrderList::<T>::get(); | ||
|
||
let first_item_index = 0; | ||
currency_list | ||
.try_insert(first_item_index, WETH) | ||
.map_err(|_| Error::<T>::MaxCurrenciesReached)?; | ||
|
||
// When all currency balances are insufficient, return the one with the highest balance | ||
let mut hopeless_currency = WETH; | ||
|
||
if let Some(currency) = priority_currency { | ||
currency_list | ||
.try_insert(first_item_index, currency) | ||
.map_err(|_| Error::<T>::MaxCurrenciesReached)?; | ||
hopeless_currency = currency; | ||
} | ||
|
||
for maybe_currency in currency_list.iter() { | ||
let comp_res = Self::cmp_with_precision(account, maybe_currency, fee, 18)?; | ||
|
||
match comp_res { | ||
Ordering::Less => { | ||
// Get the currency with the highest balance | ||
let hopeless_currency_balance = T::MultiCurrency::reducible_balance( | ||
hopeless_currency, | ||
account, | ||
Preservation::Preserve, | ||
Fortitude::Polite, | ||
); | ||
let maybe_currency_balance = T::MultiCurrency::reducible_balance( | ||
*maybe_currency, | ||
account, | ||
Preservation::Preserve, | ||
Fortitude::Polite, | ||
); | ||
hopeless_currency = match hopeless_currency_balance.cmp(&maybe_currency_balance) | ||
{ | ||
Ordering::Less => *maybe_currency, | ||
_ => hopeless_currency, | ||
}; | ||
continue; | ||
}, | ||
Ordering::Equal => return Ok(*maybe_currency), | ||
Ordering::Greater => return Ok(*maybe_currency), | ||
}; | ||
} | ||
|
||
return Ok(hopeless_currency); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// This file is part of Bifrost. | ||
|
||
// Copyright (C) Liebi Technologies PTE. 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 <https://www.gnu.org/licenses/>. | ||
|
||
pub mod account_fee_currency; | ||
pub mod on_charge_transaction; |
162 changes: 162 additions & 0 deletions
162
pallets/flexible-fee/src/impls/on_charge_transaction.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// This file is part of Bifrost. | ||
|
||
// Copyright (C) Liebi Technologies PTE. 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 <https://www.gnu.org/licenses/>. | ||
|
||
use crate::{Config, ExtraFeeByCall, Pallet}; | ||
use bifrost_primitives::{Balance, CurrencyId, Price, PriceFeeder, BNC}; | ||
use orml_traits::MultiCurrency; | ||
use pallet_transaction_payment::OnChargeTransaction; | ||
use parity_scale_codec::Encode; | ||
use sp_core::Get; | ||
use sp_runtime::{ | ||
traits::{DispatchInfoOf, PostDispatchInfoOf, Zero}, | ||
transaction_validity::{InvalidTransaction, TransactionValidityError}, | ||
}; | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
pub enum PaymentInfo { | ||
Native(Balance), | ||
NonNative(Balance, CurrencyId, Price, Price), | ||
} | ||
|
||
/// Default implementation for a Currency and an OnUnbalanced handler. | ||
impl<T> OnChargeTransaction<T> for Pallet<T> | ||
where | ||
T: Config, | ||
T::MultiCurrency: MultiCurrency<T::AccountId, CurrencyId = CurrencyId>, | ||
{ | ||
type Balance = Balance; | ||
type LiquidityInfo = Option<PaymentInfo>; | ||
|
||
/// Withdraw the predicted fee from the transaction origin. | ||
/// | ||
/// Note: The `fee` already includes the `tip`. | ||
fn withdraw_fee( | ||
who: &T::AccountId, | ||
call: &T::RuntimeCall, | ||
_info: &DispatchInfoOf<T::RuntimeCall>, | ||
fee: Self::Balance, | ||
_tip: Self::Balance, | ||
) -> Result<Self::LiquidityInfo, TransactionValidityError> { | ||
if fee.is_zero() { | ||
return Ok(None); | ||
} | ||
|
||
let (fee_currency, fee_amount, bnc_price, fee_currency_price) = | ||
Self::get_fee_currency_and_fee_amount(who, fee) | ||
.map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; | ||
|
||
// withdraw normal extrinsic fee | ||
T::MultiCurrency::withdraw(fee_currency, who, fee_amount) | ||
.map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; | ||
|
||
for (call_name, (extra_fee_currency, extra_fee_amount, extra_fee_receiver)) in | ||
ExtraFeeByCall::<T>::iter() | ||
{ | ||
let raw_call_name = call_name.to_vec(); | ||
let raw_call_name_len = raw_call_name.len(); | ||
if call.encode().len() >= raw_call_name_len { | ||
if call.encode()[0..raw_call_name_len].eq(&raw_call_name) { | ||
match Self::charge_extra_fee( | ||
who, | ||
extra_fee_currency, | ||
extra_fee_amount, | ||
&extra_fee_receiver, | ||
) { | ||
Ok(_) => {}, | ||
Err(_) => { | ||
return Err(TransactionValidityError::Invalid( | ||
InvalidTransaction::Payment, | ||
)); | ||
}, | ||
} | ||
}; | ||
} | ||
} | ||
|
||
if fee_currency == BNC { | ||
Ok(Some(PaymentInfo::Native(fee_amount))) | ||
} else { | ||
Ok(Some(PaymentInfo::NonNative( | ||
fee_amount, | ||
fee_currency, | ||
bnc_price, | ||
fee_currency_price, | ||
))) | ||
} | ||
} | ||
|
||
/// Hand the fee and the tip over to the `[OnUnbalanced]` implementation. | ||
/// Since the predicted fee might have been too high, parts of the fee may | ||
/// be refunded. | ||
/// | ||
/// Note: The `fee` already includes the `tip`. | ||
fn correct_and_deposit_fee( | ||
who: &T::AccountId, | ||
_dispatch_info: &DispatchInfoOf<T::RuntimeCall>, | ||
_post_info: &PostDispatchInfoOf<T::RuntimeCall>, | ||
corrected_fee: Self::Balance, | ||
tip: Self::Balance, | ||
already_withdrawn: Self::LiquidityInfo, | ||
) -> Result<(), TransactionValidityError> { | ||
if let Some(paid) = already_withdrawn { | ||
// Calculate how much refund we should return | ||
let (currency, refund, fee, tip) = match paid { | ||
PaymentInfo::Native(paid_fee) => ( | ||
BNC, | ||
paid_fee.saturating_sub(corrected_fee), | ||
corrected_fee.saturating_sub(tip), | ||
tip, | ||
), | ||
PaymentInfo::NonNative(paid_fee, fee_currency, bnc_price, fee_currency_price) => { | ||
// calculate corrected_fee in the non-native currency | ||
let converted_corrected_fee = T::PriceFeeder::get_amount_by_prices( | ||
&BNC, | ||
corrected_fee, | ||
bnc_price, | ||
&fee_currency, | ||
fee_currency_price, | ||
) | ||
.ok_or(TransactionValidityError::Invalid(InvalidTransaction::Payment))?; | ||
let refund = paid_fee.saturating_sub(converted_corrected_fee); | ||
let converted_tip = T::PriceFeeder::get_amount_by_prices( | ||
&BNC, | ||
tip, | ||
bnc_price, | ||
&fee_currency, | ||
fee_currency_price, | ||
) | ||
.ok_or(TransactionValidityError::Invalid(InvalidTransaction::Payment))?; | ||
( | ||
fee_currency, | ||
refund, | ||
converted_corrected_fee.saturating_sub(converted_tip), | ||
converted_tip, | ||
) | ||
}, | ||
}; | ||
// refund to the account that paid the fees | ||
T::MultiCurrency::deposit(currency, who, refund) | ||
.map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; | ||
|
||
// deposit the fee | ||
T::MultiCurrency::deposit(currency, &T::TreasuryAccount::get(), fee + tip) | ||
.map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; | ||
} | ||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.