use super::*;
use frame_support::{BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{CheckedAdd, CheckedSub, Saturating, Zero},
RuntimeDebug,
};
#[cfg(any(feature = "runtime-benchmarks", test))]
use sp_std::vec::Vec;
#[derive(
Clone, Copy, Debug, Decode, Encode, TypeInfo, Eq, MaxEncodedLen, PartialEq, PartialOrd,
)]
pub enum StakingType {
MaximumCapacity,
ProviderBoost,
}
#[derive(
TypeInfo, RuntimeDebugNoBound, PartialEqNoBound, EqNoBound, Clone, Decode, Encode, MaxEncodedLen,
)]
#[scale_info(skip_type_params(T))]
pub struct StakingDetails<T: Config> {
pub active: BalanceOf<T>,
pub staking_type: StakingType,
}
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct UnlockChunk<Balance, EpochNumber> {
pub value: Balance,
pub thaw_at: EpochNumber,
}
impl<T: Config> StakingDetails<T> {
pub fn deposit(&mut self, amount: BalanceOf<T>) -> Option<()> {
self.active = amount.checked_add(&self.active)?;
Some(())
}
pub fn withdraw(&mut self, amount: BalanceOf<T>) -> Result<BalanceOf<T>, DispatchError> {
let current_active = self.active;
let mut new_active = self.active.saturating_sub(amount);
let mut actual_unstaked: BalanceOf<T> = amount;
if new_active.le(&T::MinimumStakingAmount::get()) {
actual_unstaked = current_active;
new_active = Zero::zero();
}
self.active = new_active;
Ok(actual_unstaked)
}
}
impl<T: Config> Default for StakingDetails<T> {
fn default() -> Self {
Self { active: Zero::zero(), staking_type: StakingType::MaximumCapacity }
}
}
#[derive(PartialEq, Eq, Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct StakingTargetDetails<Balance> {
pub amount: Balance,
pub capacity: Balance,
}
impl<Balance: Saturating + Copy + CheckedAdd> StakingTargetDetails<Balance> {
pub fn deposit(&mut self, amount: Balance, capacity: Balance) -> Option<()> {
self.amount = amount.checked_add(&self.amount)?;
self.capacity = capacity.checked_add(&self.capacity)?;
Some(())
}
pub fn withdraw(&mut self, amount: Balance, capacity: Balance) {
self.amount = self.amount.saturating_sub(amount);
self.capacity = self.capacity.saturating_sub(capacity);
}
}
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct CapacityDetails<Balance, EpochNumber> {
pub remaining_capacity: Balance,
pub total_tokens_staked: Balance,
pub total_capacity_issued: Balance,
pub last_replenished_epoch: EpochNumber,
}
impl<Balance, EpochNumber> CapacityDetails<Balance, EpochNumber>
where
Balance: Saturating + Copy + CheckedAdd + CheckedSub,
EpochNumber: Clone + PartialOrd + PartialEq,
{
pub fn deposit(&mut self, amount: &Balance, capacity: &Balance) -> Option<()> {
self.total_tokens_staked = amount.checked_add(&self.total_tokens_staked)?;
self.remaining_capacity = capacity.checked_add(&self.remaining_capacity)?;
self.total_capacity_issued = capacity.checked_add(&self.total_capacity_issued)?;
Some(())
}
pub fn can_replenish(&self, current_epoch: EpochNumber) -> bool {
self.last_replenished_epoch.lt(¤t_epoch)
}
pub fn replenish_all(&mut self, current_epoch: &EpochNumber) {
self.remaining_capacity = self.total_capacity_issued;
self.last_replenished_epoch = current_epoch.clone();
}
pub fn replenish_by_amount(&mut self, amount: Balance, current_epoch: &EpochNumber) {
self.remaining_capacity = amount.saturating_add(self.remaining_capacity);
self.last_replenished_epoch = current_epoch.clone();
}
pub fn deduct_capacity_by_amount(&mut self, amount: Balance) -> Result<(), ArithmeticError> {
let new_remaining =
self.remaining_capacity.checked_sub(&amount).ok_or(ArithmeticError::Underflow)?;
self.remaining_capacity = new_remaining;
Ok(())
}
pub fn withdraw(&mut self, capacity_deduction: Balance, tokens_staked_deduction: Balance) {
self.total_tokens_staked = self.total_tokens_staked.saturating_sub(tokens_staked_deduction);
self.total_capacity_issued = self.total_capacity_issued.saturating_sub(capacity_deduction);
self.remaining_capacity = self.remaining_capacity.saturating_sub(capacity_deduction);
}
}
#[derive(
PartialEq, Eq, Clone, Default, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
)]
pub struct EpochInfo<BlockNumber> {
pub epoch_start: BlockNumber,
}
pub type UnlockChunkList<T> = BoundedVec<
UnlockChunk<BalanceOf<T>, <T as Config>::EpochNumber>,
<T as Config>::MaxUnlockingChunks,
>;
pub fn unlock_chunks_total<T: Config>(unlock_chunks: &UnlockChunkList<T>) -> BalanceOf<T> {
unlock_chunks
.iter()
.fold(Zero::zero(), |acc: BalanceOf<T>, chunk| acc.saturating_add(chunk.value))
}
pub fn unlock_chunks_reap_thawed<T: Config>(
unlock_chunks: &mut UnlockChunkList<T>,
current_epoch: <T>::EpochNumber,
) -> BalanceOf<T> {
let mut total_reaped: BalanceOf<T> = 0u32.into();
unlock_chunks.retain(|chunk| {
if current_epoch.ge(&chunk.thaw_at) {
total_reaped = total_reaped.saturating_add(chunk.value);
false
} else {
true
}
});
total_reaped
}
#[cfg(any(feature = "runtime-benchmarks", test))]
#[allow(clippy::unwrap_used)]
pub fn unlock_chunks_from_vec<T: Config>(chunks: &Vec<(u32, u32)>) -> UnlockChunkList<T> {
let result: Vec<UnlockChunk<BalanceOf<T>, <T>::EpochNumber>> = chunks
.into_iter()
.map(|chunk| UnlockChunk { value: chunk.0.into(), thaw_at: chunk.1.into() })
.collect();
BoundedVec::try_from(result).unwrap()
}