pallet_dynamic_fee/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
20#![warn(unused_crate_dependencies)]
21
22#[cfg(test)]
23mod tests;
24
25use core::cmp::{max, min};
26use frame_support::{inherent::IsFatalError, traits::Get, weights::Weight};
27use sp_core::U256;
28use sp_inherents::{InherentData, InherentIdentifier};
29
30pub use self::pallet::*;
31#[cfg(feature = "std")]
32pub use fp_dynamic_fee::InherentDataProvider;
33pub use fp_dynamic_fee::{InherentType, INHERENT_IDENTIFIER};
34
35#[frame_support::pallet]
36pub mod pallet {
37 use super::*;
38 use frame_support::pallet_prelude::*;
39 use frame_system::pallet_prelude::*;
40
41 #[pallet::pallet]
42 pub struct Pallet<T>(PhantomData<T>);
43
44 #[pallet::config]
45 pub trait Config: frame_system::Config {
46 type MinGasPriceBoundDivisor: Get<U256>;
48 }
49
50 #[pallet::hooks]
51 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
52 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
53 TargetMinGasPrice::<T>::kill();
54
55 T::DbWeight::get().writes(1)
56 }
57
58 fn on_finalize(_n: BlockNumberFor<T>) {
59 if let Some(target) = TargetMinGasPrice::<T>::take() {
60 let bound =
61 MinGasPrice::<T>::get() / T::MinGasPriceBoundDivisor::get() + U256::one();
62
63 let upper_limit = MinGasPrice::<T>::get().saturating_add(bound);
64 let lower_limit = MinGasPrice::<T>::get().saturating_sub(bound);
65
66 MinGasPrice::<T>::set(min(upper_limit, max(lower_limit, target)));
67 }
68 }
69 }
70
71 #[pallet::call]
72 impl<T: Config> Pallet<T> {
73 #[pallet::call_index(0)]
74 #[pallet::weight((T::DbWeight::get().writes(1), DispatchClass::Mandatory))]
75 pub fn note_min_gas_price_target(origin: OriginFor<T>, target: U256) -> DispatchResult {
76 ensure_none(origin)?;
77 assert!(
78 TargetMinGasPrice::<T>::get().is_none(),
79 "TargetMinGasPrice must be updated only once in the block",
80 );
81
82 TargetMinGasPrice::<T>::set(Some(target));
83 Ok(())
84 }
85 }
86
87 #[pallet::genesis_config]
88 #[derive(frame_support::DefaultNoBound)]
89 pub struct GenesisConfig<T> {
90 pub min_gas_price: U256,
91 #[serde(skip)]
92 pub _marker: PhantomData<T>,
93 }
94
95 #[pallet::genesis_build]
96 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
97 fn build(&self) {
98 MinGasPrice::<T>::put(self.min_gas_price);
99 }
100 }
101
102 #[pallet::storage]
103 pub type MinGasPrice<T: Config> = StorageValue<_, U256, ValueQuery>;
104
105 #[pallet::storage]
106 pub type TargetMinGasPrice<T: Config> = StorageValue<_, U256>;
107
108 #[derive(Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq)]
109 pub enum InherentError {
110 TargetGasPriceTooHigh,
112 TargetGasPriceTooLow,
114 TargetGasPriceZero,
116 }
117
118 impl IsFatalError for InherentError {
119 fn is_fatal_error(&self) -> bool {
120 true
122 }
123 }
124
125 #[pallet::inherent]
126 impl<T: Config> ProvideInherent for Pallet<T> {
127 type Call = Call<T>;
128 type Error = InherentError;
129 const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
130
131 fn create_inherent(data: &InherentData) -> Option<Self::Call> {
132 let target = data.get_data::<InherentType>(&INHERENT_IDENTIFIER).ok()??;
133
134 Some(Call::note_min_gas_price_target { target })
135 }
136
137 fn check_inherent(call: &Self::Call, _data: &InherentData) -> Result<(), Self::Error> {
138 if let Call::note_min_gas_price_target { target } = call {
139 if target.is_zero() {
141 return Err(InherentError::TargetGasPriceZero);
142 }
143
144 let current_gas_price = MinGasPrice::<T>::get();
146
147 let bound = current_gas_price / T::MinGasPriceBoundDivisor::get() + U256::one();
149
150 let upper_limit = current_gas_price.saturating_add(bound);
152 let lower_limit = current_gas_price.saturating_sub(bound);
153
154 if *target > upper_limit {
156 return Err(InherentError::TargetGasPriceTooHigh);
157 }
158 if *target < lower_limit {
159 return Err(InherentError::TargetGasPriceTooLow);
160 }
161 }
162
163 Ok(())
164 }
165
166 fn is_inherent(call: &Self::Call) -> bool {
167 matches!(call, Call::note_min_gas_price_target { .. })
168 }
169 }
170}
171
172impl<T: Config> fp_evm::FeeCalculator for Pallet<T> {
173 fn min_gas_price() -> (U256, Weight) {
174 (MinGasPrice::<T>::get(), T::DbWeight::get().reads(1))
175 }
176}