pallet_block_producer_fees/
lib.rs

1//! Pallet to store Block Producer Fees settings that are relevant to rewards payments.
2//!
3//! Margin fee is percent of block rewards that will be paid to the block producer before
4//! distributing the rest of rewards to his stakers. Precision of the margin fee setting is bounded
5//! to 1/100 of a percent.
6//!
7//! The margin fees are stored together with the slot at which change occurred, so this data can be
8//! exposed to rewards calculation.
9//!
10//! Log of changes per account is bounded. The oldest entries are dropped when new ones are added.
11//! Intention is to discourage users from too frequent changes and there is an assumption
12//! that rewards calculation algorithm will account for it.
13
14#![cfg_attr(not(feature = "std"), no_std)]
15#![deny(missing_docs)]
16
17pub mod benchmarking;
18
19#[cfg(test)]
20mod mock;
21#[cfg(test)]
22mod tests;
23pub mod weights;
24
25pub use pallet::*;
26pub use weights::WeightInfo;
27
28#[frame_support::pallet]
29pub mod pallet {
30	use super::*;
31	use frame_support::pallet_prelude::*;
32	use frame_system::pallet_prelude::*;
33	use sp_block_producer_fees::PerTenThousands;
34	use sp_consensus_slots::Slot;
35	use sp_std::collections::vec_deque::VecDeque;
36
37	/// Current version of the pallet
38	pub const PALLET_VERSION: u32 = 1;
39
40	#[pallet::pallet]
41	pub struct Pallet<T>(_);
42
43	#[pallet::config]
44	pub trait Config: frame_system::Config {
45		/// The maximum number of past changes per one block producer kept in the storage.
46		#[pallet::constant]
47		type HistoricalChangesPerProducer: Get<u16>;
48
49		/// Weight information on extrinsic in the pallet. For convenience weights in [weights] module can be used.
50		type WeightInfo: WeightInfo;
51
52		/// Should provide the slot number of the current block.
53		fn current_slot() -> Slot;
54
55		#[cfg(feature = "runtime-benchmarks")]
56		/// Benchmark helper type used for running benchmarks
57		type BenchmarkHelper: benchmarking::BenchmarkHelper<Self::AccountId>;
58	}
59
60	type FeeChange = (Slot, PerTenThousands);
61
62	/// Stores bounded amount of fee changes per account
63	#[pallet::storage]
64	#[pallet::unbounded]
65	pub type FeesChanges<T: Config> = StorageMap<
66		Hasher = Twox64Concat,
67		Key = T::AccountId,
68		Value = VecDeque<FeeChange>,
69		QueryKind = ValueQuery,
70	>;
71
72	#[pallet::call]
73	impl<T: Config> Pallet<T> {
74		/// Sets the margin fee of a caller. Margin fee is (fee numerator / 10000).
75		#[pallet::call_index(0)]
76		#[pallet::weight((T::WeightInfo::set_fee(), DispatchClass::Normal))]
77		pub fn set_fee(origin: OriginFor<T>, fee_numerator: PerTenThousands) -> DispatchResult {
78			let account_id = ensure_signed(origin)?;
79			if fee_numerator > 10000 {
80				return Err(DispatchError::Other("fee numerator must be in range from 0 to 10000"));
81			}
82			FeesChanges::<T>::mutate(account_id, |fees_log| {
83				if fees_log.len() > T::HistoricalChangesPerProducer::get().into() {
84					let _ = fees_log.pop_back();
85				}
86				fees_log.push_front((T::current_slot(), fee_numerator));
87			});
88			Ok(())
89		}
90	}
91
92	impl<T: Config> Pallet<T> {
93		/// Returns the current pallet version.
94		pub fn get_version() -> u32 {
95			PALLET_VERSION
96		}
97
98		/// Retrieves all stored block producer fees settings. The most recent fees are in front of vecdeque.
99		pub fn get_all() -> impl Iterator<Item = (T::AccountId, VecDeque<FeeChange>)> {
100			FeesChanges::<T>::iter()
101		}
102
103		/// Retrieves the latest fee settings for all accounts.
104		pub fn get_all_latest() -> impl Iterator<Item = (T::AccountId, FeeChange)> {
105			Self::get_all().map(|(account_id, changes)| {
106				(account_id, *changes.front().expect("There are no empty collections in storage"))
107			})
108		}
109
110		/// Retrieves fees settings for the given account id.
111		/// Empty collection is returned if there are no settings stored for given id.
112		pub fn get(id: T::AccountId) -> VecDeque<FeeChange> {
113			FeesChanges::<T>::get(id)
114		}
115
116		/// Gets the latest fee setting for the given account.
117		pub fn get_latest(id: T::AccountId) -> Option<FeeChange> {
118			FeesChanges::<T>::get(id).front().cloned()
119		}
120	}
121}