pallet_parachain_staking/
types.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Types for parachain-staking
18
19use 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)]
83/// The activity status of the collator
84pub enum CollatorStatus {
85	/// Committed to be online and producing valid blocks (not equivocating)
86	Active,
87	/// Temporarily inactive and excused for inactivity
88	Idle,
89	/// Bonded until the inner round
90	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)]
118/// Snapshot of collator state at the start of the round for which they are selected
119pub struct CollatorSnapshot<AccountId, Balance> {
120	/// The total value locked by the collator.
121	pub bond: Balance,
122
123	/// The rewardable delegations. This list is a subset of total delegators, where certain
124	/// delegators are adjusted based on their scheduled
125	/// [DelegationChange::Revoke] or [DelegationChange::Decrease] action.
126	pub delegations: Vec<BondWithAutoCompound<AccountId, Balance>>,
127
128	/// The total counted value locked for the collator, including the self bond + total staked by
129	/// top delegators.
130	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)]
171/// Info needed to make delayed payments to stakers after round end
172pub struct DelayedPayout<Balance> {
173	/// Total round reward (result of compute_issuance() at round end)
174	pub round_issuance: Balance,
175	/// The total inflation paid this round to stakers (e.g. less parachain bond fund)
176	pub total_staking_reward: Balance,
177	/// Snapshot of collator commission rate at the end of the round
178	pub collator_commission: Perbill,
179}
180
181#[derive(PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
182/// Request scheduled to change the collator candidate self-bond
183pub struct CandidateBondLessRequest<Balance> {
184	pub amount: Balance,
185	pub when_executable: RoundIndex,
186}
187
188#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
189/// Type for top and bottom delegation storage item
190pub 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	/// Insert sorted greatest to least and increase .total accordingly
211	/// Insertion respects first come first serve so new delegations are pushed after existing
212	/// delegations if the amount is the same
213	pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond<AccountId, Balance>) {
214		self.total = self.total.saturating_add(delegation.amount);
215		// if delegations nonempty && last_element == delegation.amount => push input and return
216		if !self.delegations.is_empty() {
217			// if last_element == delegation.amount => push the delegation and return early
218			if self.delegations[self.delegations.len() - 1].amount == delegation.amount {
219				self.delegations.push(delegation);
220				// early return
221				return;
222			}
223		}
224		// else binary search insertion
225		match self
226			.delegations
227			.binary_search_by(|x| delegation.amount.cmp(&x.amount))
228		{
229			// sorted insertion on sorted vec
230			// enforces first come first serve for equal bond amounts
231			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	/// Return the capacity status for top delegations
247	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	/// Return the capacity status for bottom delegations
255	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	/// Return last delegation amount without popping the delegation
265	pub fn lowest_delegation_amount(&self) -> Balance {
266		self.delegations
267			.last()
268			.map(|x| x.amount)
269			.unwrap_or(Balance::zero())
270	}
271	/// Return highest delegation amount
272	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)]
281/// Capacity status for top or bottom delegations
282pub enum CapacityStatus {
283	/// Reached capacity
284	Full,
285	/// Empty aka contains no delegations
286	Empty,
287	/// Partially full (nonempty and not full)
288	Partial,
289}
290
291#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
292/// All candidate info except the top and bottom delegations
293pub struct CandidateMetadata<Balance> {
294	/// This candidate's self bond amount
295	pub bond: Balance,
296	/// Total number of delegations to this candidate
297	pub delegation_count: u32,
298	/// Self bond + sum of top delegations
299	pub total_counted: Balance,
300	/// The smallest top delegation amount
301	pub lowest_top_delegation_amount: Balance,
302	/// The highest bottom delegation amount
303	pub highest_bottom_delegation_amount: Balance,
304	/// The smallest bottom delegation amount
305	pub lowest_bottom_delegation_amount: Balance,
306	/// Capacity status for top delegations
307	pub top_capacity: CapacityStatus,
308	/// Capacity status for bottom delegations
309	pub bottom_capacity: CapacityStatus,
310	/// Maximum 1 pending request to decrease candidate self bond at any given time
311	pub request: Option<CandidateBondLessRequest<Balance>>,
312	/// Current status of the collator
313	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		// Freeze the total new amount (current + additional)
384		<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		// Update the freeze to the new total amount
405		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		// update candidate pool value because it must change if self bond changes
417		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	/// Schedule executable decrease of collator candidate self bond
425	/// Returns the round at which the collator can execute the pending request
426	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 no pending request
434		ensure!(
435			self.request.is_none(),
436			Error::<T>::PendingCandidateRequestAlreadyExists
437		);
438		// ensure bond above min after decrease
439		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	/// Execute pending request to decrease the collator self bond
452	/// Returns the event to be emitted
453	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		// reset s.t. no pending request
466		self.request = None;
467		Ok(())
468	}
469
470	/// Cancel candidate bond less request
471	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	/// Reset top delegations metadata
488	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		// CandidatePool value for candidate always changes if top delegations total changes
500		// so we moved the update into this function to deduplicate code and patch a bug that
501		// forgot to apply the update when increasing top delegation
502		if old_total_counted != self.total_counted && self.is_active() {
503			Pallet::<T>::update_active(candidate, self.total_counted.into());
504		}
505	}
506	/// Reset bottom delegations metadata
507	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	/// Add delegation
519	/// Returns whether delegator was added and an optional negative total counted remainder
520	/// for if a bottom delegation was kicked
521	/// MUST ensure no delegation exists for this candidate in the `DelegatorState` before call
522	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				// top is full, insert into top iff the lowest_top < amount
534				if self.lowest_top_delegation_amount < delegation.amount.into() {
535					// bumps lowest top to the bottom inside this function call
536					less_total_staked = self.add_top_delegation::<T>(candidate, delegation);
537					DelegatorAdded::AddedToTop {
538						new_total: self.total_counted,
539					}
540				} else {
541					// if bottom is full, only insert if greater than lowest bottom (which will
542					// be bumped out)
543					if matches!(self.bottom_capacity, CapacityStatus::Full) {
544						ensure!(
545							delegation.amount.into() > self.lowest_bottom_delegation_amount,
546							Error::<T>::CannotDelegateLessThanOrEqualToLowestBottomWhenFull
547						);
548						// need to subtract from total staked
549						less_total_staked = Some(self.lowest_bottom_delegation_amount);
550					}
551					// insert into bottom
552					self.add_bottom_delegation::<T>(false, candidate, delegation);
553					DelegatorAdded::AddedToBottom
554				}
555			}
556			// top is either empty or partially full
557			_ => {
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	/// Add delegation to top delegation
567	/// Returns Option<negative_total_staked_remainder>
568	/// Only call if lowest top delegation is less than delegation.amount || !top_full
569	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			// pop lowest top delegation
583			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		// insert into top
593		top_delegations.insert_sorted_greatest_to_least(delegation);
594		// update candidate info
595		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
596		if less_total_staked.is_none() {
597			// only increment delegation count if we are not kicking a bottom delegation
598			self.delegation_count = self.delegation_count.saturating_add(1u32);
599		}
600		<TopDelegations<T>>::insert(&candidate, top_delegations);
601		less_total_staked
602	}
603	/// Add delegation to bottom delegations
604	/// Check before call that if capacity is full, inserted delegation is higher than lowest
605	/// bottom delegation (and if so, need to adjust the total storage item)
606	/// CALLER MUST ensure(lowest_bottom_to_be_kicked.amount < delegation.amount)
607	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		// if bottom is full, kick the lowest bottom (which is expected to be lower than input
618		// as per check)
619		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			// EXPECT lowest_bottom_to_be_kicked.amount < delegation.amount enforced by caller
627			// if lowest_bottom_to_be_kicked.amount == delegation.amount, we will still kick
628			// the lowest bottom to enforce first come first served
629			bottom_delegations.total = bottom_delegations
630				.total
631				.saturating_sub(lowest_bottom_to_be_kicked.amount);
632			// update delegator state
633			// total staked is updated via propagation of lowest bottom delegation amount prior
634			// to call
635			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		// only increase delegation count if new bottom delegation (1) doesn't come from top &&
668		// (2) doesn't pop the lowest delegation from the bottom
669		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	/// Remove delegation
677	/// Removes from top if amount is above lowest top or top is not full
678	/// Return Ok(if_total_counted_changed)
679	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				// worst case removal
699				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	/// Remove top delegation, bumps top bottom delegation if exists
708	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		// remove top delegation
718		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 bottom nonempty => bump top bottom to top
737		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			// expect already stored greatest to least by bond amount
741			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			// insert highest bottom into top delegations
748			top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
749		}
750		// update candidate info
751		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		// return whether total counted changed
755		Ok(old_total_counted == self.total_counted)
756	}
757	/// Remove bottom delegation
758	/// Returns if_total_counted_changed: bool
759	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		// remove bottom delegation
768		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		// update candidate info
787		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	/// Increase delegation amount
793	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			// definitely in top
809			self.increase_top_delegation::<T>(candidate, delegator.clone(), more)
810		} else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
811			// update top but if error then update bottom (because could be in bottom because
812			// lowest_top_eq_highest_bottom)
813			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	/// Increase top delegation
824	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	/// Increase bottom delegation
861	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			// bump it from bottom
877			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			// add it to top
896			let mut top_delegations = <TopDelegations<T>>::get(candidate)
897				.expect("CandidateInfo existence => TopDelegations existence");
898			// if top is full, pop lowest top
899			if matches!(top_delegations.top_capacity::<T>(), CapacityStatus::Full) {
900				// pop lowest top delegation
901				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			// insert into top
911			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			// just increase the delegation
918			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	/// Decrease delegation
944	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			// definitely in top
960			self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less)
961		} else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
962			// update top but if error then update bottom (because could be in bottom because
963			// lowest_top_eq_highest_bottom)
964			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	/// Decrease top delegation
976	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		// The delegation after the `decrease-delegation` will be strictly less than the
987		// highest bottom delegation
988		let bond_after_less_than_highest_bottom =
989			bond.saturating_sub(less).into() < self.highest_bottom_delegation_amount;
990		// The top delegations is full and the bottom delegations has at least one delegation
991		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			// take delegation from top
998			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			// pop highest bottom by reverse and popping
1017			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			// insert highest bottom into top
1024			top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
1025			// insert previous top into bottom
1026			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			// keep it in the top
1032			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	/// Decrease bottom delegation
1059	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/// Convey relevant information describing if a delegator was added to the top or bottom
1096/// Delegations added to the top yield a new total
1097#[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 with no scheduled exit
1107	Active,
1108	/// Schedule exit to revoke all ongoing delegations
1109	#[deprecated(note = "must only be used for backwards compatibility reasons")]
1110	Leaving(RoundIndex),
1111}
1112
1113#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
1114/// Delegator state
1115pub struct Delegator<AccountId, Balance> {
1116	/// Delegator account
1117	pub id: AccountId,
1118	/// All current delegations
1119	pub delegations: OrderedSet<Bond<AccountId, Balance>>,
1120	/// Total frozen balance for this delegator
1121	pub total: Balance,
1122	/// Sum of pending revocation amounts + bond less amounts
1123	pub less_total: Balance,
1124	/// Status for this delegator
1125	pub status: DelegatorStatus,
1126}
1127
1128// Temporary manual implementation for migration testing purposes
1129impl<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	// Return Some(remaining balance), must be more than MinDelegation
1247	// Return None if delegation not found
1248	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	/// Increases the delegation amount and returns `true` if the delegation is part of the
1278	/// TopDelegations set, `false` otherwise.
1279	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		// increase delegation
1293		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				// update collator state delegation
1301				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	/// Updates the bond freezes for this delegator.
1326	///
1327	/// This will take the current self.total and ensure that a freeze of the same amount is applied.
1328	/// When increasing the bond, it will also ensure that the account has enough free balance.
1329	///
1330	/// `additional_required_balance` should reflect the change to the amount that should be frozen if
1331	/// positive, 0 otherwise (e.g. `min(0, change_in_total_bond)`).
1332	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				// additional sanity check: shouldn't ever want to freeze more than total
1350				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				// Nothing special to do for decrease with freezes
1357			}
1358		}
1359
1360		// Set the freeze to the total amount
1361		if self.total > Balance::zero() {
1362			<Pallet<T>>::freeze_extended(&who, self.total.into(), false)?;
1363		} else {
1364			// If total is zero, remove the freeze
1365			let _ = <Pallet<T>>::thaw_extended(&who, false);
1366		}
1367
1368		Ok(())
1369	}
1370
1371	/// Retrieves the bond amount that a delegator has provided towards a collator.
1372	/// Returns `None` if missing.
1373	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)]
1383/// The current round index and transition information
1384pub struct RoundInfo<BlockNumber> {
1385	/// Current round index
1386	pub current: RoundIndex,
1387	/// The first block of the current round
1388	pub first: BlockNumber,
1389	/// The length of the current round in number of blocks
1390	pub length: u32,
1391	/// The first slot of the current round
1392	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	/// Check if the round should be updated
1407	pub fn should_update(&self, now: B) -> bool {
1408		now - self.first >= self.length.into()
1409	}
1410	/// New round
1411	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// Type which encapsulates the configuration for the inflation distribution.
1427#[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)]
1458/// Reserve information { account, percent_of_inflation }
1459pub struct InflationDistributionAccount<AccountId> {
1460	/// Account which receives funds
1461	pub account: AccountId,
1462	/// Percent of inflation set aside for the account
1463	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}