use cfg_primitives::{
constants::{CENTI_CFG, TREASURY_FEE_RATIO},
types::Balance,
AccountId,
};
use cfg_traits::fees::{Fee, Fees, PayFee};
use cfg_types::fee_keys::FeeKey;
use frame_support::{
dispatch::DispatchResult,
traits::{Currency, Get, Imbalance, OnUnbalanced},
weights::{
constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
},
};
use smallvec::smallvec;
use sp_arithmetic::Perbill;
pub type NegativeImbalance<R> = <pallet_balances::Pallet<R> as Currency<
<R as frame_system::Config>::AccountId,
>>::NegativeImbalance;
struct ToAuthor<R>(sp_std::marker::PhantomData<R>);
impl<R> OnUnbalanced<NegativeImbalance<R>> for ToAuthor<R>
where
R: pallet_balances::Config + pallet_authorship::Config,
{
fn on_nonzero_unbalanced(amount: NegativeImbalance<R>) {
if let Some(author) = <pallet_authorship::Pallet<R>>::author() {
<pallet_balances::Pallet<R>>::resolve_creating(&author, amount);
}
}
}
pub struct DealWithFees<R>(sp_std::marker::PhantomData<R>);
impl<R> OnUnbalanced<NegativeImbalance<R>> for DealWithFees<R>
where
R: pallet_balances::Config + pallet_treasury::Config + pallet_authorship::Config,
pallet_treasury::Pallet<R>: OnUnbalanced<NegativeImbalance<R>>,
{
fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance<R>>) {
if let Some(fees) = fees_then_tips.next() {
let (treasury_amount, mut author_amount) = fees.ration(
TREASURY_FEE_RATIO.deconstruct(),
(Perbill::one() - TREASURY_FEE_RATIO).deconstruct(),
);
if let Some(tips) = fees_then_tips.next() {
tips.merge_into(&mut author_amount);
}
use pallet_treasury::Pallet as Treasury;
<Treasury<R> as OnUnbalanced<_>>::on_unbalanced(treasury_amount);
<ToAuthor<R> as OnUnbalanced<_>>::on_unbalanced(author_amount);
}
}
}
pub struct WeightToFee;
impl WeightToFeePolynomial for WeightToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
let p = CENTI_CFG;
let q = 10 * Balance::from(ExtrinsicBaseWeight::get().ref_time());
smallvec!(WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p % q, q),
coeff_integer: p / q,
})
}
}
pub struct FeeToTreasury<F, V>(sp_std::marker::PhantomData<(F, V)>);
impl<
F: Fees<AccountId = AccountId, Balance = Balance, FeeKey = FeeKey>,
V: Get<Fee<Balance, FeeKey>>,
> PayFee<AccountId> for FeeToTreasury<F, V>
{
fn pay(payer: &AccountId) -> DispatchResult {
F::fee_to_treasury(payer, V::get())
}
#[cfg(feature = "runtime-benchmarks")]
fn add_pay_requirements(payer: &AccountId) {
F::add_fee_requirements(payer, V::get());
}
}
pub struct FeeToAuthor<F, V>(sp_std::marker::PhantomData<(F, V)>);
impl<
F: Fees<AccountId = AccountId, Balance = Balance, FeeKey = FeeKey>,
V: Get<Fee<Balance, FeeKey>>,
> PayFee<AccountId> for FeeToAuthor<F, V>
{
fn pay(payer: &AccountId) -> DispatchResult {
F::fee_to_author(payer, V::get())
}
#[cfg(feature = "runtime-benchmarks")]
fn add_pay_requirements(payer: &AccountId) {
F::add_fee_requirements(payer, V::get());
}
}
pub struct FeeToBurn<F, V>(sp_std::marker::PhantomData<(F, V)>);
impl<
F: Fees<AccountId = AccountId, Balance = Balance, FeeKey = FeeKey>,
V: Get<Fee<Balance, FeeKey>>,
> PayFee<AccountId> for FeeToBurn<F, V>
{
fn pay(payer: &AccountId) -> DispatchResult {
F::fee_to_burn(payer, V::get())
}
#[cfg(feature = "runtime-benchmarks")]
fn add_pay_requirements(payer: &AccountId) {
F::add_fee_requirements(payer, V::get());
}
}
#[cfg(test)]
mod test {
use cfg_primitives::{AccountId, TREASURY_FEE_RATIO};
use cfg_types::ids::TREASURY_PALLET_ID;
use frame_support::{
derive_impl, parameter_types,
traits::{
tokens::{PayFromAccount, UnityAssetBalanceConversion},
Currency, FindAuthor,
},
PalletId,
};
use sp_core::ConstU64;
use sp_runtime::{traits::IdentityLookup, Perbill};
use sp_std::convert::{TryFrom, TryInto};
use super::*;
const TEST_ACCOUNT: AccountId = AccountId::new([1; 32]);
frame_support::construct_runtime!(
pub enum Runtime {
System: frame_system,
Authorship: pallet_authorship,
Balances: pallet_balances,
Treasury: pallet_treasury,
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type AccountData = pallet_balances::AccountData<u64>;
type AccountId = AccountId;
type Block = frame_system::mocking::MockBlock<Runtime>;
type Lookup = IdentityLookup<Self::AccountId>;
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)]
impl pallet_balances::Config for Runtime {
type AccountStore = System;
type DustRemoval = ();
type ExistentialDeposit = ConstU64<1>;
type RuntimeHoldReason = ();
}
parameter_types! {
pub const TreasuryPalletId: PalletId = TREASURY_PALLET_ID;
pub TreasuryAccount: AccountId = Treasury::account_id();
pub const MaxApprovals: u32 = 100;
}
impl pallet_treasury::Config for Runtime {
type ApproveOrigin = frame_system::EnsureRoot<AccountId>;
type AssetKind = ();
type BalanceConverter = UnityAssetBalanceConversion;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Burn = ();
type BurnDestination = ();
type Currency = pallet_balances::Pallet<Runtime>;
type MaxApprovals = MaxApprovals;
type OnSlash = ();
type PalletId = TreasuryPalletId;
type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
type PayoutPeriod = ConstU64<10>;
type ProposalBond = ();
type ProposalBondMaximum = ();
type ProposalBondMinimum = ();
type RejectOrigin = frame_system::EnsureRoot<AccountId>;
type RuntimeEvent = RuntimeEvent;
type SpendFunds = ();
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
type SpendPeriod = ();
type WeightInfo = ();
}
pub struct OneAuthor;
impl FindAuthor<AccountId> for OneAuthor {
fn find_author<'a, I>(_: I) -> Option<AccountId>
where
I: 'a,
{
Some(TEST_ACCOUNT)
}
}
impl pallet_authorship::Config for Runtime {
type EventHandler = ();
type FindAuthor = OneAuthor;
}
#[test]
fn test_fees_and_tip_split() {
System::externalities().execute_with(|| {
const FEE: u64 = 10;
const TIP: u64 = 20;
let fee = Balances::issue(FEE);
let tip = Balances::issue(TIP);
assert_eq!(Balances::free_balance(Treasury::account_id()), 0);
assert_eq!(Balances::free_balance(TEST_ACCOUNT), 0);
DealWithFees::on_unbalanceds(vec![fee, tip].into_iter());
assert_eq!(
Balances::free_balance(Treasury::account_id()),
TREASURY_FEE_RATIO * FEE
);
assert_eq!(
Balances::free_balance(TEST_ACCOUNT),
TIP + (Perbill::one() - TREASURY_FEE_RATIO) * FEE
);
});
}
}