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::traits::Zero;
10use frame_support::sp_runtime::SaturatedConversion;
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}