1#![cfg_attr(not(feature = "std"), no_std)]
19
20extern crate alloc;
24
25use crate::currency_to_vote::CurrencyToVote;
26use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec};
27use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, HasCompact, MaxEncodedLen};
28use core::ops::{Add, AddAssign, Sub, SubAssign};
29use scale_info::TypeInfo;
30use sp_runtime::{
31 traits::{AtLeast32BitUnsigned, Zero},
32 Debug, DispatchError, DispatchResult, Perbill, Saturating,
33};
34
35pub mod budget;
36pub mod offence;
37
38pub mod currency_to_vote;
39
40pub type SessionIndex = u32;
42
43pub type EraIndex = u32;
45
46pub type Page = u32;
48#[derive(Clone, Debug)]
53pub enum StakingAccount<AccountId> {
54 Stash(AccountId),
55 Controller(AccountId),
56}
57
58#[cfg(feature = "std")]
59impl<AccountId> From<AccountId> for StakingAccount<AccountId> {
60 fn from(account: AccountId) -> Self {
61 StakingAccount::Stash(account)
62 }
63}
64
65#[derive(Debug, TypeInfo)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone))]
68pub enum StakerStatus<AccountId> {
69 Idle,
71 Validator,
73 Nominator(Vec<AccountId>),
75}
76
77#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
80pub struct Stake<Balance> {
81 pub total: Balance,
91 pub active: Balance,
94}
95
96#[impl_trait_for_tuples::impl_for_tuples(10)]
102pub trait OnStakingUpdate<AccountId, Balance> {
103 fn on_stake_update(_who: &AccountId, _prev_stake: Option<Stake<Balance>>) {}
108
109 fn on_nominator_add(_who: &AccountId) {}
113
114 fn on_nominator_update(_who: &AccountId, _prev_nominations: Vec<AccountId>) {}
120
121 fn on_nominator_remove(_who: &AccountId, _nominations: Vec<AccountId>) {}
126
127 fn on_validator_add(_who: &AccountId) {}
131
132 fn on_validator_update(_who: &AccountId) {}
136
137 fn on_validator_remove(_who: &AccountId) {}
139
140 fn on_unstake(_who: &AccountId) {}
142
143 fn on_slash(
151 _stash: &AccountId,
152 _slashed_active: Balance,
153 _slashed_unlocking: &BTreeMap<EraIndex, Balance>,
154 _slashed_total: Balance,
155 ) {
156 }
157
158 fn on_withdraw(_stash: &AccountId, _amount: Balance) {}
160}
161
162pub trait StakingInterface {
167 type Balance: Sub<Output = Self::Balance>
169 + Ord
170 + PartialEq
171 + Default
172 + Copy
173 + MaxEncodedLen
174 + FullCodec
175 + TypeInfo
176 + Saturating;
177
178 type AccountId: Clone + core::fmt::Debug;
180
181 type CurrencyToVote: CurrencyToVote<Self::Balance>;
183
184 fn minimum_nominator_bond() -> Self::Balance;
189
190 fn minimum_validator_bond() -> Self::Balance;
192
193 fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError>;
200
201 fn bonding_duration() -> EraIndex;
205
206 fn nominator_bonding_duration() -> EraIndex {
217 Self::bonding_duration()
218 }
219
220 fn current_era() -> EraIndex;
224
225 fn stake(who: &Self::AccountId) -> Result<Stake<Self::Balance>, DispatchError>;
227
228 fn total_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
230 Self::stake(who).map(|s| s.total)
231 }
232
233 fn active_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
235 Self::stake(who).map(|s| s.active)
236 }
237
238 fn is_unbonding(who: &Self::AccountId) -> Result<bool, DispatchError> {
240 Self::stake(who).map(|s| s.active != s.total)
241 }
242
243 fn fully_unbond(who: &Self::AccountId) -> DispatchResult {
245 Self::unbond(who, Self::stake(who)?.active)
246 }
247
248 fn bond(who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId)
250 -> DispatchResult;
251
252 fn nominate(who: &Self::AccountId, validators: Vec<Self::AccountId>) -> DispatchResult;
254
255 fn chill(who: &Self::AccountId) -> DispatchResult;
257
258 fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult;
262
263 fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult;
273
274 fn set_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult;
276
277 fn withdraw_unbonded(
281 stash: Self::AccountId,
282 num_slashing_spans: u32,
283 ) -> Result<bool, DispatchError>;
284
285 fn desired_validator_count() -> u32;
287
288 fn election_ongoing() -> bool;
290
291 fn force_unstake(who: Self::AccountId) -> DispatchResult;
293
294 fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool;
296
297 fn status(who: &Self::AccountId) -> Result<StakerStatus<Self::AccountId>, DispatchError>;
299
300 fn is_validator(who: &Self::AccountId) -> bool {
302 Self::status(who).map(|s| matches!(s, StakerStatus::Validator)).unwrap_or(false)
303 }
304
305 fn is_virtual_staker(who: &Self::AccountId) -> bool;
311
312 fn nominations(who: &Self::AccountId) -> Option<Vec<Self::AccountId>> {
314 match Self::status(who) {
315 Ok(StakerStatus::Nominator(t)) => Some(t),
316 _ => None,
317 }
318 }
319
320 fn slash_reward_fraction() -> Perbill;
322
323 #[cfg(feature = "runtime-benchmarks")]
324 fn max_exposure_page_size() -> Page;
325
326 #[cfg(feature = "runtime-benchmarks")]
327 fn add_era_stakers(
328 current_era: &EraIndex,
329 stash: &Self::AccountId,
330 exposures: Vec<(Self::AccountId, Self::Balance)>,
331 );
332
333 #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
335 fn set_era(era: EraIndex);
336}
337
338pub trait StakingUnchecked: StakingInterface {
343 fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult;
347
348 fn virtual_bond(
354 keyless_who: &Self::AccountId,
355 value: Self::Balance,
356 payee: &Self::AccountId,
357 ) -> DispatchResult;
358
359 #[cfg(feature = "runtime-benchmarks")]
363 fn migrate_to_direct_staker(who: &Self::AccountId);
364}
365
366#[derive(
368 PartialEq,
369 Eq,
370 PartialOrd,
371 Ord,
372 Clone,
373 Encode,
374 Decode,
375 DecodeWithMemTracking,
376 Debug,
377 TypeInfo,
378 Copy,
379)]
380pub struct IndividualExposure<AccountId, Balance: HasCompact> {
381 pub who: AccountId,
383 #[codec(compact)]
385 pub value: Balance,
386}
387
388#[derive(
390 PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo,
391)]
392pub struct Exposure<AccountId, Balance: HasCompact> {
393 #[codec(compact)]
395 pub total: Balance,
396 #[codec(compact)]
398 pub own: Balance,
399 pub others: Vec<IndividualExposure<AccountId, Balance>>,
401}
402
403impl<AccountId, Balance: Default + HasCompact> Default for Exposure<AccountId, Balance> {
404 fn default() -> Self {
405 Self { total: Default::default(), own: Default::default(), others: vec![] }
406 }
407}
408
409impl<
410 AccountId: Clone,
411 Balance: HasCompact + AtLeast32BitUnsigned + Copy + codec::MaxEncodedLen,
412 > Exposure<AccountId, Balance>
413{
414 pub fn split_others(&mut self, n_others: u32) -> Self {
422 let head_others: Vec<_> =
423 self.others.drain(..(n_others as usize).min(self.others.len())).collect();
424
425 let total_others_head: Balance = head_others
426 .iter()
427 .fold(Zero::zero(), |acc: Balance, o| acc.saturating_add(o.value));
428
429 self.total = self.total.saturating_sub(total_others_head);
430
431 Self {
432 total: total_others_head.saturating_add(self.own),
433 own: self.own,
434 others: head_others,
435 }
436 }
437
438 pub fn into_pages(
441 self,
442 page_size: Page,
443 ) -> (PagedExposureMetadata<Balance>, Vec<ExposurePage<AccountId, Balance>>) {
444 let individual_chunks = self.others.chunks(page_size as usize);
445 let mut exposure_pages: Vec<ExposurePage<AccountId, Balance>> =
446 Vec::with_capacity(individual_chunks.len());
447
448 for chunk in individual_chunks {
449 let mut page_total: Balance = Zero::zero();
450 let mut others: Vec<IndividualExposure<AccountId, Balance>> =
451 Vec::with_capacity(chunk.len());
452 for individual in chunk.iter() {
453 page_total.saturating_accrue(individual.value);
454 others.push(IndividualExposure {
455 who: individual.who.clone(),
456 value: individual.value,
457 })
458 }
459 exposure_pages.push(ExposurePage { page_total, others });
460 }
461
462 (
463 PagedExposureMetadata {
464 total: self.total,
465 own: self.own,
466 nominator_count: self.others.len() as u32,
467 page_count: exposure_pages.len() as Page,
468 },
469 exposure_pages,
470 )
471 }
472}
473
474#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug, TypeInfo)]
476pub struct ExposurePage<AccountId, Balance: HasCompact> {
477 #[codec(compact)]
479 pub page_total: Balance,
480 pub others: Vec<IndividualExposure<AccountId, Balance>>,
482}
483
484impl<A, B: Default + HasCompact> Default for ExposurePage<A, B> {
485 fn default() -> Self {
486 ExposurePage { page_total: Default::default(), others: vec![] }
487 }
488}
489
490impl<A, B: HasCompact + Default + AddAssign + SubAssign + Clone> From<Vec<IndividualExposure<A, B>>>
492 for ExposurePage<A, B>
493{
494 fn from(exposures: Vec<IndividualExposure<A, B>>) -> Self {
495 exposures.into_iter().fold(ExposurePage::default(), |mut page, e| {
496 page.page_total += e.value.clone();
497 page.others.push(e);
498 page
499 })
500 }
501}
502
503#[derive(
509 PartialEq,
510 Eq,
511 PartialOrd,
512 Ord,
513 Clone,
514 Encode,
515 Decode,
516 Debug,
517 TypeInfo,
518 Default,
519 MaxEncodedLen,
520 Copy,
521)]
522pub struct PagedExposureMetadata<Balance: HasCompact + codec::MaxEncodedLen> {
523 #[codec(compact)]
525 pub total: Balance,
526 #[codec(compact)]
528 pub own: Balance,
529 pub nominator_count: u32,
531 pub page_count: Page,
533}
534
535impl<Balance> PagedExposureMetadata<Balance>
536where
537 Balance: HasCompact
538 + codec::MaxEncodedLen
539 + Add<Output = Balance>
540 + Sub<Output = Balance>
541 + sp_runtime::Saturating
542 + PartialEq
543 + Copy
544 + sp_runtime::traits::Debug,
545{
546 pub fn update_with<Max: sp_core::Get<u32>>(
551 self,
552 others_balance: Balance,
553 others_num: u32,
554 ) -> Self {
555 let page_limit = Max::get().max(1);
556 let new_nominator_count = self.nominator_count.saturating_add(others_num);
557 let new_page_count = new_nominator_count
558 .saturating_add(page_limit)
559 .saturating_sub(1)
560 .saturating_div(page_limit);
561
562 Self {
563 total: self.total.saturating_add(others_balance),
564 own: self.own,
565 nominator_count: new_nominator_count,
566 page_count: new_page_count,
567 }
568 }
569}
570
571#[derive(Clone, Debug)]
581pub struct Agent<T>(T);
582impl<T> From<T> for Agent<T> {
583 fn from(acc: T) -> Self {
584 Agent(acc)
585 }
586}
587
588impl<T> Agent<T> {
589 pub fn get(self) -> T {
590 self.0
591 }
592}
593
594#[derive(Clone, Debug)]
599pub struct Delegator<T>(T);
600impl<T> From<T> for Delegator<T> {
601 fn from(acc: T) -> Self {
602 Delegator(acc)
603 }
604}
605
606impl<T> Delegator<T> {
607 pub fn get(self) -> T {
608 self.0
609 }
610}
611
612pub trait DelegationInterface {
614 type Balance: Sub<Output = Self::Balance>
616 + Ord
617 + PartialEq
618 + Default
619 + Copy
620 + MaxEncodedLen
621 + FullCodec
622 + TypeInfo
623 + Saturating;
624
625 type AccountId: Clone + core::fmt::Debug;
627
628 fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
632
633 fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
636
637 fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance>;
639
640 fn register_agent(
642 agent: Agent<Self::AccountId>,
643 reward_account: &Self::AccountId,
644 ) -> DispatchResult;
645
646 fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult;
650
651 fn delegate(
653 delegator: Delegator<Self::AccountId>,
654 agent: Agent<Self::AccountId>,
655 amount: Self::Balance,
656 ) -> DispatchResult;
657
658 fn withdraw_delegation(
663 delegator: Delegator<Self::AccountId>,
664 agent: Agent<Self::AccountId>,
665 amount: Self::Balance,
666 num_slashing_spans: u32,
667 ) -> DispatchResult;
668
669 fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
674
675 fn delegator_slash(
680 agent: Agent<Self::AccountId>,
681 delegator: Delegator<Self::AccountId>,
682 value: Self::Balance,
683 maybe_reporter: Option<Self::AccountId>,
684 ) -> DispatchResult;
685}
686
687pub trait DelegationMigrator {
690 type Balance: Sub<Output = Self::Balance>
692 + Ord
693 + PartialEq
694 + Default
695 + Copy
696 + MaxEncodedLen
697 + FullCodec
698 + TypeInfo
699 + Saturating;
700
701 type AccountId: Clone + core::fmt::Debug;
703
704 fn migrate_nominator_to_agent(
709 agent: Agent<Self::AccountId>,
710 reward_account: &Self::AccountId,
711 ) -> DispatchResult;
712
713 fn migrate_delegation(
718 agent: Agent<Self::AccountId>,
719 delegator: Delegator<Self::AccountId>,
720 value: Self::Balance,
721 ) -> DispatchResult;
722
723 #[cfg(feature = "runtime-benchmarks")]
727 fn force_kill_agent(agent: Agent<Self::AccountId>);
728}
729
730pub trait EraPayout<Balance> {
735 fn era_payout(
740 total_staked: Balance,
741 total_issuance: Balance,
742 era_duration_millis: u64,
743 ) -> (Balance, Balance);
744}
745
746impl<Balance: Default> EraPayout<Balance> for () {
747 fn era_payout(
748 _total_staked: Balance,
749 _total_issuance: Balance,
750 _era_duration_millis: u64,
751 ) -> (Balance, Balance) {
752 (Default::default(), Default::default())
753 }
754}
755
756#[derive(Debug, Clone, PartialEq, Eq)]
760pub struct StakerRewardResult<Balance> {
761 pub validator_payout: Balance,
763 pub nominator_payout: Balance,
765}
766
767pub trait StakerRewardCalculator<Balance> {
776 fn calculate_validator_incentive_weight(self_stake: Balance) -> Balance;
781
782 fn calculate_staker_reward(
784 validator_total_reward: Balance,
785 validator_commission: Perbill,
786 validator_own_stake: Balance,
787 total_exposure: Balance,
788 ) -> StakerRewardResult<Balance>;
789}
790
791impl<Balance: Default> StakerRewardCalculator<Balance> for () {
792 fn calculate_validator_incentive_weight(_self_stake: Balance) -> Balance {
793 Default::default()
794 }
795
796 fn calculate_staker_reward(
797 _validator_total_reward: Balance,
798 _validator_commission: Perbill,
799 _validator_own_stake: Balance,
800 _total_exposure: Balance,
801 ) -> StakerRewardResult<Balance> {
802 StakerRewardResult {
803 validator_payout: Default::default(),
804 nominator_payout: Default::default(),
805 }
806 }
807}
808
809sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);
810sp_core::generate_feature_enabled_macro!(std_or_benchmarks_enabled, any(feature = "std", feature = "runtime-benchmarks"), $);
811
812#[cfg(test)]
813mod tests {
814 use sp_core::ConstU32;
815
816 use super::*;
817
818 #[test]
819 fn update_with_works() {
820 let metadata = PagedExposureMetadata::<u32> {
821 total: 1000,
822 own: 0, nominator_count: 10,
824 page_count: 1,
825 };
826
827 assert_eq!(
828 metadata.update_with::<ConstU32<10>>(1, 1),
829 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 2 },
830 );
831
832 assert_eq!(
833 metadata.update_with::<ConstU32<5>>(1, 1),
834 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
835 );
836
837 assert_eq!(
838 metadata.update_with::<ConstU32<4>>(1, 1),
839 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
840 );
841
842 assert_eq!(
843 metadata.update_with::<ConstU32<1>>(1, 1),
844 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 11 },
845 );
846 }
847
848 #[test]
849 fn individual_exposures_to_exposure_works() {
850 let exposure_1 = IndividualExposure { who: 1, value: 10u32 };
851 let exposure_2 = IndividualExposure { who: 2, value: 20 };
852 let exposure_3 = IndividualExposure { who: 3, value: 30 };
853
854 let exposure_page: ExposurePage<u32, u32> = vec![exposure_1, exposure_2, exposure_3].into();
855
856 assert_eq!(
857 exposure_page,
858 ExposurePage { page_total: 60, others: vec![exposure_1, exposure_2, exposure_3] },
859 );
860 }
861
862 #[test]
863 fn empty_individual_exposures_to_exposure_works() {
864 let empty_exposures: Vec<IndividualExposure<u32, u32>> = vec![];
865
866 let exposure_page: ExposurePage<u32, u32> = empty_exposures.into();
867 assert_eq!(exposure_page, ExposurePage { page_total: 0, others: vec![] });
868 }
869
870 #[test]
871 fn exposure_split_others_works() {
872 let exposure = Exposure {
873 total: 100,
874 own: 20,
875 others: vec![
876 IndividualExposure { who: 1, value: 20u32 },
877 IndividualExposure { who: 2, value: 20 },
878 IndividualExposure { who: 3, value: 20 },
879 IndividualExposure { who: 4, value: 20 },
880 ],
881 };
882
883 let mut exposure_0 = exposure.clone();
884 let split_exposure = exposure_0.split_others(0);
887 assert_eq!(exposure_0, exposure);
888 assert_eq!(split_exposure, Exposure { total: 20, own: 20, others: vec![] });
889
890 let mut exposure_1 = exposure.clone();
891 let split_exposure = exposure_1.split_others(1);
893 assert_eq!(exposure_1.own, 20);
894 assert_eq!(exposure_1.total, 20 + 3 * 20);
895 assert_eq!(exposure_1.others.len(), 3);
896
897 assert_eq!(split_exposure.own, 20);
898 assert_eq!(split_exposure.total, 20 + 1 * 20);
899 assert_eq!(split_exposure.others.len(), 1);
900
901 let mut exposure_3 = exposure.clone();
902 let split_exposure = exposure_3.split_others(3);
905 assert_eq!(exposure_3.own, 20);
906 assert_eq!(exposure_3.total, 20 + 1 * 20);
907 assert_eq!(exposure_3.others.len(), 1);
908
909 assert_eq!(split_exposure.own, 20);
910 assert_eq!(split_exposure.total, 20 + 3 * 20);
911 assert_eq!(split_exposure.others.len(), 3);
912
913 let mut exposure_max = exposure.clone();
914 let split_exposure = exposure_max.split_others(u32::MAX);
918 assert_eq!(split_exposure, exposure);
919 assert_eq!(exposure_max, Exposure { total: 20, own: 20, others: vec![] });
920 }
921}