#![cfg_attr(not(feature = "std"), no_std)]
use cfg_traits::fees::{self, Fee};
use frame_support::{
pallet_prelude::{DispatchError, DispatchResult},
traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReasons},
DefaultNoBound,
};
pub use pallet::*;
#[cfg(test)]
mod mock;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod tests;
pub mod weights;
pub use weights::*;
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub type ImbalanceOf<T> = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config + pallet_authorship::Config {
type FeeKey: Parameter + MaybeSerializeDeserialize + MaxEncodedLen;
type Currency: Currency<Self::AccountId>;
type Treasury: OnUnbalanced<ImbalanceOf<Self>>;
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type FeeChangeOrigin: EnsureOrigin<Self::RuntimeOrigin>;
type DefaultFeeValue: Get<BalanceOf<Self>>;
type WeightInfo: WeightInfo;
}
#[pallet::storage]
#[pallet::getter(fn fee)]
pub(super) type FeeBalances<T: Config> =
StorageMap<_, Blake2_256, T::FeeKey, BalanceOf<T>, ValueQuery, T::DefaultFeeValue>;
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub initial_fees: sp_std::vec::Vec<(T::FeeKey, BalanceOf<T>)>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
for (key, fee) in self.initial_fees.iter() {
<FeeBalances<T>>::insert(key, fee);
}
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
FeeChanged {
key: T::FeeKey,
fee: BalanceOf<T>,
},
FeeToAuthor {
from: T::AccountId,
balance: BalanceOf<T>,
},
FeeToBurn {
from: T::AccountId,
balance: BalanceOf<T>,
},
FeeToTreasury {
from: T::AccountId,
balance: BalanceOf<T>,
},
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(<T as pallet::Config>::WeightInfo::set_fee())]
#[pallet::call_index(0)]
pub fn set_fee(origin: OriginFor<T>, key: T::FeeKey, fee: BalanceOf<T>) -> DispatchResult {
T::FeeChangeOrigin::ensure_origin(origin)?;
<FeeBalances<T>>::insert(key.clone(), fee);
Self::deposit_event(Event::FeeChanged { key, fee });
Ok(())
}
}
}
impl<T: Config> fees::Fees for Pallet<T> {
type AccountId = T::AccountId;
type Balance = BalanceOf<T>;
type FeeKey = T::FeeKey;
fn fee_value(key: Self::FeeKey) -> BalanceOf<T> {
<FeeBalances<T>>::get(key)
}
fn fee_to_author(
from: &Self::AccountId,
fee: Fee<BalanceOf<T>, Self::FeeKey>,
) -> DispatchResult {
if let Some(author) = <pallet_authorship::Pallet<T>>::author() {
let imbalance = Self::withdraw_fee(from, fee)?;
let balance = imbalance.peek();
T::Currency::resolve_creating(&author, imbalance);
Self::deposit_event(Event::FeeToAuthor {
from: author,
balance,
});
}
Ok(())
}
fn fee_to_burn(from: &Self::AccountId, fee: Fee<BalanceOf<T>, Self::FeeKey>) -> DispatchResult {
let imbalance = Self::withdraw_fee(from, fee)?;
let balance = imbalance.peek();
Self::deposit_event(Event::FeeToBurn {
from: from.clone(),
balance,
});
Ok(())
}
fn fee_to_treasury(
from: &Self::AccountId,
fee: Fee<BalanceOf<T>, Self::FeeKey>,
) -> DispatchResult {
let imbalance = Self::withdraw_fee(from, fee)?;
let balance = imbalance.peek();
T::Treasury::on_unbalanced(imbalance);
Self::deposit_event(Event::FeeToTreasury {
from: from.clone(),
balance,
});
Ok(())
}
#[cfg(feature = "runtime-benchmarks")]
fn add_fee_requirements(from: &Self::AccountId, fee: Fee<Self::Balance, Self::FeeKey>) {
let _ = T::Currency::deposit_creating(from, T::Currency::minimum_balance());
let _ = T::Currency::deposit_creating(from, fee.value::<Self>());
}
}
impl<T: Config> Pallet<T> {
fn withdraw_fee(
from: &T::AccountId,
fee: Fee<BalanceOf<T>, T::FeeKey>,
) -> Result<ImbalanceOf<T>, DispatchError> {
T::Currency::withdraw(
from,
fee.value::<Self>(),
WithdrawReasons::FEE,
ExistenceRequirement::KeepAlive,
)
}
}