pallet_dynamic_fee/
lib.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18// Ensure we're `no_std` when compiling for Wasm.
19#![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		/// Bound divisor for min gas price.
47		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		/// The target gas price is too high compared to the current gas price.
111		TargetGasPriceTooHigh,
112		/// The target gas price is too low compared to the current gas price.
113		TargetGasPriceTooLow,
114		/// The target gas price is zero, which is not allowed.
115		TargetGasPriceZero,
116	}
117
118	impl IsFatalError for InherentError {
119		fn is_fatal_error(&self) -> bool {
120			// All inherent errors are fatal as they indicate invalid block data
121			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				// Check that target is not zero
140				if target.is_zero() {
141					return Err(InherentError::TargetGasPriceZero);
142				}
143
144				// Get current gas price
145				let current_gas_price = MinGasPrice::<T>::get();
146
147				// Calculate the bound for validation
148				let bound = current_gas_price / T::MinGasPriceBoundDivisor::get() + U256::one();
149
150				// Calculate upper and lower limits for validation
151				let upper_limit = current_gas_price.saturating_add(bound);
152				let lower_limit = current_gas_price.saturating_sub(bound);
153
154				// Validate that target is within the allowed bounds
155				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}