1234567891011121314151617#![cfg_attr(not(feature = "std"), no_std)]1819use core::marker::PhantomData;2021use frame_support::{22 pallet,23 weights::{WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient, Weight},24 traits::Get,25};26use parity_scale_codec::{Decode, Encode, MaxEncodedLen};27use scale_info::TypeInfo;28use sp_arithmetic::traits::{BaseArithmetic, Unsigned};29use smallvec::smallvec;3031pub use pallet::*;32use sp_core::U256;33use sp_runtime::Perbill;3435#[pallet]36mod pallet {37 use super::*;38 use frame_support::{39 traits::{Get, ReservableCurrency, Currency},40 pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType, OptionQuery},41 BoundedVec, log,42 };43 use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig};44 use xcm::v1::MultiLocation;4546 pub type BalanceOf<T> =47 <<T as Config>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;4849 #[pallet::config]50 pub trait Config: frame_system::Config {51 52 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;5354 55 type Currency: ReservableCurrency<Self::AccountId>;5657 #[pallet::constant]58 type DefaultWeightToFeeCoefficient: Get<u32>;5960 #[pallet::constant]61 type DefaultMinGasPrice: Get<u64>;6263 #[pallet::constant]64 type MaxXcmAllowedLocations: Get<u32>;65 #[pallet::constant]66 type AppPromotionDailyRate: Get<Perbill>;67 #[pallet::constant]68 type DayRelayBlocks: Get<Self::BlockNumber>;6970 #[pallet::constant]71 type DefaultCollatorSelectionMaxCollators: Get<u32>;72 #[pallet::constant]73 type DefaultCollatorSelectionLicenseBond: Get<BalanceOf<Self>>;74 #[pallet::constant]75 type DefaultCollatorSelectionKickThreshold: Get<Self::BlockNumber>;76 }7778 #[pallet::event]79 #[pallet::generate_deposit(pub(super) fn deposit_event)]80 pub enum Event<T: Config> {81 NewDesiredCollators {82 desired_collators: Option<u32>,83 },84 NewCollatorLicenseBond {85 bond_cost: Option<BalanceOf<T>>,86 },87 NewCollatorKickThreshold {88 length_in_blocks: Option<T::BlockNumber>,89 },90 }9192 #[pallet::error]93 pub enum Error<T> {94 InconsistentConfiguration,95 }9697 #[pallet::storage]98 pub type WeightToFeeCoefficientOverride<T: Config> = StorageValue<99 Value = u32,100 QueryKind = ValueQuery,101 OnEmpty = T::DefaultWeightToFeeCoefficient,102 >;103104 #[pallet::storage]105 pub type MinGasPriceOverride<T: Config> =106 StorageValue<Value = u64, QueryKind = ValueQuery, OnEmpty = T::DefaultMinGasPrice>;107108 #[pallet::storage]109 pub type XcmAllowedLocationsOverride<T: Config> = StorageValue<110 Value = BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>,111 QueryKind = OptionQuery,112 >;113114 #[pallet::storage]115 pub type AppPromomotionConfigurationOverride<T: Config> =116 StorageValue<Value = AppPromotionConfiguration<T::BlockNumber>, QueryKind = ValueQuery>;117118 #[pallet::storage]119 pub type CollatorSelectionDesiredCollatorsOverride<T: Config> = StorageValue<120 Value = u32,121 QueryKind = ValueQuery,122 OnEmpty = T::DefaultCollatorSelectionMaxCollators,123 >;124125 #[pallet::storage]126 pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<127 Value = BalanceOf<T>,128 QueryKind = ValueQuery,129 OnEmpty = T::DefaultCollatorSelectionLicenseBond,130 >;131132 #[pallet::storage]133 pub type CollatorSelectionKickThresholdOverride<T: Config> = StorageValue<134 Value = T::BlockNumber,135 QueryKind = ValueQuery,136 OnEmpty = T::DefaultCollatorSelectionKickThreshold,137 >;138139 #[pallet::call]140 impl<T: Config> Pallet<T> {141 #[pallet::weight(T::DbWeight::get().writes(1))]142 pub fn set_weight_to_fee_coefficient_override(143 origin: OriginFor<T>,144 coeff: Option<u32>,145 ) -> DispatchResult {146 ensure_root(origin)?;147 if let Some(coeff) = coeff {148 <WeightToFeeCoefficientOverride<T>>::set(coeff);149 } else {150 <WeightToFeeCoefficientOverride<T>>::kill();151 }152 Ok(())153 }154155 #[pallet::weight(T::DbWeight::get().writes(1))]156 pub fn set_min_gas_price_override(157 origin: OriginFor<T>,158 coeff: Option<u64>,159 ) -> DispatchResult {160 ensure_root(origin)?;161 if let Some(coeff) = coeff {162 <MinGasPriceOverride<T>>::set(coeff);163 } else {164 <MinGasPriceOverride<T>>::kill();165 }166 Ok(())167 }168169 #[pallet::weight(T::DbWeight::get().writes(1))]170 pub fn set_xcm_allowed_locations(171 origin: OriginFor<T>,172 locations: Option<BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>>,173 ) -> DispatchResult {174 ensure_root(origin)?;175 <XcmAllowedLocationsOverride<T>>::set(locations);176 Ok(())177 }178179 #[pallet::weight(T::DbWeight::get().writes(1))]180 pub fn set_app_promotion_configuration_override(181 origin: OriginFor<T>,182 mut configuration: AppPromotionConfiguration<T::BlockNumber>,183 ) -> DispatchResult {184 ensure_root(origin)?;185 if configuration.interval_income.is_some() {186 return Err(<Error<T>>::InconsistentConfiguration.into());187 }188189 configuration.interval_income = configuration.recalculation_interval.map(|b| {190 Perbill::from_rational(b, T::DayRelayBlocks::get())191 * T::AppPromotionDailyRate::get()192 });193194 <AppPromomotionConfigurationOverride<T>>::set(configuration);195196 Ok(())197 }198199 #[pallet::weight(T::DbWeight::get().writes(1))]200 pub fn set_collator_selection_desired_collators(201 origin: OriginFor<T>,202 max: Option<u32>,203 ) -> DispatchResult {204 ensure_root(origin)?;205 if let Some(max) = max {206 207 if max > T::DefaultCollatorSelectionMaxCollators::get() {208 log::warn!("max > T::DefaultCollatorSelectionMaxCollators; you might need to run benchmarks again");209 }210 <CollatorSelectionDesiredCollatorsOverride<T>>::set(max);211 } else {212 <CollatorSelectionDesiredCollatorsOverride<T>>::kill();213 }214 Self::deposit_event(Event::NewDesiredCollators { desired_collators: max });215 Ok(())216 }217218 #[pallet::weight(T::DbWeight::get().writes(1))]219 pub fn set_collator_selection_license_bond(220 origin: OriginFor<T>,221 amount: Option<BalanceOf<T>>,222 ) -> DispatchResult {223 ensure_root(origin)?;224 if let Some(amount) = amount {225 <CollatorSelectionLicenseBondOverride<T>>::set(amount);226 } else {227 <CollatorSelectionLicenseBondOverride<T>>::kill();228 }229 Self::deposit_event(Event::NewCollatorLicenseBond { bond_cost: amount });230 Ok(())231 }232233 #[pallet::weight(T::DbWeight::get().writes(1))]234 pub fn set_collator_selection_kick_threshold(235 origin: OriginFor<T>,236 threshold: Option<T::BlockNumber>,237 ) -> DispatchResult {238 ensure_root(origin)?;239 if let Some(threshold) = threshold {240 <CollatorSelectionKickThresholdOverride<T>>::set(threshold);241 } else {242 <CollatorSelectionKickThresholdOverride<T>>::kill();243 }244 Self::deposit_event(Event::NewCollatorKickThreshold { length_in_blocks: threshold });245 Ok(())246 }247 }248249 #[pallet::pallet]250 #[pallet::generate_store(pub(super) trait Store)]251 pub struct Pallet<T>(_);252}253254pub struct WeightToFee<T, B>(PhantomData<(T, B)>);255256impl<T, B> WeightToFeePolynomial for WeightToFee<T, B>257where258 T: Config,259 B: BaseArithmetic + From<u32> + Copy + Unsigned,260{261 type Balance = B;262263 fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {264 smallvec!(WeightToFeeCoefficient {265 coeff_integer: <WeightToFeeCoefficientOverride<T>>::get().into(),266 coeff_frac: Perbill::zero(),267 negative: false,268 degree: 1,269 })270 }271}272273pub struct FeeCalculator<T>(PhantomData<T>);274impl<T: Config> fp_evm::FeeCalculator for FeeCalculator<T> {275 fn min_gas_price() -> (U256, Weight) {276 (277 <MinGasPriceOverride<T>>::get().into(),278 T::DbWeight::get().reads(1),279 )280 }281}282283#[derive(Encode, Decode, Clone, Debug, Default, TypeInfo, MaxEncodedLen, PartialEq, PartialOrd)]284pub struct AppPromotionConfiguration<BlockNumber> {285 286 pub recalculation_interval: Option<BlockNumber>,287 288 pub pending_interval: Option<BlockNumber>,289 290 pub interval_income: Option<Perbill>,291 292 pub max_stakers_per_calculation: Option<u8>,293}