1use super::{oracle::CurrencyAmount, Balance, BlockNumber};
20
21use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
22
23use frame_support::pallet_prelude::{RuntimeDebug, Weight};
24use sp_arithmetic::ArithmeticError;
25use sp_core::{DecodeWithMemTracking, H160};
26use sp_runtime::{
27 traits::{UniqueSaturatedInto, Zero},
28 FixedPointNumber,
29};
30use sp_std::hash::Hash;
31
32pub type EraNumber = u32;
34pub type PeriodNumber = u32;
36pub type DAppId = u16;
38pub type TierId = u8;
40pub type Rank = u8;
42
43pub trait CycleConfiguration {
51 fn periods_per_cycle() -> PeriodNumber;
55
56 fn eras_per_voting_subperiod() -> EraNumber;
60
61 fn eras_per_build_and_earn_subperiod() -> EraNumber;
65
66 fn blocks_per_era() -> BlockNumber;
70
71 fn period_in_era_lengths() -> EraNumber {
73 Self::eras_per_voting_subperiod().saturating_add(Self::eras_per_build_and_earn_subperiod())
74 }
75
76 fn cycle_in_era_lengths() -> EraNumber {
78 Self::period_in_era_lengths().saturating_mul(Self::periods_per_cycle())
79 }
80
81 fn blocks_per_cycle() -> BlockNumber {
83 Self::blocks_per_era().saturating_mul(Self::cycle_in_era_lengths())
84 }
85
86 fn build_and_earn_eras_per_cycle() -> EraNumber {
88 Self::eras_per_build_and_earn_subperiod().saturating_mul(Self::periods_per_cycle())
89 }
90
91 fn eras_per_period() -> EraNumber {
93 Self::eras_per_build_and_earn_subperiod().saturating_add(1)
94 }
95
96 fn eras_per_cycle() -> EraNumber {
98 Self::eras_per_period().saturating_mul(Self::periods_per_cycle())
99 }
100}
101
102pub trait Observer {
104 fn block_before_new_era(_next_era: EraNumber) -> Weight {
111 Weight::zero()
112 }
113}
114
115impl Observer for () {}
116
117pub trait StakingRewardHandler<AccountId> {
122 fn staker_and_dapp_reward_pools(total_value_staked: Balance) -> (Balance, Balance);
126
127 fn bonus_reward_pool() -> Balance;
129
130 fn payout_reward(beneficiary: &AccountId, reward: Balance) -> Result<(), ()>;
132}
133
134pub trait SmartContractHandle<AccountId> {
138 fn evm(address: H160) -> Self;
140 fn wasm(address: AccountId) -> Self;
142}
143
144#[derive(
146 PartialEq,
147 Eq,
148 Copy,
149 Clone,
150 Encode,
151 Decode,
152 DecodeWithMemTracking,
153 RuntimeDebug,
154 MaxEncodedLen,
155 Hash,
156 scale_info::TypeInfo,
157)]
158pub enum SmartContract<AccountId> {
159 Evm(H160),
161 Wasm(AccountId),
163}
164
165impl<AccountId> SmartContractHandle<AccountId> for SmartContract<AccountId> {
166 fn evm(address: H160) -> Self {
167 Self::Evm(address)
168 }
169
170 fn wasm(address: AccountId) -> Self {
171 Self::Wasm(address)
172 }
173}
174
175pub trait AccountCheck<AccountId> {
177 fn allowed_to_stake(account: &AccountId) -> bool;
179}
180
181impl<AccountId> AccountCheck<AccountId> for () {
182 fn allowed_to_stake(_account: &AccountId) -> bool {
183 true
184 }
185}
186
187pub trait TierSlots {
189 fn number_of_slots(price: CurrencyAmount, args: (u64, u64)) -> u16;
197}
198
199pub struct StandardTierSlots;
201impl TierSlots for StandardTierSlots {
202 fn number_of_slots(price: CurrencyAmount, args: (u64, u64)) -> u16 {
203 let result: u64 = price.saturating_mul_int(args.0).saturating_add(args.1);
204 result.unique_saturated_into()
205 }
206}
207
208pub const STANDARD_TIER_SLOTS_ARGS: (u64, u64) = (1000, 50);
211
212#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, MaxEncodedLen, scale_info::TypeInfo)]
216pub struct RankedTier(u8);
217
218impl RankedTier {
219 pub const MAX_RANK: u8 = 10;
220
221 pub fn new(tier: TierId, rank: Rank) -> Result<Self, ArithmeticError> {
224 if rank > Self::MAX_RANK || tier > 0xf {
225 return Err(ArithmeticError::Overflow);
226 }
227 Ok(Self(rank << 4 | tier & 0x0f))
228 }
229
230 pub fn new_saturated(tier: TierId, rank: Rank) -> Self {
232 Self(rank.min(Self::MAX_RANK) << 4 | tier.min(0xf) & 0x0f)
233 }
234
235 #[inline(always)]
236 pub fn tier(&self) -> TierId {
237 self.0 & 0x0f
238 }
239
240 #[inline(always)]
241 pub fn rank(&self) -> Rank {
242 (self.0 >> 4).min(Self::MAX_RANK)
243 }
244
245 #[inline(always)]
246 pub fn deconstruct(&self) -> (TierId, Rank) {
247 (self.tier(), self.rank())
248 }
249}
250
251impl core::fmt::Debug for RankedTier {
252 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
253 f.debug_struct("RankedTier")
254 .field("tier", &self.tier())
255 .field("rank", &self.rank())
256 .finish()
257 }
258}
259
260impl RankedTier {
261 pub fn find_rank(lower_bound: Balance, upper_bound: Balance, stake_amount: Balance) -> Rank {
268 if upper_bound.is_zero() {
269 return 0;
270 }
271 let rank_threshold = upper_bound
272 .saturating_sub(lower_bound)
273 .saturating_div(RankedTier::MAX_RANK.into());
274 if rank_threshold.is_zero() {
275 0
276 } else {
277 <Balance as TryInto<u8>>::try_into(
278 stake_amount
279 .saturating_sub(lower_bound)
280 .saturating_div(rank_threshold),
281 )
282 .unwrap_or_default()
283 .min(RankedTier::MAX_RANK)
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn tier_and_rank() {
294 let t = RankedTier::new(0, 0).unwrap();
295 assert_eq!(t.deconstruct(), (0, 0));
296
297 let t = RankedTier::new(15, 10).unwrap();
298 assert_eq!(t.deconstruct(), (15, 10));
299
300 assert_eq!(RankedTier::new(16, 10), Err(ArithmeticError::Overflow));
301 assert_eq!(RankedTier::new(15, 11), Err(ArithmeticError::Overflow));
302
303 let t = RankedTier::new_saturated(0, 0);
304 assert_eq!(t.deconstruct(), (0, 0));
305
306 let t = RankedTier::new_saturated(1, 1);
307 assert_eq!(t.deconstruct(), (1, 1));
308
309 let t = RankedTier::new_saturated(3, 15);
310 assert_eq!(t.deconstruct(), (3, 10));
311
312 let t = RankedTier::new_saturated(16, 16);
314 assert_eq!(t.deconstruct(), (15, 10));
315 }
316
317 #[test]
318 fn find_rank() {
319 assert_eq!(RankedTier::find_rank(0, 0, 0), 0);
320 assert_eq!(RankedTier::find_rank(0, 100, 9), 0);
321 assert_eq!(RankedTier::find_rank(0, 100, 10), 1);
322 assert_eq!(RankedTier::find_rank(0, 100, 49), 4);
323 assert_eq!(RankedTier::find_rank(0, 100, 50), 5);
324 assert_eq!(RankedTier::find_rank(0, 100, 51), 5);
325 assert_eq!(RankedTier::find_rank(0, 100, 101), 10);
326
327 assert_eq!(RankedTier::find_rank(100, 100, 100), 0);
328 assert_eq!(RankedTier::find_rank(200, 100, 100), 0);
329 }
330}