pallet_transaction_fees/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
4#![forbid(unsafe_code)]
5#![warn(rust_2018_idioms, missing_debug_implementations)]
6
7pub mod weights;
8
9use frame_support::sp_runtime::SaturatedConversion;
10use frame_support::sp_runtime::traits::Zero;
11use frame_support::traits::{Currency, Get};
12use frame_support::weights::Weight;
13use frame_system::pallet_prelude::*;
14pub use pallet::*;
15use parity_scale_codec::{Codec, Decode, Encode};
16use scale_info::TypeInfo;
17use subspace_runtime_primitives::FindBlockRewardAddress;
18
19type BalanceOf<T> =
20    <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
21
22pub trait WeightInfo {
23    fn on_initialize() -> Weight;
24}
25
26#[derive(Encode, Decode, TypeInfo)]
27struct CollectedFees<Balance: Codec> {
28    storage: Balance,
29    compute: Balance,
30    tips: Balance,
31}
32
33#[frame_support::pallet]
34mod pallet {
35    use super::{BalanceOf, CollectedFees, WeightInfo};
36    use frame_support::pallet_prelude::*;
37    use frame_support::traits::Currency;
38    use frame_system::pallet_prelude::*;
39    use subspace_runtime_primitives::{BlockTransactionByteFee, FindBlockRewardAddress};
40
41    #[pallet::config]
42    pub trait Config: frame_system::Config {
43        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
45
46        #[pallet::constant]
49        type MinReplicationFactor: Get<u16>;
50
51        #[pallet::constant]
53        type CreditSupply: Get<BalanceOf<Self>>;
54
55        #[pallet::constant]
57        type TotalSpacePledged: Get<u128>;
58
59        #[pallet::constant]
62        type BlockchainHistorySize: Get<u128>;
63
64        type Currency: Currency<Self::AccountId>;
65
66        type FindBlockRewardAddress: FindBlockRewardAddress<Self::AccountId>;
67
68        type DynamicCostOfStorage: Get<bool>;
70
71        type WeightInfo: WeightInfo;
72    }
73
74    #[pallet::storage]
84    pub(super) type TransactionByteFee<T> =
85        StorageValue<_, BlockTransactionByteFee<BalanceOf<T>>, ValueQuery>;
86
87    #[pallet::storage]
90    pub(super) type IsDuringBlockExecution<T: Config> = StorageValue<_, bool, ValueQuery>;
91
92    #[pallet::storage]
95    pub(super) type BlockAuthor<T: Config> = StorageValue<_, T::AccountId>;
96
97    #[pallet::storage]
100    pub(super) type CollectedBlockFees<T: Config> = StorageValue<_, CollectedFees<BalanceOf<T>>>;
101
102    #[pallet::pallet]
104    #[pallet::without_storage_info]
105    pub struct Pallet<T>(_);
106
107    #[pallet::event]
109    #[pallet::generate_deposit(pub(super) fn deposit_event)]
110    pub enum Event<T: Config> {
111        #[codec(index = 0)]
113        BlockFees {
114            who: T::AccountId,
116            storage: BalanceOf<T>,
118            compute: BalanceOf<T>,
120            tips: BalanceOf<T>,
122        },
123        #[codec(index = 1)]
125        BurnedBlockFees {
126            storage: BalanceOf<T>,
128            compute: BalanceOf<T>,
130            tips: BalanceOf<T>,
132        },
133    }
134
135    #[pallet::hooks]
136    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
137    where
138        BalanceOf<T>: From<u8> + From<u64>,
139    {
140        fn on_initialize(now: BlockNumberFor<T>) -> Weight {
141            Self::do_initialize(now);
142            T::WeightInfo::on_initialize()
143        }
144
145        fn on_finalize(now: BlockNumberFor<T>) {
146            Self::do_finalize(now);
147        }
148    }
149}
150
151impl<T: Config> Pallet<T>
152where
153    BalanceOf<T>: From<u64>,
154{
155    fn do_initialize(_n: BlockNumberFor<T>) {
156        if let Some(block_author) = T::FindBlockRewardAddress::find_block_reward_address() {
158            BlockAuthor::<T>::put(block_author);
159        }
160
161        CollectedBlockFees::<T>::put(CollectedFees {
162            storage: BalanceOf::<T>::zero(),
163            compute: BalanceOf::<T>::zero(),
164            tips: BalanceOf::<T>::zero(),
165        });
166
167        TransactionByteFee::<T>::mutate(|transaction_byte_fee| {
169            transaction_byte_fee.current = transaction_byte_fee.next
170        });
171        IsDuringBlockExecution::<T>::set(true);
172    }
173
174    fn do_finalize(_n: BlockNumberFor<T>) {
175        TransactionByteFee::<T>::mutate(|transaction_byte_fee| {
177            transaction_byte_fee.next = Self::calculate_transaction_byte_fee()
178        });
179        IsDuringBlockExecution::<T>::take();
180
181        let collected_fees = CollectedBlockFees::<T>::take()
182            .expect("`CollectedBlockFees` was set in `on_initialize`; qed");
183
184        let total = collected_fees.storage + collected_fees.compute + collected_fees.tips;
185
186        if !total.is_zero() {
187            if let Some(block_author) = BlockAuthor::<T>::take() {
189                let _imbalance = T::Currency::deposit_creating(&block_author, total);
190                Self::deposit_event(Event::<T>::BlockFees {
191                    who: block_author.clone(),
192                    storage: collected_fees.storage,
193                    compute: collected_fees.compute,
194                    tips: collected_fees.tips,
195                });
196            } else {
197                let amount = collected_fees.storage + collected_fees.compute + collected_fees.tips;
199                if !amount.is_zero() {
200                    Self::deposit_event(Event::<T>::BurnedBlockFees {
201                        storage: collected_fees.storage,
202                        compute: collected_fees.compute,
203                        tips: collected_fees.tips,
204                    });
205                }
206            }
207        }
208    }
209
210    pub fn transaction_byte_fee() -> BalanceOf<T> {
214        if !T::DynamicCostOfStorage::get() {
215            return BalanceOf::<T>::from(1);
216        }
217
218        if IsDuringBlockExecution::<T>::get() {
219            TransactionByteFee::<T>::get().current
220        } else {
221            TransactionByteFee::<T>::get().next
222        }
223    }
224
225    pub fn calculate_transaction_byte_fee() -> BalanceOf<T> {
226        let credit_supply = T::CreditSupply::get();
227
228        match (T::TotalSpacePledged::get() / u128::from(T::MinReplicationFactor::get()))
229            .checked_sub(T::BlockchainHistorySize::get())
230        {
231            Some(free_space) if free_space > 0 => {
232                credit_supply / BalanceOf::<T>::saturated_from(free_space)
233            }
234            _ => credit_supply,
235        }
236    }
237
238    pub fn note_transaction_fees(
239        storage_fee: BalanceOf<T>,
240        compute_fee: BalanceOf<T>,
241        tip: BalanceOf<T>,
242    ) {
243        CollectedBlockFees::<T>::mutate(|collected_block_fees| {
244            if let Some(collected_block_fees) = collected_block_fees.as_mut() {
247                collected_block_fees.storage += storage_fee;
248                collected_block_fees.compute += compute_fee;
249                collected_block_fees.tips += tip;
250            }
251        });
252    }
253}
254
255impl<T: Config> subspace_runtime_primitives::StorageFee<BalanceOf<T>> for Pallet<T>
256where
257    BalanceOf<T>: From<u64>,
258{
259    fn transaction_byte_fee() -> BalanceOf<T> {
260        Self::transaction_byte_fee()
261    }
262
263    fn note_storage_fees(storage_fee: BalanceOf<T>) {
264        Self::note_transaction_fees(storage_fee, Zero::zero(), Zero::zero())
265    }
266}