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