1use super::{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::traits::Zero;
27use sp_std::hash::Hash;
28
29pub type EraNumber = u32;
31pub type PeriodNumber = u32;
33pub type DAppId = u16;
35pub type TierId = u8;
37pub type Rank = u8;
39
40pub trait CycleConfiguration {
48 fn periods_per_cycle() -> PeriodNumber;
52
53 fn eras_per_voting_subperiod() -> EraNumber;
57
58 fn eras_per_build_and_earn_subperiod() -> EraNumber;
62
63 fn blocks_per_era() -> BlockNumber;
67
68 fn period_in_era_lengths() -> EraNumber {
70 Self::eras_per_voting_subperiod().saturating_add(Self::eras_per_build_and_earn_subperiod())
71 }
72
73 fn cycle_in_era_lengths() -> EraNumber {
75 Self::period_in_era_lengths().saturating_mul(Self::periods_per_cycle())
76 }
77
78 fn blocks_per_cycle() -> BlockNumber {
80 Self::blocks_per_era().saturating_mul(Self::cycle_in_era_lengths())
81 }
82
83 fn build_and_earn_eras_per_cycle() -> EraNumber {
85 Self::eras_per_build_and_earn_subperiod().saturating_mul(Self::periods_per_cycle())
86 }
87
88 fn eras_per_period() -> EraNumber {
90 Self::eras_per_build_and_earn_subperiod().saturating_add(1)
91 }
92
93 fn eras_per_cycle() -> EraNumber {
95 Self::eras_per_period().saturating_mul(Self::periods_per_cycle())
96 }
97}
98
99pub trait Observer {
101 fn block_before_new_era(_next_era: EraNumber) -> Weight {
108 Weight::zero()
109 }
110}
111
112impl Observer for () {}
113
114pub trait StakingRewardHandler<AccountId> {
119 fn staker_and_dapp_reward_pools(total_value_staked: Balance) -> (Balance, Balance);
123
124 fn bonus_reward_pool() -> Balance;
126
127 fn payout_reward(beneficiary: &AccountId, reward: Balance) -> Result<(), ()>;
129}
130
131pub trait SmartContractHandle<AccountId> {
135 fn evm(address: H160) -> Self;
137 fn wasm(address: AccountId) -> Self;
139}
140
141#[derive(
143 PartialEq,
144 Eq,
145 Copy,
146 Clone,
147 Encode,
148 Decode,
149 DecodeWithMemTracking,
150 RuntimeDebug,
151 MaxEncodedLen,
152 Hash,
153 scale_info::TypeInfo,
154)]
155pub enum SmartContract<AccountId> {
156 Evm(H160),
158 Wasm(AccountId),
160}
161
162impl<AccountId> SmartContractHandle<AccountId> for SmartContract<AccountId> {
163 fn evm(address: H160) -> Self {
164 Self::Evm(address)
165 }
166
167 fn wasm(address: AccountId) -> Self {
168 Self::Wasm(address)
169 }
170}
171
172pub trait AccountCheck<AccountId> {
174 fn allowed_to_stake(account: &AccountId) -> bool;
176}
177
178impl<AccountId> AccountCheck<AccountId> for () {
179 fn allowed_to_stake(_account: &AccountId) -> bool {
180 true
181 }
182}
183
184pub const FIXED_NUMBER_OF_TIER_SLOTS: u16 = 16;
186
187pub const FIXED_TIER_SLOTS_ARGS: (u64, u64) = (0, FIXED_NUMBER_OF_TIER_SLOTS as u64);
190
191#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, MaxEncodedLen, scale_info::TypeInfo)]
195pub struct RankedTier(u8);
196
197impl RankedTier {
198 pub const MAX_RANK: u8 = 10;
199
200 pub fn new(tier: TierId, rank: Rank) -> Result<Self, ArithmeticError> {
203 if rank > Self::MAX_RANK || tier > 0xf {
204 return Err(ArithmeticError::Overflow);
205 }
206 Ok(Self(rank << 4 | tier & 0x0f))
207 }
208
209 pub fn new_saturated(tier: TierId, rank: Rank) -> Self {
211 Self(rank.min(Self::MAX_RANK) << 4 | tier.min(0xf) & 0x0f)
212 }
213
214 #[inline(always)]
215 pub fn tier(&self) -> TierId {
216 self.0 & 0x0f
217 }
218
219 #[inline(always)]
220 pub fn rank(&self) -> Rank {
221 (self.0 >> 4).min(Self::MAX_RANK)
222 }
223
224 #[inline(always)]
225 pub fn deconstruct(&self) -> (TierId, Rank) {
226 (self.tier(), self.rank())
227 }
228}
229
230impl core::fmt::Debug for RankedTier {
231 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232 f.debug_struct("RankedTier")
233 .field("tier", &self.tier())
234 .field("rank", &self.rank())
235 .finish()
236 }
237}
238
239impl RankedTier {
240 pub fn find_rank(lower_bound: Balance, upper_bound: Balance, stake_amount: Balance) -> Rank {
247 if upper_bound.is_zero() {
248 return 0;
249 }
250 let rank_threshold = upper_bound
251 .saturating_sub(lower_bound)
252 .saturating_div(RankedTier::MAX_RANK.into());
253 if rank_threshold.is_zero() {
254 0
255 } else {
256 <Balance as TryInto<u8>>::try_into(
257 stake_amount
258 .saturating_sub(lower_bound)
259 .saturating_div(rank_threshold),
260 )
261 .unwrap_or_default()
262 .min(RankedTier::MAX_RANK)
263 }
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn tier_and_rank() {
273 let t = RankedTier::new(0, 0).unwrap();
274 assert_eq!(t.deconstruct(), (0, 0));
275
276 let t = RankedTier::new(15, 10).unwrap();
277 assert_eq!(t.deconstruct(), (15, 10));
278
279 assert_eq!(RankedTier::new(16, 10), Err(ArithmeticError::Overflow));
280 assert_eq!(RankedTier::new(15, 11), Err(ArithmeticError::Overflow));
281
282 let t = RankedTier::new_saturated(0, 0);
283 assert_eq!(t.deconstruct(), (0, 0));
284
285 let t = RankedTier::new_saturated(1, 1);
286 assert_eq!(t.deconstruct(), (1, 1));
287
288 let t = RankedTier::new_saturated(3, 15);
289 assert_eq!(t.deconstruct(), (3, 10));
290
291 let t = RankedTier::new_saturated(16, 16);
293 assert_eq!(t.deconstruct(), (15, 10));
294 }
295
296 #[test]
297 fn find_rank() {
298 assert_eq!(RankedTier::find_rank(0, 0, 0), 0);
299 assert_eq!(RankedTier::find_rank(0, 100, 9), 0);
300 assert_eq!(RankedTier::find_rank(0, 100, 10), 1);
301 assert_eq!(RankedTier::find_rank(0, 100, 49), 4);
302 assert_eq!(RankedTier::find_rank(0, 100, 50), 5);
303 assert_eq!(RankedTier::find_rank(0, 100, 51), 5);
304 assert_eq!(RankedTier::find_rank(0, 100, 101), 10);
305
306 assert_eq!(RankedTier::find_rank(100, 100, 100), 0);
307 assert_eq!(RankedTier::find_rank(200, 100, 100), 0);
308 }
309}