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

            
19
use crate::{
20
	auto_compound::AutoCompoundDelegations, set::OrderedSet, BalanceOf, BottomDelegations,
21
	CandidateInfo, Config, DelegatorState, Error, Event, Pallet, Round, RoundIndex, TopDelegations,
22
	Total,
23
};
24
use frame_support::pallet_prelude::*;
25
use parity_scale_codec::{Decode, Encode};
26
use sp_runtime::{
27
	traits::{Saturating, Zero},
28
	Perbill, Percent, RuntimeDebug,
29
};
30
use sp_std::{cmp::Ordering, prelude::*};
31

            
32
pub struct CountedDelegations<T: Config> {
33
	pub uncounted_stake: BalanceOf<T>,
34
	pub rewardable_delegations: Vec<Bond<T::AccountId, BalanceOf<T>>>,
35
}
36

            
37
38
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
38
pub struct Bond<AccountId, Balance> {
39
	pub owner: AccountId,
40
	pub amount: Balance,
41
}
42

            
43
impl<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

            
53
impl<A, B: Default> Bond<A, B> {
54
580
	pub fn from_owner(owner: A) -> Self {
55
580
		Bond {
56
580
			owner,
57
580
			amount: B::default(),
58
580
		}
59
580
	}
60
}
61

            
62
impl<AccountId: Ord, Balance> Eq for Bond<AccountId, Balance> {}
63

            
64
impl<AccountId: Ord, Balance> Ord for Bond<AccountId, Balance> {
65
4477
	fn cmp(&self, other: &Self) -> Ordering {
66
4477
		self.owner.cmp(&other.owner)
67
4477
	}
68
}
69

            
70
impl<AccountId: Ord, Balance> PartialOrd for Bond<AccountId, Balance> {
71
4
	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
72
4
		Some(self.cmp(other))
73
4
	}
74
}
75

            
76
impl<AccountId: Ord, Balance> PartialEq for Bond<AccountId, Balance> {
77
4
	fn eq(&self, other: &Self) -> bool {
78
4
		self.owner == other.owner
79
4
	}
80
}
81

            
82
180
#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
83
/// The activity status of the collator
84
pub enum CollatorStatus {
85
10268
	/// Committed to be online and producing valid blocks (not equivocating)
86
	Active,
87
7
	/// Temporarily inactive and excused for inactivity
88
	Idle,
89
	/// Bonded until the inner round
90
	Leaving(RoundIndex),
91
}
92

            
93
impl Default for CollatorStatus {
94
	fn default() -> CollatorStatus {
95
		CollatorStatus::Active
96
	}
97
}
98

            
99
57
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
100
pub struct BondWithAutoCompound<AccountId, Balance> {
101
	pub owner: AccountId,
102
	pub amount: Balance,
103
	pub auto_compound: Percent,
104
}
105

            
106
impl<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
57
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
118
/// Snapshot of collator state at the start of the round for which they are selected
119
pub 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

            
133
impl<A: PartialEq, B: PartialEq> PartialEq for CollatorSnapshot<A, B> {
134
5
	fn eq(&self, other: &Self) -> bool {
135
5
		let must_be_true = self.bond == other.bond && self.total == other.total;
136
5
		if !must_be_true {
137
			return false;
138
5
		}
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
5
		) in self.delegations.iter().zip(other.delegations.iter())
151
		{
152
			if o1 != o2 || a1 != a2 || c1 != c2 {
153
				return false;
154
			}
155
		}
156
5
		true
157
5
	}
158
}
159

            
160
impl<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
57
#[derive(Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
171
/// Info needed to make delayed payments to stakers after round end
172
pub 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
38
#[derive(PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
182
/// Request scheduled to change the collator candidate self-bond
183
pub struct CandidateBondLessRequest<Balance> {
184
	pub amount: Balance,
185
	pub when_executable: RoundIndex,
186
}
187

            
188
38
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
189
/// Type for top and bottom delegation storage item
190
pub struct Delegations<AccountId, Balance> {
191
	pub delegations: Vec<Bond<AccountId, Balance>>,
192
	pub total: Balance,
193
}
194

            
195
impl<A, B: Default> Default for Delegations<A, B> {
196
645
	fn default() -> Delegations<A, B> {
197
645
		Delegations {
198
645
			delegations: Vec::new(),
199
645
			total: B::default(),
200
645
		}
201
645
	}
202
}
203

            
204
impl<AccountId, Balance: Copy + Ord + sp_std::ops::AddAssign + Zero + Saturating>
205
	Delegations<AccountId, Balance>
206
{
207
27
	pub fn sort_greatest_to_least(&mut self) {
208
27
		self.delegations.sort_by(|a, b| b.amount.cmp(&a.amount));
209
27
	}
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
632
	pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond<AccountId, Balance>) {
214
632
		self.total = self.total.saturating_add(delegation.amount);
215
632
		// if delegations nonempty && last_element == delegation.amount => push input and return
216
632
		if !self.delegations.is_empty() {
217
			// if last_element == delegation.amount => push the delegation and return early
218
331
			if self.delegations[self.delegations.len() - 1].amount == delegation.amount {
219
260
				self.delegations.push(delegation);
220
260
				// early return
221
260
				return;
222
71
			}
223
301
		}
224
		// else binary search insertion
225
372
		match self
226
372
			.delegations
227
372
			.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
11
			Ok(i) => {
232
11
				let mut new_index = i + 1;
233
11
				while new_index <= (self.delegations.len() - 1) {
234
11
					if self.delegations[new_index].amount == delegation.amount {
235
						new_index = new_index.saturating_add(1);
236
					} else {
237
11
						self.delegations.insert(new_index, delegation);
238
11
						return;
239
					}
240
				}
241
				self.delegations.push(delegation)
242
			}
243
361
			Err(i) => self.delegations.insert(i, delegation),
244
		}
245
632
	}
246
	/// Return the capacity status for top delegations
247
503
	pub fn top_capacity<T: Config>(&self) -> CapacityStatus {
248
503
		match &self.delegations {
249
503
			x if x.len() as u32 >= T::MaxTopDelegationsPerCandidate::get() => CapacityStatus::Full,
250
415
			x if x.is_empty() => CapacityStatus::Empty,
251
393
			_ => CapacityStatus::Partial,
252
		}
253
503
	}
254
	/// Return the capacity status for bottom delegations
255
186
	pub fn bottom_capacity<T: Config>(&self) -> CapacityStatus {
256
186
		match &self.delegations {
257
186
			x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => {
258
52
				CapacityStatus::Full
259
			}
260
134
			x if x.is_empty() => CapacityStatus::Empty,
261
134
			_ => CapacityStatus::Partial,
262
		}
263
186
	}
264
	/// Return last delegation amount without popping the delegation
265
682
	pub fn lowest_delegation_amount(&self) -> Balance {
266
682
		self.delegations
267
682
			.last()
268
682
			.map(|x| x.amount)
269
682
			.unwrap_or(Balance::zero())
270
682
	}
271
	/// Return highest delegation amount
272
186
	pub fn highest_delegation_amount(&self) -> Balance {
273
186
		self.delegations
274
186
			.first()
275
186
			.map(|x| x.amount)
276
186
			.unwrap_or(Balance::zero())
277
186
	}
278
}
279

            
280
180
#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
281
/// Capacity status for top or bottom delegations
282
pub enum CapacityStatus {
283
17268
	/// Reached capacity
284
	Full,
285
2565
	/// Empty aka contains no delegations
286
	Empty,
287
851
	/// Partially full (nonempty and not full)
288
	Partial,
289
}
290

            
291
190
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
292
/// All candidate info except the top and bottom delegations
293
pub 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

            
316
impl<
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
668
	pub fn new(bond: Balance) -> Self {
328
668
		CandidateMetadata {
329
668
			bond,
330
668
			delegation_count: 0u32,
331
668
			total_counted: bond,
332
668
			lowest_top_delegation_amount: Zero::zero(),
333
668
			highest_bottom_delegation_amount: Zero::zero(),
334
668
			lowest_bottom_delegation_amount: Zero::zero(),
335
668
			top_capacity: CapacityStatus::Empty,
336
668
			bottom_capacity: CapacityStatus::Empty,
337
668
			request: None,
338
668
			status: CollatorStatus::Active,
339
668
		}
340
668
	}
341
553
	pub fn is_active(&self) -> bool {
342
553
		matches!(self.status, CollatorStatus::Active)
343
553
	}
344
58
	pub fn is_leaving(&self) -> bool {
345
58
		matches!(self.status, CollatorStatus::Leaving(_))
346
58
	}
347
47
	pub fn schedule_leave<T: Config>(&mut self) -> Result<(RoundIndex, RoundIndex), DispatchError> {
348
47
		ensure!(!self.is_leaving(), Error::<T>::CandidateAlreadyLeaving);
349
46
		let now = <Round<T>>::get().current;
350
46
		let when = now + T::LeaveCandidatesDelay::get();
351
46
		self.status = CollatorStatus::Leaving(when);
352
46
		Ok((now, when))
353
47
	}
354
24
	pub fn can_leave<T: Config>(&self) -> DispatchResult {
355
24
		if let CollatorStatus::Leaving(when) = self.status {
356
24
			ensure!(
357
24
				<Round<T>>::get().current >= when,
358
2
				Error::<T>::CandidateCannotLeaveYet
359
			);
360
22
			Ok(())
361
		} else {
362
			Err(Error::<T>::CandidateNotLeaving.into())
363
		}
364
24
	}
365
14
	pub fn go_offline(&mut self) {
366
14
		self.status = CollatorStatus::Idle;
367
14
	}
368
8
	pub fn go_online(&mut self) {
369
8
		self.status = CollatorStatus::Active;
370
8
	}
371
8
	pub fn bond_more<T: Config>(&mut self, who: T::AccountId, more: Balance) -> DispatchResult
372
8
	where
373
8
		BalanceOf<T>: From<Balance>,
374
8
	{
375
8
		ensure!(
376
8
			<Pallet<T>>::get_collator_stakable_free_balance(&who) >= more.into(),
377
			Error::<T>::InsufficientBalance
378
		);
379
8
		let new_total = <Total<T>>::get().saturating_add(more.into());
380
8
		<Total<T>>::put(new_total);
381
8
		self.bond = self.bond.saturating_add(more);
382
8

            
383
8
		// Freeze the total new amount (current + additional)
384
8
		<Pallet<T>>::freeze_extended(&who.clone(), self.bond.into(), true)
385
8
			.map_err(|_| DispatchError::from(Error::<T>::InsufficientBalance))?;
386

            
387
8
		self.total_counted = self.total_counted.saturating_add(more);
388
8
		<Pallet<T>>::deposit_event(Event::CandidateBondedMore {
389
8
			candidate: who.clone(),
390
8
			amount: more.into(),
391
8
			new_total_bond: self.bond.into(),
392
8
		});
393
8
		Ok(())
394
8
	}
395

            
396
7
	pub fn bond_less<T: Config>(&mut self, who: T::AccountId, amount: Balance) -> DispatchResult
397
7
	where
398
7
		BalanceOf<T>: From<Balance>,
399
7
	{
400
7
		let new_total_staked = <Total<T>>::get().saturating_sub(amount.into());
401
7
		<Total<T>>::put(new_total_staked);
402
7
		self.bond = self.bond.saturating_sub(amount);
403
7

            
404
7
		// Update the freeze to the new total amount
405
7
		if self.bond.is_zero() {
406
			<Pallet<T>>::thaw_extended(&who, true)?;
407
		} else {
408
7
			<Pallet<T>>::freeze_extended(&who, self.bond.into(), true)?;
409
		}
410
7
		self.total_counted = self.total_counted.saturating_sub(amount);
411
7
		let event = Event::CandidateBondedLess {
412
7
			candidate: who.clone(),
413
7
			amount: amount.into(),
414
7
			new_bond: self.bond.into(),
415
7
		};
416
7
		// update candidate pool value because it must change if self bond changes
417
7
		if self.is_active() {
418
7
			Pallet::<T>::update_active(who, self.total_counted.into());
419
7
		}
420
7
		Pallet::<T>::deposit_event(event);
421
7
		Ok(())
422
7
	}
423

            
424
	/// Schedule executable decrease of collator candidate self bond
425
	/// Returns the round at which the collator can execute the pending request
426
18
	pub fn schedule_bond_less<T: Config>(
427
18
		&mut self,
428
18
		less: Balance,
429
18
	) -> Result<RoundIndex, DispatchError>
430
18
	where
431
18
		BalanceOf<T>: Into<Balance>,
432
18
	{
433
18
		// ensure no pending request
434
18
		ensure!(
435
18
			self.request.is_none(),
436
1
			Error::<T>::PendingCandidateRequestAlreadyExists
437
		);
438
		// ensure bond above min after decrease
439
17
		ensure!(self.bond > less, Error::<T>::CandidateBondBelowMin);
440
17
		ensure!(
441
17
			self.bond - less >= T::MinCandidateStk::get().into(),
442
1
			Error::<T>::CandidateBondBelowMin
443
		);
444
16
		let when_executable = <Round<T>>::get().current + T::CandidateBondLessDelay::get();
445
16
		self.request = Some(CandidateBondLessRequest {
446
16
			amount: less,
447
16
			when_executable,
448
16
		});
449
16
		Ok(when_executable)
450
18
	}
451
	/// Execute pending request to decrease the collator self bond
452
	/// Returns the event to be emitted
453
7
	pub fn execute_bond_less<T: Config>(&mut self, who: T::AccountId) -> DispatchResult
454
7
	where
455
7
		BalanceOf<T>: From<Balance>,
456
7
	{
457
7
		let request = self
458
7
			.request
459
7
			.ok_or(Error::<T>::PendingCandidateRequestsDNE)?;
460
7
		ensure!(
461
7
			request.when_executable <= <Round<T>>::get().current,
462
			Error::<T>::PendingCandidateRequestNotDueYet
463
		);
464
7
		self.bond_less::<T>(who.clone(), request.amount)?;
465
		// reset s.t. no pending request
466
7
		self.request = None;
467
7
		Ok(())
468
7
	}
469

            
470
	/// Cancel candidate bond less request
471
3
	pub fn cancel_bond_less<T: Config>(&mut self, who: T::AccountId) -> DispatchResult
472
3
	where
473
3
		BalanceOf<T>: From<Balance>,
474
3
	{
475
3
		let request = self
476
3
			.request
477
3
			.ok_or(Error::<T>::PendingCandidateRequestsDNE)?;
478
3
		let event = Event::CancelledCandidateBondLess {
479
3
			candidate: who.clone().into(),
480
3
			amount: request.amount.into(),
481
3
			execute_round: request.when_executable,
482
3
		};
483
3
		self.request = None;
484
3
		Pallet::<T>::deposit_event(event);
485
3
		Ok(())
486
3
	}
487
	/// Reset top delegations metadata
488
496
	pub fn reset_top_data<T: Config>(
489
496
		&mut self,
490
496
		candidate: T::AccountId,
491
496
		top_delegations: &Delegations<T::AccountId, BalanceOf<T>>,
492
496
	) where
493
496
		BalanceOf<T>: Into<Balance> + From<Balance>,
494
496
	{
495
496
		self.lowest_top_delegation_amount = top_delegations.lowest_delegation_amount().into();
496
496
		self.top_capacity = top_delegations.top_capacity::<T>();
497
496
		let old_total_counted = self.total_counted;
498
496
		self.total_counted = self.bond.saturating_add(top_delegations.total.into());
499
496
		// CandidatePool value for candidate always changes if top delegations total changes
500
496
		// so we moved the update into this function to deduplicate code and patch a bug that
501
496
		// forgot to apply the update when increasing top delegation
502
496
		if old_total_counted != self.total_counted && self.is_active() {
503
492
			Pallet::<T>::update_active(candidate, self.total_counted.into());
504
492
		}
505
496
	}
506
	/// Reset bottom delegations metadata
507
186
	pub fn reset_bottom_data<T: Config>(
508
186
		&mut self,
509
186
		bottom_delegations: &Delegations<T::AccountId, BalanceOf<T>>,
510
186
	) where
511
186
		BalanceOf<T>: Into<Balance>,
512
186
	{
513
186
		self.lowest_bottom_delegation_amount = bottom_delegations.lowest_delegation_amount().into();
514
186
		self.highest_bottom_delegation_amount =
515
186
			bottom_delegations.highest_delegation_amount().into();
516
186
		self.bottom_capacity = bottom_delegations.bottom_capacity::<T>();
517
186
	}
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
9065
	pub fn add_delegation<T: Config>(
523
9065
		&mut self,
524
9065
		candidate: &T::AccountId,
525
9065
		delegation: Bond<T::AccountId, BalanceOf<T>>,
526
9065
	) -> Result<(DelegatorAdded<Balance>, Option<Balance>), DispatchError>
527
9065
	where
528
9065
		BalanceOf<T>: Into<Balance> + From<Balance>,
529
9065
	{
530
9065
		let mut less_total_staked = None;
531
9065
		let delegator_added = match self.top_capacity {
532
			CapacityStatus::Full => {
533
				// top is full, insert into top iff the lowest_top < amount
534
8643
				if self.lowest_top_delegation_amount < delegation.amount.into() {
535
					// bumps lowest top to the bottom inside this function call
536
17
					less_total_staked = self.add_top_delegation::<T>(candidate, delegation);
537
17
					DelegatorAdded::AddedToTop {
538
17
						new_total: self.total_counted,
539
17
					}
540
				} else {
541
					// if bottom is full, only insert if greater than lowest bottom (which will
542
					// be bumped out)
543
8626
					if matches!(self.bottom_capacity, CapacityStatus::Full) {
544
8472
						ensure!(
545
8472
							delegation.amount.into() > self.lowest_bottom_delegation_amount,
546
8470
							Error::<T>::CannotDelegateLessThanOrEqualToLowestBottomWhenFull
547
						);
548
						// need to subtract from total staked
549
2
						less_total_staked = Some(self.lowest_bottom_delegation_amount);
550
154
					}
551
					// insert into bottom
552
156
					self.add_bottom_delegation::<T>(false, candidate, delegation);
553
156
					DelegatorAdded::AddedToBottom
554
				}
555
			}
556
			// top is either empty or partially full
557
			_ => {
558
422
				self.add_top_delegation::<T>(candidate, delegation);
559
422
				DelegatorAdded::AddedToTop {
560
422
					new_total: self.total_counted,
561
422
				}
562
			}
563
		};
564
595
		Ok((delegator_added, less_total_staked))
565
9065
	}
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
439
	pub fn add_top_delegation<T: Config>(
570
439
		&mut self,
571
439
		candidate: &T::AccountId,
572
439
		delegation: Bond<T::AccountId, BalanceOf<T>>,
573
439
	) -> Option<Balance>
574
439
	where
575
439
		BalanceOf<T>: Into<Balance> + From<Balance>,
576
439
	{
577
439
		let mut less_total_staked = None;
578
439
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
579
439
			.expect("CandidateInfo existence => TopDelegations existence");
580
439
		let max_top_delegations_per_candidate = T::MaxTopDelegationsPerCandidate::get();
581
439
		if top_delegations.delegations.len() as u32 == max_top_delegations_per_candidate {
582
			// pop lowest top delegation
583
17
			let new_bottom_delegation = top_delegations.delegations.pop().expect("");
584
17
			top_delegations.total = top_delegations
585
17
				.total
586
17
				.saturating_sub(new_bottom_delegation.amount);
587
17
			if matches!(self.bottom_capacity, CapacityStatus::Full) {
588
2
				less_total_staked = Some(self.lowest_bottom_delegation_amount);
589
15
			}
590
17
			self.add_bottom_delegation::<T>(true, candidate, new_bottom_delegation);
591
422
		}
592
		// insert into top
593
439
		top_delegations.insert_sorted_greatest_to_least(delegation);
594
439
		// update candidate info
595
439
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
596
439
		if less_total_staked.is_none() {
597
437
			// only increment delegation count if we are not kicking a bottom delegation
598
437
			self.delegation_count = self.delegation_count.saturating_add(1u32);
599
437
		}
600
439
		<TopDelegations<T>>::insert(&candidate, top_delegations);
601
439
		less_total_staked
602
439
	}
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
173
	pub fn add_bottom_delegation<T: Config>(
608
173
		&mut self,
609
173
		bumped_from_top: bool,
610
173
		candidate: &T::AccountId,
611
173
		delegation: Bond<T::AccountId, BalanceOf<T>>,
612
173
	) where
613
173
		BalanceOf<T>: Into<Balance> + From<Balance>,
614
173
	{
615
173
		let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
616
173
			.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
173
		let increase_delegation_count = if bottom_delegations.delegations.len() as u32
620
173
			== T::MaxBottomDelegationsPerCandidate::get()
621
		{
622
4
			let lowest_bottom_to_be_kicked = bottom_delegations
623
4
				.delegations
624
4
				.pop()
625
4
				.expect("if at full capacity (>0), then >0 bottom delegations exist; qed");
626
4
			// EXPECT lowest_bottom_to_be_kicked.amount < delegation.amount enforced by caller
627
4
			// if lowest_bottom_to_be_kicked.amount == delegation.amount, we will still kick
628
4
			// the lowest bottom to enforce first come first served
629
4
			bottom_delegations.total = bottom_delegations
630
4
				.total
631
4
				.saturating_sub(lowest_bottom_to_be_kicked.amount);
632
4
			// update delegator state
633
4
			// total staked is updated via propagation of lowest bottom delegation amount prior
634
4
			// to call
635
4
			let mut delegator_state = <DelegatorState<T>>::get(&lowest_bottom_to_be_kicked.owner)
636
4
				.expect("Delegation existence => DelegatorState existence");
637
4
			let leaving = delegator_state.delegations.0.len() == 1usize;
638
4
			delegator_state.rm_delegation::<T>(candidate);
639
4
			<Pallet<T>>::delegation_remove_request_with_state(
640
4
				&candidate,
641
4
				&lowest_bottom_to_be_kicked.owner,
642
4
				&mut delegator_state,
643
4
			);
644
4
			<AutoCompoundDelegations<T>>::remove_auto_compound(
645
4
				&candidate,
646
4
				&lowest_bottom_to_be_kicked.owner,
647
4
			);
648
4

            
649
4
			Pallet::<T>::deposit_event(Event::DelegationKicked {
650
4
				delegator: lowest_bottom_to_be_kicked.owner.clone(),
651
4
				candidate: candidate.clone(),
652
4
				unstaked_amount: lowest_bottom_to_be_kicked.amount,
653
4
			});
654
4
			if leaving {
655
2
				<DelegatorState<T>>::remove(&lowest_bottom_to_be_kicked.owner);
656
2
				Pallet::<T>::deposit_event(Event::DelegatorLeft {
657
2
					delegator: lowest_bottom_to_be_kicked.owner,
658
2
					unstaked_amount: lowest_bottom_to_be_kicked.amount,
659
2
				});
660
2
			} else {
661
2
				<DelegatorState<T>>::insert(&lowest_bottom_to_be_kicked.owner, delegator_state);
662
2
			}
663
4
			false
664
		} else {
665
169
			!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
173
		if increase_delegation_count {
670
154
			self.delegation_count = self.delegation_count.saturating_add(1u32);
671
154
		}
672
173
		bottom_delegations.insert_sorted_greatest_to_least(delegation);
673
173
		self.reset_bottom_data::<T>(&bottom_delegations);
674
173
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
675
173
	}
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
23
	pub fn rm_delegation_if_exists<T: Config>(
680
23
		&mut self,
681
23
		candidate: &T::AccountId,
682
23
		delegator: T::AccountId,
683
23
		amount: Balance,
684
23
	) -> Result<bool, DispatchError>
685
23
	where
686
23
		BalanceOf<T>: Into<Balance> + From<Balance>,
687
23
	{
688
23
		let amount_geq_lowest_top = amount >= self.lowest_top_delegation_amount;
689
23
		let top_is_not_full = !matches!(self.top_capacity, CapacityStatus::Full);
690
23
		let lowest_top_eq_highest_bottom =
691
23
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
692
23
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
693
23
		if top_is_not_full || (amount_geq_lowest_top && !lowest_top_eq_highest_bottom) {
694
23
			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
23
	}
707
	/// Remove top delegation, bumps top bottom delegation if exists
708
23
	pub fn rm_top_delegation<T: Config>(
709
23
		&mut self,
710
23
		candidate: &T::AccountId,
711
23
		delegator: T::AccountId,
712
23
	) -> Result<bool, DispatchError>
713
23
	where
714
23
		BalanceOf<T>: Into<Balance> + From<Balance>,
715
23
	{
716
23
		let old_total_counted = self.total_counted;
717
23
		// remove top delegation
718
23
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
719
23
			.expect("CandidateInfo exists => TopDelegations exists");
720
23
		let mut actual_amount_option: Option<BalanceOf<T>> = None;
721
23
		top_delegations.delegations = top_delegations
722
23
			.delegations
723
23
			.clone()
724
23
			.into_iter()
725
25
			.filter(|d| {
726
25
				if d.owner != delegator {
727
2
					true
728
				} else {
729
23
					actual_amount_option = Some(d.amount);
730
23
					false
731
				}
732
25
			})
733
23
			.collect();
734
23
		let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
735
23
		top_delegations.total = top_delegations.total.saturating_sub(actual_amount);
736
		// if bottom nonempty => bump top bottom to top
737
23
		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
23
		}
750
		// update candidate info
751
23
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
752
23
		self.delegation_count = self.delegation_count.saturating_sub(1u32);
753
23
		<TopDelegations<T>>::insert(candidate, top_delegations);
754
23
		// return whether total counted changed
755
23
		Ok(old_total_counted == self.total_counted)
756
23
	}
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
21
	pub fn increase_delegation<T: Config>(
794
21
		&mut self,
795
21
		candidate: &T::AccountId,
796
21
		delegator: T::AccountId,
797
21
		bond: BalanceOf<T>,
798
21
		more: BalanceOf<T>,
799
21
	) -> Result<bool, DispatchError>
800
21
	where
801
21
		BalanceOf<T>: Into<Balance> + From<Balance>,
802
21
	{
803
21
		let lowest_top_eq_highest_bottom =
804
21
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
805
21
		let bond_geq_lowest_top = bond.into() >= self.lowest_top_delegation_amount;
806
21
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
807
21
		if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
808
			// definitely in top
809
12
			self.increase_top_delegation::<T>(candidate, delegator.clone(), more)
810
9
		} 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
9
			self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
821
		}
822
21
	}
823
	/// Increase top delegation
824
12
	pub fn increase_top_delegation<T: Config>(
825
12
		&mut self,
826
12
		candidate: &T::AccountId,
827
12
		delegator: T::AccountId,
828
12
		more: BalanceOf<T>,
829
12
	) -> Result<bool, DispatchError>
830
12
	where
831
12
		BalanceOf<T>: Into<Balance> + From<Balance>,
832
12
	{
833
12
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
834
12
			.expect("CandidateInfo exists => TopDelegations exists");
835
12
		let mut in_top = false;
836
12
		top_delegations.delegations = top_delegations
837
12
			.delegations
838
12
			.clone()
839
12
			.into_iter()
840
19
			.map(|d| {
841
19
				if d.owner != delegator {
842
7
					d
843
				} else {
844
12
					in_top = true;
845
12
					let new_amount = d.amount.saturating_add(more);
846
12
					Bond {
847
12
						owner: d.owner,
848
12
						amount: new_amount,
849
12
					}
850
				}
851
19
			})
852
12
			.collect();
853
12
		ensure!(in_top, Error::<T>::DelegationDNE);
854
12
		top_delegations.total = top_delegations.total.saturating_add(more);
855
12
		top_delegations.sort_greatest_to_least();
856
12
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
857
12
		<TopDelegations<T>>::insert(candidate, top_delegations);
858
12
		Ok(true)
859
12
	}
860
	/// Increase bottom delegation
861
9
	pub fn increase_bottom_delegation<T: Config>(
862
9
		&mut self,
863
9
		candidate: &T::AccountId,
864
9
		delegator: T::AccountId,
865
9
		bond: BalanceOf<T>,
866
9
		more: BalanceOf<T>,
867
9
	) -> Result<bool, DispatchError>
868
9
	where
869
9
		BalanceOf<T>: Into<Balance> + From<Balance>,
870
9
	{
871
9
		let mut bottom_delegations =
872
9
			<BottomDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
873
9
		let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
874
9
		let in_top_after = if (bond.saturating_add(more)).into() > self.lowest_top_delegation_amount
875
		{
876
			// bump it from bottom
877
7
			bottom_delegations.delegations = bottom_delegations
878
7
				.delegations
879
7
				.clone()
880
7
				.into_iter()
881
26
				.filter(|d| {
882
26
					if d.owner != delegator {
883
19
						true
884
					} else {
885
7
						delegation_option = Some(Bond {
886
7
							owner: d.owner.clone(),
887
7
							amount: d.amount.saturating_add(more),
888
7
						});
889
7
						false
890
					}
891
26
				})
892
7
				.collect();
893
7
			let delegation = delegation_option.ok_or(Error::<T>::DelegationDNE)?;
894
7
			bottom_delegations.total = bottom_delegations.total.saturating_sub(bond);
895
7
			// add it to top
896
7
			let mut top_delegations = <TopDelegations<T>>::get(candidate)
897
7
				.expect("CandidateInfo existence => TopDelegations existence");
898
			// if top is full, pop lowest top
899
7
			if matches!(top_delegations.top_capacity::<T>(), CapacityStatus::Full) {
900
7
				// pop lowest top delegation
901
7
				let new_bottom_delegation = top_delegations
902
7
					.delegations
903
7
					.pop()
904
7
					.expect("Top capacity full => Exists at least 1 top delegation");
905
7
				top_delegations.total = top_delegations
906
7
					.total
907
7
					.saturating_sub(new_bottom_delegation.amount);
908
7
				bottom_delegations.insert_sorted_greatest_to_least(new_bottom_delegation);
909
7
			}
910
			// insert into top
911
7
			top_delegations.insert_sorted_greatest_to_least(delegation);
912
7
			self.reset_top_data::<T>(candidate.clone(), &top_delegations);
913
7
			<TopDelegations<T>>::insert(candidate, top_delegations);
914
7
			true
915
		} else {
916
2
			let mut in_bottom = false;
917
2
			// just increase the delegation
918
2
			bottom_delegations.delegations = bottom_delegations
919
2
				.delegations
920
2
				.clone()
921
2
				.into_iter()
922
3
				.map(|d| {
923
3
					if d.owner != delegator {
924
1
						d
925
					} else {
926
2
						in_bottom = true;
927
2
						Bond {
928
2
							owner: d.owner,
929
2
							amount: d.amount.saturating_add(more),
930
2
						}
931
					}
932
3
				})
933
2
				.collect();
934
2
			ensure!(in_bottom, Error::<T>::DelegationDNE);
935
2
			bottom_delegations.total = bottom_delegations.total.saturating_add(more);
936
2
			bottom_delegations.sort_greatest_to_least();
937
2
			false
938
		};
939
9
		self.reset_bottom_data::<T>(&bottom_delegations);
940
9
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
941
9
		Ok(in_top_after)
942
9
	}
943
	/// Decrease delegation
944
16
	pub fn decrease_delegation<T: Config>(
945
16
		&mut self,
946
16
		candidate: &T::AccountId,
947
16
		delegator: T::AccountId,
948
16
		bond: Balance,
949
16
		less: BalanceOf<T>,
950
16
	) -> Result<bool, DispatchError>
951
16
	where
952
16
		BalanceOf<T>: Into<Balance> + From<Balance>,
953
16
	{
954
16
		let lowest_top_eq_highest_bottom =
955
16
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
956
16
		let bond_geq_lowest_top = bond >= self.lowest_top_delegation_amount;
957
16
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
958
16
		if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
959
			// definitely in top
960
14
			self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less)
961
2
		} 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
1
			let result =
965
1
				self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less);
966
1
			if result == Err(delegation_dne_err) {
967
				self.decrease_bottom_delegation::<T>(candidate, delegator, less)
968
			} else {
969
1
				result
970
			}
971
		} else {
972
1
			self.decrease_bottom_delegation::<T>(candidate, delegator, less)
973
		}
974
16
	}
975
	/// Decrease top delegation
976
15
	pub fn decrease_top_delegation<T: Config>(
977
15
		&mut self,
978
15
		candidate: &T::AccountId,
979
15
		delegator: T::AccountId,
980
15
		bond: BalanceOf<T>,
981
15
		less: BalanceOf<T>,
982
15
	) -> Result<bool, DispatchError>
983
15
	where
984
15
		BalanceOf<T>: Into<Balance> + From<Balance>,
985
15
	{
986
15
		// The delegation after the `decrease-delegation` will be strictly less than the
987
15
		// highest bottom delegation
988
15
		let bond_after_less_than_highest_bottom =
989
15
			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
15
		let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full)
992
5
			&& !matches!(self.bottom_capacity, CapacityStatus::Empty);
993
15
		let mut top_delegations =
994
15
			<TopDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
995
15
		let in_top_after = if bond_after_less_than_highest_bottom && full_top_and_nonempty_bottom {
996
3
			let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
997
3
			// take delegation from top
998
3
			top_delegations.delegations = top_delegations
999
3
				.delegations
3
				.clone()
3
				.into_iter()
12
				.filter(|d| {
12
					if d.owner != delegator {
9
						true
					} else {
3
						top_delegations.total = top_delegations.total.saturating_sub(d.amount);
3
						delegation_option = Some(Bond {
3
							owner: d.owner.clone(),
3
							amount: d.amount.saturating_sub(less),
3
						});
3
						false
					}
12
				})
3
				.collect();
3
			let delegation = delegation_option.ok_or(Error::<T>::DelegationDNE)?;
			// pop highest bottom by reverse and popping
3
			let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
3
				.expect("CandidateInfo existence => BottomDelegations existence");
3
			let highest_bottom_delegation = bottom_delegations.delegations.remove(0);
3
			bottom_delegations.total = bottom_delegations
3
				.total
3
				.saturating_sub(highest_bottom_delegation.amount);
3
			// insert highest bottom into top
3
			top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
3
			// insert previous top into bottom
3
			bottom_delegations.insert_sorted_greatest_to_least(delegation);
3
			self.reset_bottom_data::<T>(&bottom_delegations);
3
			<BottomDelegations<T>>::insert(candidate, bottom_delegations);
3
			false
		} else {
			// keep it in the top
12
			let mut is_in_top = false;
12
			top_delegations.delegations = top_delegations
12
				.delegations
12
				.clone()
12
				.into_iter()
18
				.map(|d| {
18
					if d.owner != delegator {
6
						d
					} else {
12
						is_in_top = true;
12
						Bond {
12
							owner: d.owner,
12
							amount: d.amount.saturating_sub(less),
12
						}
					}
18
				})
12
				.collect();
12
			ensure!(is_in_top, Error::<T>::DelegationDNE);
12
			top_delegations.total = top_delegations.total.saturating_sub(less);
12
			top_delegations.sort_greatest_to_least();
12
			true
		};
15
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
15
		<TopDelegations<T>>::insert(candidate, top_delegations);
15
		Ok(in_top_after)
15
	}
	/// Decrease bottom delegation
1
	pub fn decrease_bottom_delegation<T: Config>(
1
		&mut self,
1
		candidate: &T::AccountId,
1
		delegator: T::AccountId,
1
		less: BalanceOf<T>,
1
	) -> Result<bool, DispatchError>
1
	where
1
		BalanceOf<T>: Into<Balance>,
1
	{
1
		let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
1
			.expect("CandidateInfo exists => BottomDelegations exists");
1
		let mut in_bottom = false;
1
		bottom_delegations.delegations = bottom_delegations
1
			.delegations
1
			.clone()
1
			.into_iter()
1
			.map(|d| {
1
				if d.owner != delegator {
					d
				} else {
1
					in_bottom = true;
1
					Bond {
1
						owner: d.owner,
1
						amount: d.amount.saturating_sub(less),
1
					}
				}
1
			})
1
			.collect();
1
		ensure!(in_bottom, Error::<T>::DelegationDNE);
1
		bottom_delegations.sort_greatest_to_least();
1
		self.reset_bottom_data::<T>(&bottom_delegations);
1
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
1
		Ok(false)
1
	}
}
/// Convey relevant information describing if a delegator was added to the top or bottom
/// Delegations added to the top yield a new total
38
#[derive(Clone, Copy, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, DecodeWithMemTracking)]
pub enum DelegatorAdded<B> {
	AddedToTop { new_total: B },
6
	AddedToBottom,
}
#[allow(deprecated)]
120
#[derive(Clone, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum DelegatorStatus {
728
	/// Active with no scheduled exit
	Active,
	/// Schedule exit to revoke all ongoing delegations
	#[deprecated(note = "must only be used for backwards compatibility reasons")]
	Leaving(RoundIndex),
}
95
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
/// Delegator state
pub struct Delegator<AccountId, Balance> {
	/// Delegator account
	pub id: AccountId,
	/// All current delegations
	pub delegations: OrderedSet<Bond<AccountId, Balance>>,
	/// Total frozen balance for this delegator
	pub total: Balance,
	/// Sum of pending revocation amounts + bond less amounts
	pub less_total: Balance,
	/// Status for this delegator
	pub status: DelegatorStatus,
}
// Temporary manual implementation for migration testing purposes
impl<A: PartialEq, B: PartialEq> PartialEq for Delegator<A, B> {
	fn eq(&self, other: &Self) -> bool {
		let must_be_true = self.id == other.id
			&& self.total == other.total
			&& self.less_total == other.less_total
			&& self.status == other.status;
		if !must_be_true {
			return false;
		}
		for (
			Bond {
				owner: o1,
				amount: a1,
			},
			Bond {
				owner: o2,
				amount: a2,
			},
		) in self.delegations.0.iter().zip(other.delegations.0.iter())
		{
			if o1 != o2 || a1 != a2 {
				return false;
			}
		}
		true
	}
}
impl<
		AccountId: Ord + Clone,
		Balance: Copy
			+ sp_std::ops::AddAssign
			+ sp_std::ops::Add<Output = Balance>
			+ sp_std::ops::SubAssign
			+ sp_std::ops::Sub<Output = Balance>
			+ Ord
			+ Zero
			+ Default
			+ Saturating,
	> Delegator<AccountId, Balance>
{
9011
	pub fn new(id: AccountId, collator: AccountId, amount: Balance) -> Self {
9011
		Delegator {
9011
			id,
9011
			delegations: OrderedSet::from(vec![Bond {
9011
				owner: collator,
9011
				amount,
9011
			}]),
9011
			total: amount,
9011
			less_total: Balance::zero(),
9011
			status: DelegatorStatus::Active,
9011
		}
9011
	}
	pub fn default_with_total(id: AccountId, amount: Balance) -> Self {
		Delegator {
			id,
			total: amount,
			delegations: OrderedSet::from(vec![]),
			less_total: Balance::zero(),
			status: DelegatorStatus::Active,
		}
	}
105
	pub fn total(&self) -> Balance {
105
		self.total
105
	}
16
	pub fn total_sub_if<T, F>(&mut self, amount: Balance, check: F) -> DispatchResult
16
	where
16
		T: Config,
16
		T::AccountId: From<AccountId>,
16
		BalanceOf<T>: From<Balance>,
16
		F: Fn(Balance) -> DispatchResult,
16
	{
16
		let total = self.total.saturating_sub(amount);
16
		check(total)?;
16
		self.total = total;
16
		self.adjust_bond_lock::<T>(BondAdjust::Decrease)?;
16
		Ok(())
16
	}
	pub fn total_add<T, F>(&mut self, amount: Balance) -> DispatchResult
	where
		T: Config,
		T::AccountId: From<AccountId>,
		BalanceOf<T>: From<Balance>,
	{
		self.total = self.total.saturating_add(amount);
		self.adjust_bond_lock::<T>(BondAdjust::Increase(amount))?;
		Ok(())
	}
42
	pub fn total_sub<T>(&mut self, amount: Balance) -> DispatchResult
42
	where
42
		T: Config,
42
		T::AccountId: From<AccountId>,
42
		BalanceOf<T>: From<Balance>,
42
	{
42
		self.total = self.total.saturating_sub(amount);
42
		self.adjust_bond_lock::<T>(BondAdjust::Decrease)?;
42
		Ok(())
42
	}
	pub fn is_active(&self) -> bool {
		matches!(self.status, DelegatorStatus::Active)
	}
66
	pub fn add_delegation(&mut self, bond: Bond<AccountId, Balance>) -> bool {
66
		let amt = bond.amount;
66
		if self.delegations.insert(bond) {
64
			self.total = self.total.saturating_add(amt);
64
			true
		} else {
2
			false
		}
66
	}
	// Return Some(remaining balance), must be more than MinDelegation
	// Return None if delegation not found
42
	pub fn rm_delegation<T: Config>(&mut self, collator: &AccountId) -> Option<Balance>
42
	where
42
		BalanceOf<T>: From<Balance>,
42
		T::AccountId: From<AccountId>,
42
	{
42
		let mut amt: Option<Balance> = None;
42
		let delegations = self
42
			.delegations
42
			.0
42
			.iter()
65
			.filter_map(|x| {
65
				if &x.owner == collator {
42
					amt = Some(x.amount);
42
					None
				} else {
23
					Some(x.clone())
				}
65
			})
42
			.collect();
42
		if let Some(balance) = amt {
42
			self.delegations = OrderedSet::from(delegations);
42
			self.total_sub::<T>(balance)
42
				.expect("Decreasing lock cannot fail, qed");
42
			Some(self.total)
		} else {
			None
		}
42
	}
	/// Increases the delegation amount and returns `true` if the delegation is part of the
	/// TopDelegations set, `false` otherwise.
21
	pub fn increase_delegation<T: Config>(
21
		&mut self,
21
		candidate: AccountId,
21
		amount: Balance,
21
	) -> Result<bool, sp_runtime::DispatchError>
21
	where
21
		BalanceOf<T>: From<Balance>,
21
		T::AccountId: From<AccountId>,
21
		Delegator<T::AccountId, BalanceOf<T>>: From<Delegator<AccountId, Balance>>,
21
	{
21
		let delegator_id: T::AccountId = self.id.clone().into();
21
		let candidate_id: T::AccountId = candidate.clone().into();
21
		let balance_amt: BalanceOf<T> = amount.into();
		// increase delegation
22
		for x in &mut self.delegations.0 {
22
			if x.owner == candidate {
21
				let before_amount: BalanceOf<T> = x.amount.into();
21
				x.amount = x.amount.saturating_add(amount);
21
				self.total = self.total.saturating_add(amount);
21
				self.adjust_bond_lock::<T>(BondAdjust::Increase(amount))?;
				// update collator state delegation
21
				let mut collator_state =
21
					<CandidateInfo<T>>::get(&candidate_id).ok_or(Error::<T>::CandidateDNE)?;
21
				let before = collator_state.total_counted;
21
				let in_top = collator_state.increase_delegation::<T>(
21
					&candidate_id,
21
					delegator_id.clone(),
21
					before_amount,
21
					balance_amt,
21
				)?;
21
				let after = collator_state.total_counted;
21
				if collator_state.is_active() && (before != after) {
18
					Pallet::<T>::update_active(candidate_id.clone(), after);
18
				}
21
				<CandidateInfo<T>>::insert(&candidate_id, collator_state);
21
				let new_total_staked = <Total<T>>::get().saturating_add(balance_amt);
21
				<Total<T>>::put(new_total_staked);
21
				let nom_st: Delegator<T::AccountId, BalanceOf<T>> = self.clone().into();
21
				<DelegatorState<T>>::insert(&delegator_id, nom_st);
21
				return Ok(in_top);
1
			}
		}
		Err(Error::<T>::DelegationDNE.into())
21
	}
	/// Updates the bond freezes for this delegator.
	///
	/// This will take the current self.total and ensure that a freeze of the same amount is applied.
	/// When increasing the bond, it will also ensure that the account has enough free balance.
	///
	/// `additional_required_balance` should reflect the change to the amount that should be frozen if
	/// positive, 0 otherwise (e.g. `min(0, change_in_total_bond)`).
674
	pub fn adjust_bond_lock<T: Config>(
674
		&mut self,
674
		additional_required_balance: BondAdjust<Balance>,
674
	) -> DispatchResult
674
	where
674
		BalanceOf<T>: From<Balance>,
674
		T::AccountId: From<AccountId>,
674
	{
674
		let who: T::AccountId = self.id.clone().into();
674

            
674
		match additional_required_balance {
616
			BondAdjust::Increase(amount) => {
616
				ensure!(
616
					<Pallet<T>>::get_delegator_stakable_balance(&who) >= amount.into(),
					Error::<T>::InsufficientBalance,
				);
				// additional sanity check: shouldn't ever want to freeze more than total
616
				if amount > self.total {
					log::warn!("LOGIC ERROR: request to freeze more than bond total");
					return Err(DispatchError::Other("Invalid additional_required_balance"));
616
				}
			}
58
			BondAdjust::Decrease => {
58
				// Nothing special to do for decrease with freezes
58
			}
		}
		// Set the freeze to the total amount
674
		if self.total > Balance::zero() {
651
			<Pallet<T>>::freeze_extended(&who, self.total.into(), false)?;
23
		} else {
23
			// If total is zero, remove the freeze
23
			let _ = <Pallet<T>>::thaw_extended(&who, false);
23
		}
674
		Ok(())
674
	}
	/// Retrieves the bond amount that a delegator has provided towards a collator.
	/// Returns `None` if missing.
134
	pub fn get_bond_amount(&self, collator: &AccountId) -> Option<Balance> {
134
		self.delegations
134
			.0
134
			.iter()
141
			.find(|b| &b.owner == collator)
134
			.map(|b| b.amount)
134
	}
}
76
#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
/// The current round index and transition information
pub struct RoundInfo<BlockNumber> {
	/// Current round index
	pub current: RoundIndex,
	/// The first block of the current round
	pub first: BlockNumber,
	/// The length of the current round in number of blocks
	pub length: u32,
	/// The first slot of the current round
	pub first_slot: u64,
}
impl<
		B: Copy + sp_std::ops::Add<Output = B> + sp_std::ops::Sub<Output = B> + From<u32> + PartialOrd,
	> RoundInfo<B>
{
543
	pub fn new(current: RoundIndex, first: B, length: u32, first_slot: u64) -> RoundInfo<B> {
543
		RoundInfo {
543
			current,
543
			first,
543
			length,
543
			first_slot,
543
		}
543
	}
	/// Check if the round should be updated
30772
	pub fn should_update(&self, now: B) -> bool {
30772
		now - self.first >= self.length.into()
30772
	}
	/// New round
267
	pub fn update(&mut self, now: B, now_slot: u64) {
267
		self.current = self.current.saturating_add(1u32);
267
		self.first = now;
267
		self.first_slot = now_slot;
267
	}
}
impl<
		B: Copy + sp_std::ops::Add<Output = B> + sp_std::ops::Sub<Output = B> + From<u32> + PartialOrd,
	> Default for RoundInfo<B>
{
19
	fn default() -> RoundInfo<B> {
19
		RoundInfo::new(1u32, 1u32.into(), 20u32, 0)
19
	}
}
// Type which encapsulates the configuration for the inflation distribution.
19
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, DecodeWithMemTracking)]
pub struct InflationDistributionConfig<AccountId>(
	pub(crate) [InflationDistributionAccount<AccountId>; 2],
);
impl<AccountId> From<[InflationDistributionAccount<AccountId>; 2]>
	for InflationDistributionConfig<AccountId>
{
621
	fn from(configs: [InflationDistributionAccount<AccountId>; 2]) -> Self {
621
		InflationDistributionConfig(configs)
621
	}
}
impl<AccountId: Decode> Default for InflationDistributionConfig<AccountId> {
19
	fn default() -> InflationDistributionConfig<AccountId> {
19
		InflationDistributionConfig([
19
			InflationDistributionAccount {
19
				account: AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
19
					.expect("infinite length input; no invalid inputs for type; qed"),
19
				percent: Percent::zero(),
19
			},
19
			InflationDistributionAccount {
19
				account: AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
19
					.expect("infinite length input; no invalid inputs for type; qed"),
19
				percent: Percent::zero(),
19
			},
19
		])
19
	}
}
38
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, DecodeWithMemTracking)]
/// Reserve information { account, percent_of_inflation }
pub struct InflationDistributionAccount<AccountId> {
	/// Account which receives funds
	pub account: AccountId,
	/// Percent of inflation set aside for the account
	pub percent: Percent,
}
impl<A: Decode> Default for InflationDistributionAccount<A> {
3
	fn default() -> InflationDistributionAccount<A> {
3
		InflationDistributionAccount {
3
			account: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
3
				.expect("infinite length input; no invalid inputs for type; qed"),
3
			percent: Percent::zero(),
3
		}
3
	}
}
pub enum BondAdjust<Balance> {
	Increase(Balance),
	Decrease,
}