1use crate::*;
32use frame_support::{
33 defensive,
34 traits::{
35 fungible::{Balanced, Inspect, Mutate},
36 tokens::{Fortitude, Precision, Preservation},
37 OnUnbalanced,
38 },
39};
40use sp_runtime::{
41 traits::{AtLeast32BitUnsigned, Zero},
42 Perbill,
43};
44use sp_staking::EraIndex;
45
46#[derive(
48 Debug,
49 Clone,
50 Copy,
51 PartialEq,
52 Eq,
53 codec::Encode,
54 codec::Decode,
55 codec::DecodeWithMemTracking,
56 scale_info::TypeInfo,
57)]
58pub struct EraRewardAllocation<Balance> {
59 pub staker_rewards: Balance,
60 pub validator_incentive: Balance,
61}
62
63pub struct EraRewardManager<T: Config>(core::marker::PhantomData<T>);
65
66impl<T: Config> EraRewardManager<T> {
67 pub(crate) fn create(era: EraIndex, kind: RewardKind) -> T::AccountId {
73 debug_assert!(
74 T::DisableMinting::get(),
75 "Era pots should only be created when DisableMinting is true"
76 );
77 let pot_account = T::RewardPots::pot_account(RewardPot::Era(era, kind));
78 if frame_system::Pallet::<T>::providers(&pot_account) == 0 {
79 frame_system::Pallet::<T>::inc_providers(&pot_account);
80 }
81 pot_account
82 }
83
84 pub(crate) fn snapshot_era_rewards(era: EraIndex) -> EraRewardAllocation<BalanceOf<T>> {
89 let staker_era_pot = Self::create(era, RewardKind::StakerRewards);
90 let incentive_era_pot = Self::create(era, RewardKind::ValidatorSelfStake);
91
92 let general_staker_pot =
93 T::RewardPots::pot_account(RewardPot::General(RewardKind::StakerRewards));
94 let general_incentive_pot =
95 T::RewardPots::pot_account(RewardPot::General(RewardKind::ValidatorSelfStake));
96
97 let staker_balance = T::Currency::reducible_balance(
99 &general_staker_pot,
100 Preservation::Preserve,
101 Fortitude::Polite,
102 );
103 let incentive_balance = T::Currency::reducible_balance(
104 &general_incentive_pot,
105 Preservation::Preserve,
106 Fortitude::Polite,
107 );
108
109 let actual_staker = if !staker_balance.is_zero() {
110 match T::Currency::transfer(
111 &general_staker_pot,
112 &staker_era_pot,
113 staker_balance,
114 Preservation::Preserve,
115 ) {
116 Ok(_) => staker_balance,
117 Err(e) => {
118 log!(error, "Era {:?}: staker reward transfer failed: {:?}", era, e);
119 defensive!("Failed to transfer staker rewards to era pot");
120 Zero::zero()
121 },
122 }
123 } else {
124 Zero::zero()
125 };
126
127 let actual_incentive = if !incentive_balance.is_zero() {
128 match T::Currency::transfer(
129 &general_incentive_pot,
130 &incentive_era_pot,
131 incentive_balance,
132 Preservation::Preserve,
133 ) {
134 Ok(_) => incentive_balance,
135 Err(e) => {
136 log!(error, "Era {:?}: validator incentive transfer failed: {:?}", era, e);
137 defensive!("Failed to transfer validator incentive to era pot");
138 Zero::zero()
139 },
140 }
141 } else {
142 Zero::zero()
143 };
144
145 log!(
146 info,
147 "Era {:?}: snapshotted staker_rewards={:?}, validator_incentive={:?}",
148 era,
149 actual_staker,
150 actual_incentive
151 );
152
153 EraRewardAllocation { staker_rewards: actual_staker, validator_incentive: actual_incentive }
154 }
155
156 pub(crate) fn drain(era: EraIndex, kind: RewardKind) {
162 let pot_account = T::RewardPots::pot_account(RewardPot::Era(era, kind));
163
164 if frame_system::Pallet::<T>::providers(&pot_account) == 0 {
166 return;
167 }
168
169 let remaining = T::Currency::balance(&pot_account);
170
171 if remaining.is_zero() {
172 return;
173 }
174
175 match T::Currency::withdraw(
176 &pot_account,
177 remaining,
178 Precision::BestEffort,
179 Preservation::Expendable,
180 Fortitude::Force,
181 ) {
182 Ok(credit) => {
183 T::UnclaimedRewardHandler::on_unbalanced(credit);
184 log!(
185 debug,
186 "Drained {:?} unclaimed rewards from era {:?} {:?} pot",
187 remaining,
188 era,
189 kind
190 );
191 },
192 Err(e) => {
193 defensive!("Failed to withdraw unclaimed rewards from era pot");
194 log!(
195 error,
196 "Era {:?} {:?}: unclaimed reward withdrawal failed: {:?}",
197 era,
198 kind,
199 e
200 );
201 },
202 }
203 }
204
205 #[cfg(any(test, feature = "try-runtime"))]
211 pub(crate) fn has_staker_rewards_pot(era: EraIndex) -> bool {
212 let pot = T::RewardPots::pot_account(RewardPot::Era(era, RewardKind::StakerRewards));
213 frame_system::Pallet::<T>::providers(&pot) > 0
214 }
215
216 pub(crate) fn cleanup_era(era: EraIndex) {
220 Self::drain(era, RewardKind::StakerRewards);
221 Self::drain(era, RewardKind::ValidatorSelfStake);
222 }
223}
224
225pub struct DefaultStakerRewardCalculator<T>(core::marker::PhantomData<T>);
230
231impl<T: Config> sp_staking::StakerRewardCalculator<BalanceOf<T>>
232 for DefaultStakerRewardCalculator<T>
233where
234 BalanceOf<T>: Into<u128> + From<u128>,
235{
236 fn calculate_validator_incentive_weight(self_stake: BalanceOf<T>) -> BalanceOf<T> {
237 let optimum = OptimumSelfStake::<T>::get();
238 let cap = HardCapSelfStake::<T>::get();
239 let slope_factor = SelfStakeSlopeFactor::<T>::get();
240
241 incentive_weight::<BalanceOf<T>>(self_stake, optimum, cap, slope_factor)
242 }
243
244 fn calculate_staker_reward(
245 validator_total_reward: BalanceOf<T>,
246 validator_commission: Perbill,
247 validator_own_stake: BalanceOf<T>,
248 total_exposure: BalanceOf<T>,
249 ) -> sp_staking::StakerRewardResult<BalanceOf<T>> {
250 let validator_commission_payout = validator_commission.mul_floor(validator_total_reward);
251 let leftover = validator_total_reward.saturating_sub(validator_commission_payout);
252 let validator_exposure_part = Perbill::from_rational(validator_own_stake, total_exposure);
253 let validator_staking_payout = validator_exposure_part.mul_floor(leftover);
254 let validator_payout = validator_staking_payout.saturating_add(validator_commission_payout);
255 let nominator_payout = leftover.saturating_sub(validator_staking_payout);
256
257 debug_assert_eq!(validator_payout + nominator_payout, validator_total_reward);
259
260 sp_staking::StakerRewardResult { validator_payout, nominator_payout }
261 }
262}
263
264fn incentive_weight<Balance>(
270 self_stake: Balance,
271 optimum: Balance,
272 cap: Balance,
273 slope_factor: Perbill,
274) -> Balance
275where
276 Balance: AtLeast32BitUnsigned + Copy + Into<u128> + From<u128>,
277{
278 debug_assert!(optimum <= cap, "config invariant: optimum must be <= cap");
279
280 if self_stake.is_zero() {
281 return Balance::zero();
282 }
283
284 if optimum.is_zero() && cap.is_zero() {
285 return Balance::zero();
286 }
287
288 let self_stake_u128: u128 = self_stake.into();
289 let optimum_u128: u128 = optimum.into();
290 let cap_u128: u128 = cap.into();
291
292 let weight_u128 = if self_stake <= optimum {
293 sp_arithmetic::helpers_128bit::sqrt(self_stake_u128)
294 } else if self_stake <= cap {
295 let k_squared = slope_factor.square();
296 let excess = self_stake_u128.saturating_sub(optimum_u128);
297 let arg = optimum_u128.saturating_add(k_squared.mul_floor(excess));
298 sp_arithmetic::helpers_128bit::sqrt(arg)
299 } else {
300 let k_squared = slope_factor.square();
301 let excess = cap_u128.saturating_sub(optimum_u128);
302 let arg = optimum_u128.saturating_add(k_squared.mul_floor(excess));
303 sp_arithmetic::helpers_128bit::sqrt(arg)
304 };
305
306 Balance::from(weight_u128)
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use sp_runtime::Perbill;
313
314 type Balance = u128;
315
316 #[test]
317 fn incentive_weight_zero_self_stake() {
318 assert_eq!(
319 incentive_weight::<Balance>(0, 100_000, 500_000, Perbill::from_rational(1u32, 2u32)),
320 0
321 );
322 }
323
324 #[test]
325 fn incentive_weight_config_not_set() {
326 assert_eq!(
328 incentive_weight::<Balance>(100_000, 0, 0, Perbill::from_rational(1u32, 2u32)),
329 0
330 );
331 }
332
333 #[test]
334 fn incentive_weight_optimum_zero_cap_set() {
335 let slope = Perbill::from_rational(1u32, 2u32);
337 assert_eq!(incentive_weight::<Balance>(400_000, 0, 500_000, slope), 316);
340 assert_eq!(incentive_weight::<Balance>(400_000, 100_000, 500_000, slope), 418);
342 assert_eq!(incentive_weight::<Balance>(1_000_000, 0, 500_000, slope), 353);
344 }
345
346 #[test]
347 fn incentive_weight_below_optimum() {
348 assert_eq!(
350 incentive_weight::<Balance>(
351 10_000,
352 100_000,
353 500_000,
354 Perbill::from_rational(1u32, 2u32)
355 ),
356 100
357 );
358 }
359
360 #[test]
361 fn incentive_weight_at_optimum() {
362 assert_eq!(
364 incentive_weight::<Balance>(
365 100_000,
366 100_000,
367 500_000,
368 Perbill::from_rational(1u32, 2u32)
369 ),
370 316
371 );
372 }
373
374 #[test]
375 fn incentive_weight_between_optimum_and_cap() {
376 assert_eq!(
378 incentive_weight::<Balance>(
379 300_000,
380 100_000,
381 500_000,
382 Perbill::from_rational(1u32, 2u32)
383 ),
384 387
385 );
386 }
387
388 #[test]
389 fn incentive_weight_at_cap() {
390 assert_eq!(
392 incentive_weight::<Balance>(
393 500_000,
394 100_000,
395 500_000,
396 Perbill::from_rational(1u32, 2u32)
397 ),
398 447
399 );
400 }
401
402 #[test]
403 fn incentive_weight_plateau_above_cap() {
404 let at_cap = incentive_weight::<Balance>(
405 500_000,
406 100_000,
407 500_000,
408 Perbill::from_rational(1u32, 2u32),
409 );
410 let above = incentive_weight::<Balance>(
411 1_000_000,
412 100_000,
413 500_000,
414 Perbill::from_rational(1u32, 2u32),
415 );
416 assert_eq!(at_cap, above);
417 }
418
419 #[test]
420 fn incentive_weight_monotonically_increasing_below_cap() {
421 let slope = Perbill::from_rational(1u32, 2u32);
422 let w1 = incentive_weight::<Balance>(50_000, 100_000, 500_000, slope);
423 let w2 = incentive_weight::<Balance>(100_000, 100_000, 500_000, slope);
424 let w3 = incentive_weight::<Balance>(200_000, 100_000, 500_000, slope);
425 let w4 = incentive_weight::<Balance>(400_000, 100_000, 500_000, slope);
426 assert!(w1 < w2 && w2 < w3 && w3 < w4);
427 }
428
429 #[test]
430 fn incentive_weight_different_slope_factors() {
431 let self_stake = 300_000;
432 let w_025 = incentive_weight::<Balance>(
433 self_stake,
434 100_000,
435 500_000,
436 Perbill::from_rational(1u32, 4u32),
437 );
438 let w_050 = incentive_weight::<Balance>(
439 self_stake,
440 100_000,
441 500_000,
442 Perbill::from_rational(1u32, 2u32),
443 );
444 let w_075 = incentive_weight::<Balance>(
445 self_stake,
446 100_000,
447 500_000,
448 Perbill::from_rational(3u32, 4u32),
449 );
450 assert!(w_025 < w_050 && w_050 < w_075);
451 }
452
453 #[test]
454 fn incentive_weight_slope_factor_zero_plateaus_at_optimum() {
455 let at_optimum = incentive_weight::<Balance>(100_000, 100_000, 500_000, Perbill::zero());
457 let above_optimum = incentive_weight::<Balance>(300_000, 100_000, 500_000, Perbill::zero());
458 assert_eq!(at_optimum, above_optimum);
459 }
460
461 #[test]
462 fn incentive_weight_slope_factor_one_no_discouragement() {
463 let at_optimum = incentive_weight::<Balance>(100_000, 100_000, 500_000, Perbill::one());
465 let at_cap = incentive_weight::<Balance>(500_000, 100_000, 500_000, Perbill::one());
466 assert_eq!(at_optimum, 316);
468 assert_eq!(at_cap, 707);
469 }
470
471 #[test]
472 fn incentive_weight_optimum_equals_cap() {
473 let slope = Perbill::from_rational(1u32, 2u32);
475 let at_boundary = incentive_weight::<Balance>(100_000, 100_000, 100_000, slope);
476 let above = incentive_weight::<Balance>(200_000, 100_000, 100_000, slope);
477 assert_eq!(at_boundary, above);
478 assert_eq!(at_boundary, 316); }
480}