1#![cfg_attr(not(feature = "std"), no_std)]
19#![expect(clippy::clone_on_copy, reason = "Comes from Substrate")]
21
22#[cfg(not(feature = "std"))]
23extern crate alloc;
24
25pub mod fees;
26
27pub use pallet::*;
28
29#[frame_support::pallet]
30mod pallet {
31 #[cfg(not(feature = "std"))]
32 use alloc::vec::Vec;
33 use frame_support::pallet_prelude::*;
34 use frame_support::storage::generator::StorageValue as _;
35 use frame_system::pallet_prelude::*;
36 use parity_scale_codec::{Codec, MaxEncodedLen};
37 use scale_info::TypeInfo;
38 use sp_block_fees::{INHERENT_IDENTIFIER, InherentError, InherentType};
39 use sp_domains::ChainId;
40 use sp_domains::execution_receipt::BlockFees;
41 use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Saturating};
42 use sp_runtime::{FixedPointOperand, SaturatedConversion};
43 use sp_std::fmt::Debug;
44 use sp_std::result;
45
46 #[pallet::config]
47 pub trait Config: frame_system::Config {
48 type Balance: Parameter
50 + Member
51 + AtLeast32BitUnsigned
52 + Codec
53 + Default
54 + Copy
55 + MaybeSerializeDeserialize
56 + Debug
57 + MaxEncodedLen
58 + TypeInfo
59 + FixedPointOperand;
60
61 type DomainChainByteFee: Get<Self::Balance>;
63 }
64
65 #[pallet::storage]
70 #[pallet::getter(fn collected_block_fees)]
71 pub(super) type CollectedBlockFees<T: Config> =
72 StorageValue<_, BlockFees<T::Balance>, ValueQuery>;
73
74 #[pallet::storage]
79 #[pallet::getter(fn consensus_chain_byte_fee)]
80 pub(super) type ConsensusChainByteFee<T: Config> = StorageValue<_, T::Balance, ValueQuery>;
81
82 #[pallet::storage]
86 pub(super) type NextConsensusChainByteFee<T: Config> = StorageValue<_, T::Balance, ValueQuery>;
87
88 #[pallet::pallet]
90 #[pallet::without_storage_info]
91 pub struct Pallet<T>(_);
92
93 #[pallet::hooks]
94 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
95 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
96 CollectedBlockFees::<T>::set(BlockFees::<T::Balance>::default());
100 T::DbWeight::get().writes(1)
101 }
102
103 fn on_finalize(_now: BlockNumberFor<T>) {
104 let transaction_byte_fee = NextConsensusChainByteFee::<T>::take();
105 ConsensusChainByteFee::<T>::put(transaction_byte_fee);
106 }
107 }
108
109 #[pallet::call]
110 impl<T: Config> Pallet<T> {
111 #[pallet::call_index(0)]
112 #[pallet::weight((
113 Weight::from_all(10_000).saturating_add(
119 T::DbWeight::get().writes(1)
120 ),
121 DispatchClass::Mandatory
122 ))]
123 pub fn set_next_consensus_chain_byte_fee(
124 origin: OriginFor<T>,
125 #[pallet::compact] transaction_byte_fee: T::Balance,
126 ) -> DispatchResult {
127 ensure_none(origin)?;
128 NextConsensusChainByteFee::<T>::put(transaction_byte_fee);
129 Ok(())
130 }
131 }
132
133 #[pallet::inherent]
134 impl<T: Config> ProvideInherent for Pallet<T> {
135 type Call = Call<T>;
136 type Error = InherentError;
137 const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
138
139 fn create_inherent(data: &InherentData) -> Option<Self::Call> {
140 let inherent_data = data
141 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
142 .expect("Domain block fees inherent data not correctly encoded")
143 .expect("Domain block fees inherent data must be provided");
144
145 let transaction_byte_fee = inherent_data.saturated_into::<T::Balance>();
146
147 Some(Call::set_next_consensus_chain_byte_fee {
148 transaction_byte_fee,
149 })
150 }
151
152 fn check_inherent(
153 call: &Self::Call,
154 data: &InherentData,
155 ) -> result::Result<(), Self::Error> {
156 let inherent_data = data
157 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
158 .expect("Domain block fees inherent data not correctly encoded")
159 .expect("Domain block fees inherent data must be provided");
160
161 let provided_transaction_byte_fee = inherent_data.saturated_into::<T::Balance>();
162
163 if let Call::set_next_consensus_chain_byte_fee {
164 transaction_byte_fee,
165 } = call
166 && transaction_byte_fee != &provided_transaction_byte_fee
167 {
168 return Err(InherentError::IncorrectConsensusChainByteFee);
169 }
170
171 Ok(())
172 }
173
174 fn is_inherent(call: &Self::Call) -> bool {
175 matches!(call, Call::set_next_consensus_chain_byte_fee { .. })
176 }
177 }
178
179 impl<T: Config> Pallet<T> {
180 pub fn note_domain_execution_fee(rewards: T::Balance) {
183 CollectedBlockFees::<T>::mutate(|block_fees| {
184 block_fees.domain_execution_fee =
185 block_fees.domain_execution_fee.saturating_add(rewards);
186 });
187 }
188
189 pub fn note_consensus_storage_fee(storage_fee: T::Balance) {
191 CollectedBlockFees::<T>::mutate(|block_fees| {
192 block_fees.consensus_storage_fee =
193 block_fees.consensus_storage_fee.saturating_add(storage_fee);
194 });
195 }
196
197 pub fn note_burned_balance(burned_balance: T::Balance) {
199 CollectedBlockFees::<T>::mutate(|block_fees| {
200 block_fees.burned_balance =
201 block_fees.burned_balance.saturating_add(burned_balance);
202 });
203 }
204
205 pub fn note_chain_rewards(chain_id: ChainId, balance: T::Balance) {
207 CollectedBlockFees::<T>::mutate(|block_fees| {
208 let total_balance = match block_fees.chain_rewards.get(&chain_id) {
209 None => balance,
210 Some(prev_balance) => prev_balance.saturating_add(balance),
211 };
212 block_fees.chain_rewards.insert(chain_id, total_balance)
213 });
214 }
215
216 pub fn final_domain_transaction_byte_fee() -> T::Balance {
222 ConsensusChainByteFee::<T>::get().saturating_add(T::DomainChainByteFee::get())
223 }
224
225 pub fn block_fees_storage_key() -> Vec<u8> {
226 CollectedBlockFees::<T>::storage_value_final_key().to_vec()
227 }
228 }
229}