pallet_transaction_fees/
lib.rs#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, missing_debug_implementations)]
pub mod weights;
use codec::{Codec, Decode, Encode};
use frame_support::sp_runtime::traits::Zero;
use frame_support::sp_runtime::SaturatedConversion;
use frame_support::traits::{Currency, Get};
use frame_support::weights::Weight;
use frame_system::pallet_prelude::*;
pub use pallet::*;
use scale_info::TypeInfo;
use subspace_runtime_primitives::FindBlockRewardAddress;
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub trait WeightInfo {
fn on_initialize() -> Weight;
}
#[derive(Encode, Decode, TypeInfo)]
struct CollectedFees<Balance: Codec> {
storage: Balance,
compute: Balance,
tips: Balance,
}
#[frame_support::pallet]
mod pallet {
use super::{BalanceOf, CollectedFees, WeightInfo};
use frame_support::pallet_prelude::*;
use frame_support::traits::Currency;
use frame_system::pallet_prelude::*;
use subspace_runtime_primitives::{BlockTransactionByteFee, FindBlockRewardAddress};
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
#[pallet::constant]
type MinReplicationFactor: Get<u16>;
#[pallet::constant]
type CreditSupply: Get<BalanceOf<Self>>;
#[pallet::constant]
type TotalSpacePledged: Get<u128>;
#[pallet::constant]
type BlockchainHistorySize: Get<u128>;
type Currency: Currency<Self::AccountId>;
type FindBlockRewardAddress: FindBlockRewardAddress<Self::AccountId>;
type DynamicCostOfStorage: Get<bool>;
type WeightInfo: WeightInfo;
}
#[pallet::storage]
pub(super) type TransactionByteFee<T> =
StorageValue<_, BlockTransactionByteFee<BalanceOf<T>>, ValueQuery>;
#[pallet::storage]
pub(super) type IsDuringBlockExecution<T: Config> = StorageValue<_, bool, ValueQuery>;
#[pallet::storage]
pub(super) type BlockAuthor<T: Config> = StorageValue<_, T::AccountId>;
#[pallet::storage]
pub(super) type CollectedBlockFees<T: Config> = StorageValue<_, CollectedFees<BalanceOf<T>>>;
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
#[codec(index = 0)]
BlockFees {
who: T::AccountId,
storage: BalanceOf<T>,
compute: BalanceOf<T>,
tips: BalanceOf<T>,
},
#[codec(index = 1)]
BurnedBlockFees {
storage: BalanceOf<T>,
compute: BalanceOf<T>,
tips: BalanceOf<T>,
},
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
where
BalanceOf<T>: From<u8> + From<u64>,
{
fn on_initialize(now: BlockNumberFor<T>) -> Weight {
Self::do_initialize(now);
T::WeightInfo::on_initialize()
}
fn on_finalize(now: BlockNumberFor<T>) {
Self::do_finalize(now);
}
}
}
impl<T: Config> Pallet<T>
where
BalanceOf<T>: From<u64>,
{
fn do_initialize(_n: BlockNumberFor<T>) {
if let Some(block_author) = T::FindBlockRewardAddress::find_block_reward_address() {
BlockAuthor::<T>::put(block_author);
}
CollectedBlockFees::<T>::put(CollectedFees {
storage: BalanceOf::<T>::zero(),
compute: BalanceOf::<T>::zero(),
tips: BalanceOf::<T>::zero(),
});
TransactionByteFee::<T>::mutate(|transaction_byte_fee| {
transaction_byte_fee.current = transaction_byte_fee.next
});
IsDuringBlockExecution::<T>::set(true);
}
fn do_finalize(_n: BlockNumberFor<T>) {
TransactionByteFee::<T>::mutate(|transaction_byte_fee| {
transaction_byte_fee.next = Self::calculate_transaction_byte_fee()
});
IsDuringBlockExecution::<T>::take();
let collected_fees = CollectedBlockFees::<T>::take()
.expect("`CollectedBlockFees` was set in `on_initialize`; qed");
let total = collected_fees.storage + collected_fees.compute + collected_fees.tips;
if !total.is_zero() {
if let Some(block_author) = BlockAuthor::<T>::take() {
let _imbalance = T::Currency::deposit_creating(&block_author, total);
Self::deposit_event(Event::<T>::BlockFees {
who: block_author.clone(),
storage: collected_fees.storage,
compute: collected_fees.compute,
tips: collected_fees.tips,
});
} else {
let amount = collected_fees.storage + collected_fees.compute + collected_fees.tips;
if !amount.is_zero() {
Self::deposit_event(Event::<T>::BurnedBlockFees {
storage: collected_fees.storage,
compute: collected_fees.compute,
tips: collected_fees.tips,
});
}
}
}
}
pub fn transaction_byte_fee() -> BalanceOf<T> {
if !T::DynamicCostOfStorage::get() {
return BalanceOf::<T>::from(1);
}
if IsDuringBlockExecution::<T>::get() {
TransactionByteFee::<T>::get().current
} else {
TransactionByteFee::<T>::get().next
}
}
pub fn calculate_transaction_byte_fee() -> BalanceOf<T> {
let credit_supply = T::CreditSupply::get();
match (T::TotalSpacePledged::get() / u128::from(T::MinReplicationFactor::get()))
.checked_sub(T::BlockchainHistorySize::get())
{
Some(free_space) if free_space > 0 => {
credit_supply / BalanceOf::<T>::saturated_from(free_space)
}
_ => credit_supply,
}
}
pub fn note_transaction_fees(
storage_fee: BalanceOf<T>,
compute_fee: BalanceOf<T>,
tip: BalanceOf<T>,
) {
CollectedBlockFees::<T>::mutate(|collected_block_fees| {
if let Some(collected_block_fees) = collected_block_fees.as_mut() {
collected_block_fees.storage += storage_fee;
collected_block_fees.compute += compute_fee;
collected_block_fees.tips += tip;
}
});
}
}
impl<T: Config> subspace_runtime_primitives::StorageFee<BalanceOf<T>> for Pallet<T>
where
BalanceOf<T>: From<u64>,
{
fn transaction_byte_fee() -> BalanceOf<T> {
Self::transaction_byte_fee()
}
fn note_storage_fees(storage_fee: BalanceOf<T>) {
Self::note_transaction_fees(storage_fee, Zero::zero(), Zero::zero())
}
}