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
187#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, MaxEncodedLen, scale_info::TypeInfo)]
191pub struct RankedTier(u8);
192
193impl RankedTier {
194 pub const MAX_RANK: u8 = 10;
195
196 pub fn new(tier: TierId, rank: Rank) -> Result<Self, ArithmeticError> {
199 if rank > Self::MAX_RANK || tier > 0xf {
200 return Err(ArithmeticError::Overflow);
201 }
202 Ok(Self(rank << 4 | tier & 0x0f))
203 }
204
205 pub fn new_saturated(tier: TierId, rank: Rank) -> Self {
207 Self(rank.min(Self::MAX_RANK) << 4 | tier.min(0xf) & 0x0f)
208 }
209
210 #[inline(always)]
211 pub fn tier(&self) -> TierId {
212 self.0 & 0x0f
213 }
214
215 #[inline(always)]
216 pub fn rank(&self) -> Rank {
217 (self.0 >> 4).min(Self::MAX_RANK)
218 }
219
220 #[inline(always)]
221 pub fn deconstruct(&self) -> (TierId, Rank) {
222 (self.tier(), self.rank())
223 }
224}
225
226impl core::fmt::Debug for RankedTier {
227 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
228 f.debug_struct("RankedTier")
229 .field("tier", &self.tier())
230 .field("rank", &self.rank())
231 .finish()
232 }
233}
234
235impl RankedTier {
236 pub fn find_rank(lower_bound: Balance, upper_bound: Balance, stake_amount: Balance) -> Rank {
243 if upper_bound.is_zero() {
244 return 0;
245 }
246 let rank_threshold = upper_bound
247 .saturating_sub(lower_bound)
248 .saturating_div(RankedTier::MAX_RANK.into());
249 if rank_threshold.is_zero() {
250 0
251 } else {
252 <Balance as TryInto<u8>>::try_into(
253 stake_amount
254 .saturating_sub(lower_bound)
255 .saturating_div(rank_threshold),
256 )
257 .unwrap_or_default()
258 .min(RankedTier::MAX_RANK)
259 }
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn tier_and_rank() {
269 let t = RankedTier::new(0, 0).unwrap();
270 assert_eq!(t.deconstruct(), (0, 0));
271
272 let t = RankedTier::new(15, 10).unwrap();
273 assert_eq!(t.deconstruct(), (15, 10));
274
275 assert_eq!(RankedTier::new(16, 10), Err(ArithmeticError::Overflow));
276 assert_eq!(RankedTier::new(15, 11), Err(ArithmeticError::Overflow));
277
278 let t = RankedTier::new_saturated(0, 0);
279 assert_eq!(t.deconstruct(), (0, 0));
280
281 let t = RankedTier::new_saturated(1, 1);
282 assert_eq!(t.deconstruct(), (1, 1));
283
284 let t = RankedTier::new_saturated(3, 15);
285 assert_eq!(t.deconstruct(), (3, 10));
286
287 let t = RankedTier::new_saturated(16, 16);
289 assert_eq!(t.deconstruct(), (15, 10));
290 }
291
292 #[test]
293 fn find_rank() {
294 assert_eq!(RankedTier::find_rank(0, 0, 0), 0);
295 assert_eq!(RankedTier::find_rank(0, 100, 9), 0);
296 assert_eq!(RankedTier::find_rank(0, 100, 10), 1);
297 assert_eq!(RankedTier::find_rank(0, 100, 49), 4);
298 assert_eq!(RankedTier::find_rank(0, 100, 50), 5);
299 assert_eq!(RankedTier::find_rank(0, 100, 51), 5);
300 assert_eq!(RankedTier::find_rank(0, 100, 101), 10);
301
302 assert_eq!(RankedTier::find_rank(100, 100, 100), 0);
303 assert_eq!(RankedTier::find_rank(200, 100, 100), 0);
304 }
305}