1use crate::{
20 auto_compound::AutoCompoundDelegations, set::OrderedSet, BalanceOf, BottomDelegations,
21 CandidateInfo, Config, DelegatorState, Error, Event, Pallet, Round, RoundIndex, TopDelegations,
22 Total,
23};
24use frame_support::pallet_prelude::*;
25use parity_scale_codec::{Decode, Encode};
26use sp_runtime::{
27 traits::{Saturating, Zero},
28 Perbill, Percent, RuntimeDebug,
29};
30use sp_std::{cmp::Ordering, prelude::*};
31
32pub struct CountedDelegations<T: Config> {
33 pub uncounted_stake: BalanceOf<T>,
34 pub rewardable_delegations: Vec<Bond<T::AccountId, BalanceOf<T>>>,
35}
36
37#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
38pub struct Bond<AccountId, Balance> {
39 pub owner: AccountId,
40 pub amount: Balance,
41}
42
43impl<A: Decode, B: Default> Default for Bond<A, B> {
44 fn default() -> Bond<A, B> {
45 Bond {
46 owner: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
47 .expect("infinite length input; no invalid inputs for type; qed"),
48 amount: B::default(),
49 }
50 }
51}
52
53impl<A, B: Default> Bond<A, B> {
54 pub fn from_owner(owner: A) -> Self {
55 Bond {
56 owner,
57 amount: B::default(),
58 }
59 }
60}
61
62impl<AccountId: Ord, Balance> Eq for Bond<AccountId, Balance> {}
63
64impl<AccountId: Ord, Balance> Ord for Bond<AccountId, Balance> {
65 fn cmp(&self, other: &Self) -> Ordering {
66 self.owner.cmp(&other.owner)
67 }
68}
69
70impl<AccountId: Ord, Balance> PartialOrd for Bond<AccountId, Balance> {
71 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
72 Some(self.cmp(other))
73 }
74}
75
76impl<AccountId: Ord, Balance> PartialEq for Bond<AccountId, Balance> {
77 fn eq(&self, other: &Self) -> bool {
78 self.owner == other.owner
79 }
80}
81
82#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
83pub enum CollatorStatus {
85 Active,
87 Idle,
89 Leaving(RoundIndex),
91}
92
93impl Default for CollatorStatus {
94 fn default() -> CollatorStatus {
95 CollatorStatus::Active
96 }
97}
98
99#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
100pub struct BondWithAutoCompound<AccountId, Balance> {
101 pub owner: AccountId,
102 pub amount: Balance,
103 pub auto_compound: Percent,
104}
105
106impl<A: Decode, B: Default> Default for BondWithAutoCompound<A, B> {
107 fn default() -> BondWithAutoCompound<A, B> {
108 BondWithAutoCompound {
109 owner: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
110 .expect("infinite length input; no invalid inputs for type; qed"),
111 amount: B::default(),
112 auto_compound: Percent::zero(),
113 }
114 }
115}
116
117#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
118pub struct CollatorSnapshot<AccountId, Balance> {
120 pub bond: Balance,
122
123 pub delegations: Vec<BondWithAutoCompound<AccountId, Balance>>,
127
128 pub total: Balance,
131}
132
133impl<A: PartialEq, B: PartialEq> PartialEq for CollatorSnapshot<A, B> {
134 fn eq(&self, other: &Self) -> bool {
135 let must_be_true = self.bond == other.bond && self.total == other.total;
136 if !must_be_true {
137 return false;
138 }
139 for (
140 BondWithAutoCompound {
141 owner: o1,
142 amount: a1,
143 auto_compound: c1,
144 },
145 BondWithAutoCompound {
146 owner: o2,
147 amount: a2,
148 auto_compound: c2,
149 },
150 ) in self.delegations.iter().zip(other.delegations.iter())
151 {
152 if o1 != o2 || a1 != a2 || c1 != c2 {
153 return false;
154 }
155 }
156 true
157 }
158}
159
160impl<A, B: Default> Default for CollatorSnapshot<A, B> {
161 fn default() -> CollatorSnapshot<A, B> {
162 CollatorSnapshot {
163 bond: B::default(),
164 delegations: Vec::new(),
165 total: B::default(),
166 }
167 }
168}
169
170#[derive(Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
171pub struct DelayedPayout<Balance> {
173 pub round_issuance: Balance,
175 pub total_staking_reward: Balance,
177 pub collator_commission: Perbill,
179}
180
181#[derive(PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
182pub struct CandidateBondLessRequest<Balance> {
184 pub amount: Balance,
185 pub when_executable: RoundIndex,
186}
187
188#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
189pub struct Delegations<AccountId, Balance> {
191 pub delegations: Vec<Bond<AccountId, Balance>>,
192 pub total: Balance,
193}
194
195impl<A, B: Default> Default for Delegations<A, B> {
196 fn default() -> Delegations<A, B> {
197 Delegations {
198 delegations: Vec::new(),
199 total: B::default(),
200 }
201 }
202}
203
204impl<AccountId, Balance: Copy + Ord + sp_std::ops::AddAssign + Zero + Saturating>
205 Delegations<AccountId, Balance>
206{
207 pub fn sort_greatest_to_least(&mut self) {
208 self.delegations.sort_by(|a, b| b.amount.cmp(&a.amount));
209 }
210 pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond<AccountId, Balance>) {
214 self.total = self.total.saturating_add(delegation.amount);
215 if !self.delegations.is_empty() {
217 if self.delegations[self.delegations.len() - 1].amount == delegation.amount {
219 self.delegations.push(delegation);
220 return;
222 }
223 }
224 match self
226 .delegations
227 .binary_search_by(|x| delegation.amount.cmp(&x.amount))
228 {
229 Ok(i) => {
232 let mut new_index = i + 1;
233 while new_index <= (self.delegations.len() - 1) {
234 if self.delegations[new_index].amount == delegation.amount {
235 new_index = new_index.saturating_add(1);
236 } else {
237 self.delegations.insert(new_index, delegation);
238 return;
239 }
240 }
241 self.delegations.push(delegation)
242 }
243 Err(i) => self.delegations.insert(i, delegation),
244 }
245 }
246 pub fn top_capacity<T: Config>(&self) -> CapacityStatus {
248 match &self.delegations {
249 x if x.len() as u32 >= T::MaxTopDelegationsPerCandidate::get() => CapacityStatus::Full,
250 x if x.is_empty() => CapacityStatus::Empty,
251 _ => CapacityStatus::Partial,
252 }
253 }
254 pub fn bottom_capacity<T: Config>(&self) -> CapacityStatus {
256 match &self.delegations {
257 x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => {
258 CapacityStatus::Full
259 }
260 x if x.is_empty() => CapacityStatus::Empty,
261 _ => CapacityStatus::Partial,
262 }
263 }
264 pub fn lowest_delegation_amount(&self) -> Balance {
266 self.delegations
267 .last()
268 .map(|x| x.amount)
269 .unwrap_or(Balance::zero())
270 }
271 pub fn highest_delegation_amount(&self) -> Balance {
273 self.delegations
274 .first()
275 .map(|x| x.amount)
276 .unwrap_or(Balance::zero())
277 }
278}
279
280#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
281pub enum CapacityStatus {
283 Full,
285 Empty,
287 Partial,
289}
290
291#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
292pub struct CandidateMetadata<Balance> {
294 pub bond: Balance,
296 pub delegation_count: u32,
298 pub total_counted: Balance,
300 pub lowest_top_delegation_amount: Balance,
302 pub highest_bottom_delegation_amount: Balance,
304 pub lowest_bottom_delegation_amount: Balance,
306 pub top_capacity: CapacityStatus,
308 pub bottom_capacity: CapacityStatus,
310 pub request: Option<CandidateBondLessRequest<Balance>>,
312 pub status: CollatorStatus,
314}
315
316impl<
317 Balance: Copy
318 + Zero
319 + PartialOrd
320 + sp_std::ops::AddAssign
321 + sp_std::ops::SubAssign
322 + sp_std::ops::Sub<Output = Balance>
323 + sp_std::fmt::Debug
324 + Saturating,
325 > CandidateMetadata<Balance>
326{
327 pub fn new(bond: Balance) -> Self {
328 CandidateMetadata {
329 bond,
330 delegation_count: 0u32,
331 total_counted: bond,
332 lowest_top_delegation_amount: Zero::zero(),
333 highest_bottom_delegation_amount: Zero::zero(),
334 lowest_bottom_delegation_amount: Zero::zero(),
335 top_capacity: CapacityStatus::Empty,
336 bottom_capacity: CapacityStatus::Empty,
337 request: None,
338 status: CollatorStatus::Active,
339 }
340 }
341 pub fn is_active(&self) -> bool {
342 matches!(self.status, CollatorStatus::Active)
343 }
344 pub fn is_leaving(&self) -> bool {
345 matches!(self.status, CollatorStatus::Leaving(_))
346 }
347 pub fn schedule_leave<T: Config>(&mut self) -> Result<(RoundIndex, RoundIndex), DispatchError> {
348 ensure!(!self.is_leaving(), Error::<T>::CandidateAlreadyLeaving);
349 let now = <Round<T>>::get().current;
350 let when = now + T::LeaveCandidatesDelay::get();
351 self.status = CollatorStatus::Leaving(when);
352 Ok((now, when))
353 }
354 pub fn can_leave<T: Config>(&self) -> DispatchResult {
355 if let CollatorStatus::Leaving(when) = self.status {
356 ensure!(
357 <Round<T>>::get().current >= when,
358 Error::<T>::CandidateCannotLeaveYet
359 );
360 Ok(())
361 } else {
362 Err(Error::<T>::CandidateNotLeaving.into())
363 }
364 }
365 pub fn go_offline(&mut self) {
366 self.status = CollatorStatus::Idle;
367 }
368 pub fn go_online(&mut self) {
369 self.status = CollatorStatus::Active;
370 }
371 pub fn bond_more<T: Config>(&mut self, who: T::AccountId, more: Balance) -> DispatchResult
372 where
373 BalanceOf<T>: From<Balance>,
374 {
375 ensure!(
376 <Pallet<T>>::get_collator_stakable_free_balance(&who) >= more.into(),
377 Error::<T>::InsufficientBalance
378 );
379 let new_total = <Total<T>>::get().saturating_add(more.into());
380 <Total<T>>::put(new_total);
381 self.bond = self.bond.saturating_add(more);
382
383 <Pallet<T>>::freeze_extended(&who.clone(), self.bond.into(), true)
385 .map_err(|_| DispatchError::from(Error::<T>::InsufficientBalance))?;
386
387 self.total_counted = self.total_counted.saturating_add(more);
388 <Pallet<T>>::deposit_event(Event::CandidateBondedMore {
389 candidate: who.clone(),
390 amount: more.into(),
391 new_total_bond: self.bond.into(),
392 });
393 Ok(())
394 }
395
396 pub fn bond_less<T: Config>(&mut self, who: T::AccountId, amount: Balance) -> DispatchResult
397 where
398 BalanceOf<T>: From<Balance>,
399 {
400 let new_total_staked = <Total<T>>::get().saturating_sub(amount.into());
401 <Total<T>>::put(new_total_staked);
402 self.bond = self.bond.saturating_sub(amount);
403
404 if self.bond.is_zero() {
406 <Pallet<T>>::thaw_extended(&who, true)?;
407 } else {
408 <Pallet<T>>::freeze_extended(&who, self.bond.into(), true)?;
409 }
410 self.total_counted = self.total_counted.saturating_sub(amount);
411 let event = Event::CandidateBondedLess {
412 candidate: who.clone(),
413 amount: amount.into(),
414 new_bond: self.bond.into(),
415 };
416 if self.is_active() {
418 Pallet::<T>::update_active(who, self.total_counted.into());
419 }
420 Pallet::<T>::deposit_event(event);
421 Ok(())
422 }
423
424 pub fn schedule_bond_less<T: Config>(
427 &mut self,
428 less: Balance,
429 ) -> Result<RoundIndex, DispatchError>
430 where
431 BalanceOf<T>: Into<Balance>,
432 {
433 ensure!(
435 self.request.is_none(),
436 Error::<T>::PendingCandidateRequestAlreadyExists
437 );
438 ensure!(self.bond > less, Error::<T>::CandidateBondBelowMin);
440 ensure!(
441 self.bond - less >= T::MinCandidateStk::get().into(),
442 Error::<T>::CandidateBondBelowMin
443 );
444 let when_executable = <Round<T>>::get().current + T::CandidateBondLessDelay::get();
445 self.request = Some(CandidateBondLessRequest {
446 amount: less,
447 when_executable,
448 });
449 Ok(when_executable)
450 }
451 pub fn execute_bond_less<T: Config>(&mut self, who: T::AccountId) -> DispatchResult
454 where
455 BalanceOf<T>: From<Balance>,
456 {
457 let request = self
458 .request
459 .ok_or(Error::<T>::PendingCandidateRequestsDNE)?;
460 ensure!(
461 request.when_executable <= <Round<T>>::get().current,
462 Error::<T>::PendingCandidateRequestNotDueYet
463 );
464 self.bond_less::<T>(who.clone(), request.amount)?;
465 self.request = None;
467 Ok(())
468 }
469
470 pub fn cancel_bond_less<T: Config>(&mut self, who: T::AccountId) -> DispatchResult
472 where
473 BalanceOf<T>: From<Balance>,
474 {
475 let request = self
476 .request
477 .ok_or(Error::<T>::PendingCandidateRequestsDNE)?;
478 let event = Event::CancelledCandidateBondLess {
479 candidate: who.clone().into(),
480 amount: request.amount.into(),
481 execute_round: request.when_executable,
482 };
483 self.request = None;
484 Pallet::<T>::deposit_event(event);
485 Ok(())
486 }
487 pub fn reset_top_data<T: Config>(
489 &mut self,
490 candidate: T::AccountId,
491 top_delegations: &Delegations<T::AccountId, BalanceOf<T>>,
492 ) where
493 BalanceOf<T>: Into<Balance> + From<Balance>,
494 {
495 self.lowest_top_delegation_amount = top_delegations.lowest_delegation_amount().into();
496 self.top_capacity = top_delegations.top_capacity::<T>();
497 let old_total_counted = self.total_counted;
498 self.total_counted = self.bond.saturating_add(top_delegations.total.into());
499 if old_total_counted != self.total_counted && self.is_active() {
503 Pallet::<T>::update_active(candidate, self.total_counted.into());
504 }
505 }
506 pub fn reset_bottom_data<T: Config>(
508 &mut self,
509 bottom_delegations: &Delegations<T::AccountId, BalanceOf<T>>,
510 ) where
511 BalanceOf<T>: Into<Balance>,
512 {
513 self.lowest_bottom_delegation_amount = bottom_delegations.lowest_delegation_amount().into();
514 self.highest_bottom_delegation_amount =
515 bottom_delegations.highest_delegation_amount().into();
516 self.bottom_capacity = bottom_delegations.bottom_capacity::<T>();
517 }
518 pub fn add_delegation<T: Config>(
523 &mut self,
524 candidate: &T::AccountId,
525 delegation: Bond<T::AccountId, BalanceOf<T>>,
526 ) -> Result<(DelegatorAdded<Balance>, Option<Balance>), DispatchError>
527 where
528 BalanceOf<T>: Into<Balance> + From<Balance>,
529 {
530 let mut less_total_staked = None;
531 let delegator_added = match self.top_capacity {
532 CapacityStatus::Full => {
533 if self.lowest_top_delegation_amount < delegation.amount.into() {
535 less_total_staked = self.add_top_delegation::<T>(candidate, delegation);
537 DelegatorAdded::AddedToTop {
538 new_total: self.total_counted,
539 }
540 } else {
541 if matches!(self.bottom_capacity, CapacityStatus::Full) {
544 ensure!(
545 delegation.amount.into() > self.lowest_bottom_delegation_amount,
546 Error::<T>::CannotDelegateLessThanOrEqualToLowestBottomWhenFull
547 );
548 less_total_staked = Some(self.lowest_bottom_delegation_amount);
550 }
551 self.add_bottom_delegation::<T>(false, candidate, delegation);
553 DelegatorAdded::AddedToBottom
554 }
555 }
556 _ => {
558 self.add_top_delegation::<T>(candidate, delegation);
559 DelegatorAdded::AddedToTop {
560 new_total: self.total_counted,
561 }
562 }
563 };
564 Ok((delegator_added, less_total_staked))
565 }
566 pub fn add_top_delegation<T: Config>(
570 &mut self,
571 candidate: &T::AccountId,
572 delegation: Bond<T::AccountId, BalanceOf<T>>,
573 ) -> Option<Balance>
574 where
575 BalanceOf<T>: Into<Balance> + From<Balance>,
576 {
577 let mut less_total_staked = None;
578 let mut top_delegations = <TopDelegations<T>>::get(candidate)
579 .expect("CandidateInfo existence => TopDelegations existence");
580 let max_top_delegations_per_candidate = T::MaxTopDelegationsPerCandidate::get();
581 if top_delegations.delegations.len() as u32 == max_top_delegations_per_candidate {
582 let new_bottom_delegation = top_delegations.delegations.pop().expect("");
584 top_delegations.total = top_delegations
585 .total
586 .saturating_sub(new_bottom_delegation.amount);
587 if matches!(self.bottom_capacity, CapacityStatus::Full) {
588 less_total_staked = Some(self.lowest_bottom_delegation_amount);
589 }
590 self.add_bottom_delegation::<T>(true, candidate, new_bottom_delegation);
591 }
592 top_delegations.insert_sorted_greatest_to_least(delegation);
594 self.reset_top_data::<T>(candidate.clone(), &top_delegations);
596 if less_total_staked.is_none() {
597 self.delegation_count = self.delegation_count.saturating_add(1u32);
599 }
600 <TopDelegations<T>>::insert(&candidate, top_delegations);
601 less_total_staked
602 }
603 pub fn add_bottom_delegation<T: Config>(
608 &mut self,
609 bumped_from_top: bool,
610 candidate: &T::AccountId,
611 delegation: Bond<T::AccountId, BalanceOf<T>>,
612 ) where
613 BalanceOf<T>: Into<Balance> + From<Balance>,
614 {
615 let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
616 .expect("CandidateInfo existence => BottomDelegations existence");
617 let increase_delegation_count = if bottom_delegations.delegations.len() as u32
620 == T::MaxBottomDelegationsPerCandidate::get()
621 {
622 let lowest_bottom_to_be_kicked = bottom_delegations
623 .delegations
624 .pop()
625 .expect("if at full capacity (>0), then >0 bottom delegations exist; qed");
626 bottom_delegations.total = bottom_delegations
630 .total
631 .saturating_sub(lowest_bottom_to_be_kicked.amount);
632 let mut delegator_state = <DelegatorState<T>>::get(&lowest_bottom_to_be_kicked.owner)
636 .expect("Delegation existence => DelegatorState existence");
637 let leaving = delegator_state.delegations.0.len() == 1usize;
638 delegator_state.rm_delegation::<T>(candidate);
639 <Pallet<T>>::delegation_remove_request_with_state(
640 &candidate,
641 &lowest_bottom_to_be_kicked.owner,
642 &mut delegator_state,
643 );
644 <AutoCompoundDelegations<T>>::remove_auto_compound(
645 &candidate,
646 &lowest_bottom_to_be_kicked.owner,
647 );
648
649 Pallet::<T>::deposit_event(Event::DelegationKicked {
650 delegator: lowest_bottom_to_be_kicked.owner.clone(),
651 candidate: candidate.clone(),
652 unstaked_amount: lowest_bottom_to_be_kicked.amount,
653 });
654 if leaving {
655 <DelegatorState<T>>::remove(&lowest_bottom_to_be_kicked.owner);
656 Pallet::<T>::deposit_event(Event::DelegatorLeft {
657 delegator: lowest_bottom_to_be_kicked.owner,
658 unstaked_amount: lowest_bottom_to_be_kicked.amount,
659 });
660 } else {
661 <DelegatorState<T>>::insert(&lowest_bottom_to_be_kicked.owner, delegator_state);
662 }
663 false
664 } else {
665 !bumped_from_top
666 };
667 if increase_delegation_count {
670 self.delegation_count = self.delegation_count.saturating_add(1u32);
671 }
672 bottom_delegations.insert_sorted_greatest_to_least(delegation);
673 self.reset_bottom_data::<T>(&bottom_delegations);
674 <BottomDelegations<T>>::insert(candidate, bottom_delegations);
675 }
676 pub fn rm_delegation_if_exists<T: Config>(
680 &mut self,
681 candidate: &T::AccountId,
682 delegator: T::AccountId,
683 amount: Balance,
684 ) -> Result<bool, DispatchError>
685 where
686 BalanceOf<T>: Into<Balance> + From<Balance>,
687 {
688 let amount_geq_lowest_top = amount >= self.lowest_top_delegation_amount;
689 let top_is_not_full = !matches!(self.top_capacity, CapacityStatus::Full);
690 let lowest_top_eq_highest_bottom =
691 self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
692 let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
693 if top_is_not_full || (amount_geq_lowest_top && !lowest_top_eq_highest_bottom) {
694 self.rm_top_delegation::<T>(candidate, delegator)
695 } else if amount_geq_lowest_top && lowest_top_eq_highest_bottom {
696 let result = self.rm_top_delegation::<T>(candidate, delegator.clone());
697 if result == Err(delegation_dne_err) {
698 self.rm_bottom_delegation::<T>(candidate, delegator)
700 } else {
701 result
702 }
703 } else {
704 self.rm_bottom_delegation::<T>(candidate, delegator)
705 }
706 }
707 pub fn rm_top_delegation<T: Config>(
709 &mut self,
710 candidate: &T::AccountId,
711 delegator: T::AccountId,
712 ) -> Result<bool, DispatchError>
713 where
714 BalanceOf<T>: Into<Balance> + From<Balance>,
715 {
716 let old_total_counted = self.total_counted;
717 let mut top_delegations = <TopDelegations<T>>::get(candidate)
719 .expect("CandidateInfo exists => TopDelegations exists");
720 let mut actual_amount_option: Option<BalanceOf<T>> = None;
721 top_delegations.delegations = top_delegations
722 .delegations
723 .clone()
724 .into_iter()
725 .filter(|d| {
726 if d.owner != delegator {
727 true
728 } else {
729 actual_amount_option = Some(d.amount);
730 false
731 }
732 })
733 .collect();
734 let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
735 top_delegations.total = top_delegations.total.saturating_sub(actual_amount);
736 if !matches!(self.bottom_capacity, CapacityStatus::Empty) {
738 let mut bottom_delegations =
739 <BottomDelegations<T>>::get(candidate).expect("bottom is nonempty as just checked");
740 let highest_bottom_delegation = bottom_delegations.delegations.remove(0);
742 bottom_delegations.total = bottom_delegations
743 .total
744 .saturating_sub(highest_bottom_delegation.amount);
745 self.reset_bottom_data::<T>(&bottom_delegations);
746 <BottomDelegations<T>>::insert(candidate, bottom_delegations);
747 top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
749 }
750 self.reset_top_data::<T>(candidate.clone(), &top_delegations);
752 self.delegation_count = self.delegation_count.saturating_sub(1u32);
753 <TopDelegations<T>>::insert(candidate, top_delegations);
754 Ok(old_total_counted == self.total_counted)
756 }
757 pub fn rm_bottom_delegation<T: Config>(
760 &mut self,
761 candidate: &T::AccountId,
762 delegator: T::AccountId,
763 ) -> Result<bool, DispatchError>
764 where
765 BalanceOf<T>: Into<Balance>,
766 {
767 let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
769 .expect("CandidateInfo exists => BottomDelegations exists");
770 let mut actual_amount_option: Option<BalanceOf<T>> = None;
771 bottom_delegations.delegations = bottom_delegations
772 .delegations
773 .clone()
774 .into_iter()
775 .filter(|d| {
776 if d.owner != delegator {
777 true
778 } else {
779 actual_amount_option = Some(d.amount);
780 false
781 }
782 })
783 .collect();
784 let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
785 bottom_delegations.total = bottom_delegations.total.saturating_sub(actual_amount);
786 self.reset_bottom_data::<T>(&bottom_delegations);
788 self.delegation_count = self.delegation_count.saturating_sub(1u32);
789 <BottomDelegations<T>>::insert(candidate, bottom_delegations);
790 Ok(false)
791 }
792 pub fn increase_delegation<T: Config>(
794 &mut self,
795 candidate: &T::AccountId,
796 delegator: T::AccountId,
797 bond: BalanceOf<T>,
798 more: BalanceOf<T>,
799 ) -> Result<bool, DispatchError>
800 where
801 BalanceOf<T>: Into<Balance> + From<Balance>,
802 {
803 let lowest_top_eq_highest_bottom =
804 self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
805 let bond_geq_lowest_top = bond.into() >= self.lowest_top_delegation_amount;
806 let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
807 if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
808 self.increase_top_delegation::<T>(candidate, delegator.clone(), more)
810 } else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
811 let result = self.increase_top_delegation::<T>(candidate, delegator.clone(), more);
814 if result == Err(delegation_dne_err) {
815 self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
816 } else {
817 result
818 }
819 } else {
820 self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
821 }
822 }
823 pub fn increase_top_delegation<T: Config>(
825 &mut self,
826 candidate: &T::AccountId,
827 delegator: T::AccountId,
828 more: BalanceOf<T>,
829 ) -> Result<bool, DispatchError>
830 where
831 BalanceOf<T>: Into<Balance> + From<Balance>,
832 {
833 let mut top_delegations = <TopDelegations<T>>::get(candidate)
834 .expect("CandidateInfo exists => TopDelegations exists");
835 let mut in_top = false;
836 top_delegations.delegations = top_delegations
837 .delegations
838 .clone()
839 .into_iter()
840 .map(|d| {
841 if d.owner != delegator {
842 d
843 } else {
844 in_top = true;
845 let new_amount = d.amount.saturating_add(more);
846 Bond {
847 owner: d.owner,
848 amount: new_amount,
849 }
850 }
851 })
852 .collect();
853 ensure!(in_top, Error::<T>::DelegationDNE);
854 top_delegations.total = top_delegations.total.saturating_add(more);
855 top_delegations.sort_greatest_to_least();
856 self.reset_top_data::<T>(candidate.clone(), &top_delegations);
857 <TopDelegations<T>>::insert(candidate, top_delegations);
858 Ok(true)
859 }
860 pub fn increase_bottom_delegation<T: Config>(
862 &mut self,
863 candidate: &T::AccountId,
864 delegator: T::AccountId,
865 bond: BalanceOf<T>,
866 more: BalanceOf<T>,
867 ) -> Result<bool, DispatchError>
868 where
869 BalanceOf<T>: Into<Balance> + From<Balance>,
870 {
871 let mut bottom_delegations =
872 <BottomDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
873 let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
874 let in_top_after = if (bond.saturating_add(more)).into() > self.lowest_top_delegation_amount
875 {
876 bottom_delegations.delegations = bottom_delegations
878 .delegations
879 .clone()
880 .into_iter()
881 .filter(|d| {
882 if d.owner != delegator {
883 true
884 } else {
885 delegation_option = Some(Bond {
886 owner: d.owner.clone(),
887 amount: d.amount.saturating_add(more),
888 });
889 false
890 }
891 })
892 .collect();
893 let delegation = delegation_option.ok_or(Error::<T>::DelegationDNE)?;
894 bottom_delegations.total = bottom_delegations.total.saturating_sub(bond);
895 let mut top_delegations = <TopDelegations<T>>::get(candidate)
897 .expect("CandidateInfo existence => TopDelegations existence");
898 if matches!(top_delegations.top_capacity::<T>(), CapacityStatus::Full) {
900 let new_bottom_delegation = top_delegations
902 .delegations
903 .pop()
904 .expect("Top capacity full => Exists at least 1 top delegation");
905 top_delegations.total = top_delegations
906 .total
907 .saturating_sub(new_bottom_delegation.amount);
908 bottom_delegations.insert_sorted_greatest_to_least(new_bottom_delegation);
909 }
910 top_delegations.insert_sorted_greatest_to_least(delegation);
912 self.reset_top_data::<T>(candidate.clone(), &top_delegations);
913 <TopDelegations<T>>::insert(candidate, top_delegations);
914 true
915 } else {
916 let mut in_bottom = false;
917 bottom_delegations.delegations = bottom_delegations
919 .delegations
920 .clone()
921 .into_iter()
922 .map(|d| {
923 if d.owner != delegator {
924 d
925 } else {
926 in_bottom = true;
927 Bond {
928 owner: d.owner,
929 amount: d.amount.saturating_add(more),
930 }
931 }
932 })
933 .collect();
934 ensure!(in_bottom, Error::<T>::DelegationDNE);
935 bottom_delegations.total = bottom_delegations.total.saturating_add(more);
936 bottom_delegations.sort_greatest_to_least();
937 false
938 };
939 self.reset_bottom_data::<T>(&bottom_delegations);
940 <BottomDelegations<T>>::insert(candidate, bottom_delegations);
941 Ok(in_top_after)
942 }
943 pub fn decrease_delegation<T: Config>(
945 &mut self,
946 candidate: &T::AccountId,
947 delegator: T::AccountId,
948 bond: Balance,
949 less: BalanceOf<T>,
950 ) -> Result<bool, DispatchError>
951 where
952 BalanceOf<T>: Into<Balance> + From<Balance>,
953 {
954 let lowest_top_eq_highest_bottom =
955 self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
956 let bond_geq_lowest_top = bond >= self.lowest_top_delegation_amount;
957 let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
958 if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
959 self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less)
961 } else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
962 let result =
965 self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less);
966 if result == Err(delegation_dne_err) {
967 self.decrease_bottom_delegation::<T>(candidate, delegator, less)
968 } else {
969 result
970 }
971 } else {
972 self.decrease_bottom_delegation::<T>(candidate, delegator, less)
973 }
974 }
975 pub fn decrease_top_delegation<T: Config>(
977 &mut self,
978 candidate: &T::AccountId,
979 delegator: T::AccountId,
980 bond: BalanceOf<T>,
981 less: BalanceOf<T>,
982 ) -> Result<bool, DispatchError>
983 where
984 BalanceOf<T>: Into<Balance> + From<Balance>,
985 {
986 let bond_after_less_than_highest_bottom =
989 bond.saturating_sub(less).into() < self.highest_bottom_delegation_amount;
990 let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full)
992 && !matches!(self.bottom_capacity, CapacityStatus::Empty);
993 let mut top_delegations =
994 <TopDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
995 let in_top_after = if bond_after_less_than_highest_bottom && full_top_and_nonempty_bottom {
996 let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
997 top_delegations.delegations = top_delegations
999 .delegations
1000 .clone()
1001 .into_iter()
1002 .filter(|d| {
1003 if d.owner != delegator {
1004 true
1005 } else {
1006 top_delegations.total = top_delegations.total.saturating_sub(d.amount);
1007 delegation_option = Some(Bond {
1008 owner: d.owner.clone(),
1009 amount: d.amount.saturating_sub(less),
1010 });
1011 false
1012 }
1013 })
1014 .collect();
1015 let delegation = delegation_option.ok_or(Error::<T>::DelegationDNE)?;
1016 let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
1018 .expect("CandidateInfo existence => BottomDelegations existence");
1019 let highest_bottom_delegation = bottom_delegations.delegations.remove(0);
1020 bottom_delegations.total = bottom_delegations
1021 .total
1022 .saturating_sub(highest_bottom_delegation.amount);
1023 top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
1025 bottom_delegations.insert_sorted_greatest_to_least(delegation);
1027 self.reset_bottom_data::<T>(&bottom_delegations);
1028 <BottomDelegations<T>>::insert(candidate, bottom_delegations);
1029 false
1030 } else {
1031 let mut is_in_top = false;
1033 top_delegations.delegations = top_delegations
1034 .delegations
1035 .clone()
1036 .into_iter()
1037 .map(|d| {
1038 if d.owner != delegator {
1039 d
1040 } else {
1041 is_in_top = true;
1042 Bond {
1043 owner: d.owner,
1044 amount: d.amount.saturating_sub(less),
1045 }
1046 }
1047 })
1048 .collect();
1049 ensure!(is_in_top, Error::<T>::DelegationDNE);
1050 top_delegations.total = top_delegations.total.saturating_sub(less);
1051 top_delegations.sort_greatest_to_least();
1052 true
1053 };
1054 self.reset_top_data::<T>(candidate.clone(), &top_delegations);
1055 <TopDelegations<T>>::insert(candidate, top_delegations);
1056 Ok(in_top_after)
1057 }
1058 pub fn decrease_bottom_delegation<T: Config>(
1060 &mut self,
1061 candidate: &T::AccountId,
1062 delegator: T::AccountId,
1063 less: BalanceOf<T>,
1064 ) -> Result<bool, DispatchError>
1065 where
1066 BalanceOf<T>: Into<Balance>,
1067 {
1068 let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
1069 .expect("CandidateInfo exists => BottomDelegations exists");
1070 let mut in_bottom = false;
1071 bottom_delegations.delegations = bottom_delegations
1072 .delegations
1073 .clone()
1074 .into_iter()
1075 .map(|d| {
1076 if d.owner != delegator {
1077 d
1078 } else {
1079 in_bottom = true;
1080 Bond {
1081 owner: d.owner,
1082 amount: d.amount.saturating_sub(less),
1083 }
1084 }
1085 })
1086 .collect();
1087 ensure!(in_bottom, Error::<T>::DelegationDNE);
1088 bottom_delegations.sort_greatest_to_least();
1089 self.reset_bottom_data::<T>(&bottom_delegations);
1090 <BottomDelegations<T>>::insert(candidate, bottom_delegations);
1091 Ok(false)
1092 }
1093}
1094
1095#[derive(Clone, Copy, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, DecodeWithMemTracking)]
1098pub enum DelegatorAdded<B> {
1099 AddedToTop { new_total: B },
1100 AddedToBottom,
1101}
1102
1103#[allow(deprecated)]
1104#[derive(Clone, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
1105pub enum DelegatorStatus {
1106 Active,
1108 #[deprecated(note = "must only be used for backwards compatibility reasons")]
1110 Leaving(RoundIndex),
1111}
1112
1113#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
1114pub struct Delegator<AccountId, Balance> {
1116 pub id: AccountId,
1118 pub delegations: OrderedSet<Bond<AccountId, Balance>>,
1120 pub total: Balance,
1122 pub less_total: Balance,
1124 pub status: DelegatorStatus,
1126}
1127
1128impl<A: PartialEq, B: PartialEq> PartialEq for Delegator<A, B> {
1130 fn eq(&self, other: &Self) -> bool {
1131 let must_be_true = self.id == other.id
1132 && self.total == other.total
1133 && self.less_total == other.less_total
1134 && self.status == other.status;
1135 if !must_be_true {
1136 return false;
1137 }
1138 for (
1139 Bond {
1140 owner: o1,
1141 amount: a1,
1142 },
1143 Bond {
1144 owner: o2,
1145 amount: a2,
1146 },
1147 ) in self.delegations.0.iter().zip(other.delegations.0.iter())
1148 {
1149 if o1 != o2 || a1 != a2 {
1150 return false;
1151 }
1152 }
1153 true
1154 }
1155}
1156
1157impl<
1158 AccountId: Ord + Clone,
1159 Balance: Copy
1160 + sp_std::ops::AddAssign
1161 + sp_std::ops::Add<Output = Balance>
1162 + sp_std::ops::SubAssign
1163 + sp_std::ops::Sub<Output = Balance>
1164 + Ord
1165 + Zero
1166 + Default
1167 + Saturating,
1168 > Delegator<AccountId, Balance>
1169{
1170 pub fn new(id: AccountId, collator: AccountId, amount: Balance) -> Self {
1171 Delegator {
1172 id,
1173 delegations: OrderedSet::from(vec![Bond {
1174 owner: collator,
1175 amount,
1176 }]),
1177 total: amount,
1178 less_total: Balance::zero(),
1179 status: DelegatorStatus::Active,
1180 }
1181 }
1182
1183 pub fn default_with_total(id: AccountId, amount: Balance) -> Self {
1184 Delegator {
1185 id,
1186 total: amount,
1187 delegations: OrderedSet::from(vec![]),
1188 less_total: Balance::zero(),
1189 status: DelegatorStatus::Active,
1190 }
1191 }
1192
1193 pub fn total(&self) -> Balance {
1194 self.total
1195 }
1196
1197 pub fn total_sub_if<T, F>(&mut self, amount: Balance, check: F) -> DispatchResult
1198 where
1199 T: Config,
1200 T::AccountId: From<AccountId>,
1201 BalanceOf<T>: From<Balance>,
1202 F: Fn(Balance) -> DispatchResult,
1203 {
1204 let total = self.total.saturating_sub(amount);
1205 check(total)?;
1206 self.total = total;
1207 self.adjust_bond_lock::<T>(BondAdjust::Decrease)?;
1208 Ok(())
1209 }
1210
1211 pub fn total_add<T, F>(&mut self, amount: Balance) -> DispatchResult
1212 where
1213 T: Config,
1214 T::AccountId: From<AccountId>,
1215 BalanceOf<T>: From<Balance>,
1216 {
1217 self.total = self.total.saturating_add(amount);
1218 self.adjust_bond_lock::<T>(BondAdjust::Increase(amount))?;
1219 Ok(())
1220 }
1221
1222 pub fn total_sub<T>(&mut self, amount: Balance) -> DispatchResult
1223 where
1224 T: Config,
1225 T::AccountId: From<AccountId>,
1226 BalanceOf<T>: From<Balance>,
1227 {
1228 self.total = self.total.saturating_sub(amount);
1229 self.adjust_bond_lock::<T>(BondAdjust::Decrease)?;
1230 Ok(())
1231 }
1232
1233 pub fn is_active(&self) -> bool {
1234 matches!(self.status, DelegatorStatus::Active)
1235 }
1236
1237 pub fn add_delegation(&mut self, bond: Bond<AccountId, Balance>) -> bool {
1238 let amt = bond.amount;
1239 if self.delegations.insert(bond) {
1240 self.total = self.total.saturating_add(amt);
1241 true
1242 } else {
1243 false
1244 }
1245 }
1246 pub fn rm_delegation<T: Config>(&mut self, collator: &AccountId) -> Option<Balance>
1249 where
1250 BalanceOf<T>: From<Balance>,
1251 T::AccountId: From<AccountId>,
1252 {
1253 let mut amt: Option<Balance> = None;
1254 let delegations = self
1255 .delegations
1256 .0
1257 .iter()
1258 .filter_map(|x| {
1259 if &x.owner == collator {
1260 amt = Some(x.amount);
1261 None
1262 } else {
1263 Some(x.clone())
1264 }
1265 })
1266 .collect();
1267 if let Some(balance) = amt {
1268 self.delegations = OrderedSet::from(delegations);
1269 self.total_sub::<T>(balance)
1270 .expect("Decreasing lock cannot fail, qed");
1271 Some(self.total)
1272 } else {
1273 None
1274 }
1275 }
1276
1277 pub fn increase_delegation<T: Config>(
1280 &mut self,
1281 candidate: AccountId,
1282 amount: Balance,
1283 ) -> Result<bool, sp_runtime::DispatchError>
1284 where
1285 BalanceOf<T>: From<Balance>,
1286 T::AccountId: From<AccountId>,
1287 Delegator<T::AccountId, BalanceOf<T>>: From<Delegator<AccountId, Balance>>,
1288 {
1289 let delegator_id: T::AccountId = self.id.clone().into();
1290 let candidate_id: T::AccountId = candidate.clone().into();
1291 let balance_amt: BalanceOf<T> = amount.into();
1292 for x in &mut self.delegations.0 {
1294 if x.owner == candidate {
1295 let before_amount: BalanceOf<T> = x.amount.into();
1296 x.amount = x.amount.saturating_add(amount);
1297 self.total = self.total.saturating_add(amount);
1298 self.adjust_bond_lock::<T>(BondAdjust::Increase(amount))?;
1299
1300 let mut collator_state =
1302 <CandidateInfo<T>>::get(&candidate_id).ok_or(Error::<T>::CandidateDNE)?;
1303 let before = collator_state.total_counted;
1304 let in_top = collator_state.increase_delegation::<T>(
1305 &candidate_id,
1306 delegator_id.clone(),
1307 before_amount,
1308 balance_amt,
1309 )?;
1310 let after = collator_state.total_counted;
1311 if collator_state.is_active() && (before != after) {
1312 Pallet::<T>::update_active(candidate_id.clone(), after);
1313 }
1314 <CandidateInfo<T>>::insert(&candidate_id, collator_state);
1315 let new_total_staked = <Total<T>>::get().saturating_add(balance_amt);
1316 <Total<T>>::put(new_total_staked);
1317 let nom_st: Delegator<T::AccountId, BalanceOf<T>> = self.clone().into();
1318 <DelegatorState<T>>::insert(&delegator_id, nom_st);
1319 return Ok(in_top);
1320 }
1321 }
1322 Err(Error::<T>::DelegationDNE.into())
1323 }
1324
1325 pub fn adjust_bond_lock<T: Config>(
1333 &mut self,
1334 additional_required_balance: BondAdjust<Balance>,
1335 ) -> DispatchResult
1336 where
1337 BalanceOf<T>: From<Balance>,
1338 T::AccountId: From<AccountId>,
1339 {
1340 let who: T::AccountId = self.id.clone().into();
1341
1342 match additional_required_balance {
1343 BondAdjust::Increase(amount) => {
1344 ensure!(
1345 <Pallet<T>>::get_delegator_stakable_balance(&who) >= amount.into(),
1346 Error::<T>::InsufficientBalance,
1347 );
1348
1349 if amount > self.total {
1351 log::warn!("LOGIC ERROR: request to freeze more than bond total");
1352 return Err(DispatchError::Other("Invalid additional_required_balance"));
1353 }
1354 }
1355 BondAdjust::Decrease => {
1356 }
1358 }
1359
1360 if self.total > Balance::zero() {
1362 <Pallet<T>>::freeze_extended(&who, self.total.into(), false)?;
1363 } else {
1364 let _ = <Pallet<T>>::thaw_extended(&who, false);
1366 }
1367
1368 Ok(())
1369 }
1370
1371 pub fn get_bond_amount(&self, collator: &AccountId) -> Option<Balance> {
1374 self.delegations
1375 .0
1376 .iter()
1377 .find(|b| &b.owner == collator)
1378 .map(|b| b.amount)
1379 }
1380}
1381
1382#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
1383pub struct RoundInfo<BlockNumber> {
1385 pub current: RoundIndex,
1387 pub first: BlockNumber,
1389 pub length: u32,
1391 pub first_slot: u64,
1393}
1394impl<
1395 B: Copy + sp_std::ops::Add<Output = B> + sp_std::ops::Sub<Output = B> + From<u32> + PartialOrd,
1396 > RoundInfo<B>
1397{
1398 pub fn new(current: RoundIndex, first: B, length: u32, first_slot: u64) -> RoundInfo<B> {
1399 RoundInfo {
1400 current,
1401 first,
1402 length,
1403 first_slot,
1404 }
1405 }
1406 pub fn should_update(&self, now: B) -> bool {
1408 now - self.first >= self.length.into()
1409 }
1410 pub fn update(&mut self, now: B, now_slot: u64) {
1412 self.current = self.current.saturating_add(1u32);
1413 self.first = now;
1414 self.first_slot = now_slot;
1415 }
1416}
1417impl<
1418 B: Copy + sp_std::ops::Add<Output = B> + sp_std::ops::Sub<Output = B> + From<u32> + PartialOrd,
1419 > Default for RoundInfo<B>
1420{
1421 fn default() -> RoundInfo<B> {
1422 RoundInfo::new(1u32, 1u32.into(), 20u32, 0)
1423 }
1424}
1425
1426#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, DecodeWithMemTracking)]
1428pub struct InflationDistributionConfig<AccountId>(
1429 pub(crate) [InflationDistributionAccount<AccountId>; 2],
1430);
1431
1432impl<AccountId> From<[InflationDistributionAccount<AccountId>; 2]>
1433 for InflationDistributionConfig<AccountId>
1434{
1435 fn from(configs: [InflationDistributionAccount<AccountId>; 2]) -> Self {
1436 InflationDistributionConfig(configs)
1437 }
1438}
1439
1440impl<AccountId: Decode> Default for InflationDistributionConfig<AccountId> {
1441 fn default() -> InflationDistributionConfig<AccountId> {
1442 InflationDistributionConfig([
1443 InflationDistributionAccount {
1444 account: AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
1445 .expect("infinite length input; no invalid inputs for type; qed"),
1446 percent: Percent::zero(),
1447 },
1448 InflationDistributionAccount {
1449 account: AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
1450 .expect("infinite length input; no invalid inputs for type; qed"),
1451 percent: Percent::zero(),
1452 },
1453 ])
1454 }
1455}
1456
1457#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, DecodeWithMemTracking)]
1458pub struct InflationDistributionAccount<AccountId> {
1460 pub account: AccountId,
1462 pub percent: Percent,
1464}
1465impl<A: Decode> Default for InflationDistributionAccount<A> {
1466 fn default() -> InflationDistributionAccount<A> {
1467 InflationDistributionAccount {
1468 account: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
1469 .expect("infinite length input; no invalid inputs for type; qed"),
1470 percent: Percent::zero(),
1471 }
1472 }
1473}
1474
1475pub enum BondAdjust<Balance> {
1476 Increase(Balance),
1477 Decrease,
1478}