#![cfg_attr(not(feature = "std"), no_std)]
#![doc = include_str!("../README.md")]
use frame_support::{
pallet_prelude::{EnsureOrigin, IsType},
traits::Get,
};
use frame_system::pallet_prelude::{BlockNumberFor, OriginFor};
pub use pallet::*;
use sp_runtime::{DispatchError, DispatchResult, SaturatedConversion};
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use weights::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod types;
pub use types::*;
pub mod extensions;
pub use extensions::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type StatusOrigin: EnsureOrigin<Self::RuntimeOrigin>;
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type MaxTxByPeriod: Get<u32>;
type MaxSizeByPeriod: Get<u32>;
type Period: Get<u32>;
type WeightInfo: WeightInfo;
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
StatusChanged { who: T::AccountId, status: Status },
}
#[pallet::error]
pub enum Error<T> {
StatusNotChanged,
}
#[pallet::call]
impl<T: Config> Pallet<T>
where
T: frame_system::Config<AccountData = AccountData<T::Balance, BlockNumberFor<T>>>
+ Config
+ pallet_balances::Config,
{
#[pallet::call_index(0)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::set_status())]
pub fn set_status(
origin: OriginFor<T>,
who: T::AccountId,
status: Status,
) -> DispatchResult {
T::StatusOrigin::ensure_origin(origin)?;
Self::deposit_event(Event::StatusChanged {
who: who.clone(),
status: status.clone(),
});
frame_system::Account::<T>::try_mutate_exists(who.clone(), |account| {
if let Some(ref mut account) = account {
account.data.rate.status = status.clone();
Ok(())
} else {
Err(Error::<T>::StatusNotChanged.into())
}
})
}
}
}
impl<T> frame_support::traits::StoredMap<T::AccountId, pallet_balances::AccountData<T::Balance>>
for Pallet<T>
where
T: frame_system::Config<AccountData = AccountData<T::Balance, BlockNumberFor<T>>>
+ pallet_balances::Config,
{
fn get(k: &T::AccountId) -> pallet_balances::AccountData<T::Balance> {
frame_system::Account::<T>::get(k).data.balance
}
fn try_mutate_exists<R, E: From<DispatchError>>(
k: &T::AccountId,
f: impl FnOnce(&mut Option<pallet_balances::AccountData<T::Balance>>) -> Result<R, E>,
) -> Result<R, E> {
let account = frame_system::Account::<T>::get(k);
let is_default =
account.data.balance == pallet_balances::AccountData::<T::Balance>::default();
let mut some_data = if is_default {
None
} else {
Some(account.data.balance)
};
let result = f(&mut some_data)?;
if frame_system::Pallet::<T>::providers(k) > 0
|| frame_system::Pallet::<T>::sufficients(k) > 0
{
frame_system::Account::<T>::mutate(k, |a| {
a.data.balance = some_data.unwrap_or_default()
});
} else {
frame_system::Account::<T>::remove(k)
}
Ok(result)
}
}
impl<T> RateLimiter<T> for AccountData<T::Balance, BlockNumberFor<T>>
where
T: frame_system::Config<AccountData = AccountData<T::Balance, BlockNumberFor<T>>>
+ Config
+ pallet_balances::Config,
{
fn is_allowed(&self, b: BlockNumberFor<T>, len: u32) -> bool {
if self.rate.status == Status::Unlimited {
true
} else if (b - self.rate.last_block).saturated_into::<u32>() < T::Period::get() {
self.rate.tx_since_last < T::MaxTxByPeriod::get()
&& self.rate.size_since_last.saturating_add(len) < T::MaxSizeByPeriod::get()
} else {
len < T::MaxSizeByPeriod::get()
}
}
fn update_rate(&mut self, b: BlockNumberFor<T>, len: u32) {
if (b - self.rate.last_block).saturated_into::<u32>() < T::Period::get() {
self.rate.tx_since_last += 1;
self.rate.size_since_last += len;
} else {
self.rate.tx_since_last = 1;
self.rate.size_since_last = len;
self.rate.last_block = b;
}
}
}