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
40
#[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
572
	pub fn from_owner(owner: A) -> Self {
55
572
		Bond {
56
572
			owner,
57
572
			amount: B::default(),
58
572
		}
59
572
	}
60
}
61

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

            
64
impl<AccountId: Ord, Balance> Ord for Bond<AccountId, Balance> {
65
4463
	fn cmp(&self, other: &Self) -> Ordering {
66
4463
		self.owner.cmp(&other.owner)
67
4463
	}
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
10264
	/// 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
60
#[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
60
#[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
60
#[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
40
#[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
40
#[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
651
	fn default() -> Delegations<A, B> {
197
651
		Delegations {
198
651
			delegations: Vec::new(),
199
651
			total: B::default(),
200
651
		}
201
651
	}
202
}
203

            
204
impl<AccountId, Balance: Copy + Ord + sp_std::ops::AddAssign + Zero + Saturating>
205
	Delegations<AccountId, Balance>
206
{
207
25
	pub fn sort_greatest_to_least(&mut self) {
208
25
		self.delegations.sort_by(|a, b| b.amount.cmp(&a.amount));
209
25
	}
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
626
	pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond<AccountId, Balance>) {
214
626
		self.total = self.total.saturating_add(delegation.amount);
215
626
		// if delegations nonempty && last_element == delegation.amount => push input and return
216
626
		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
295
		}
224
		// else binary search insertion
225
366
		match self
226
366
			.delegations
227
366
			.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
355
			Err(i) => self.delegations.insert(i, delegation),
244
		}
245
626
	}
246
	/// Return the capacity status for top delegations
247
495
	pub fn top_capacity<T: Config>(&self) -> CapacityStatus {
248
495
		match &self.delegations {
249
495
			x if x.len() as u32 >= T::MaxTopDelegationsPerCandidate::get() => CapacityStatus::Full,
250
407
			x if x.is_empty() => CapacityStatus::Empty,
251
385
			_ => CapacityStatus::Partial,
252
		}
253
495
	}
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
674
	pub fn lowest_delegation_amount(&self) -> Balance {
266
674
		self.delegations
267
674
			.last()
268
674
			.map(|x| x.amount)
269
674
			.unwrap_or(Balance::zero())
270
674
	}
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
2567
	/// Empty aka contains no delegations
286
	Empty,
287
841
	/// Partially full (nonempty and not full)
288
	Partial,
289
}
290

            
291
200
#[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
674
	pub fn new(bond: Balance) -> Self {
328
674
		CandidateMetadata {
329
674
			bond,
330
674
			delegation_count: 0u32,
331
674
			total_counted: bond,
332
674
			lowest_top_delegation_amount: Zero::zero(),
333
674
			highest_bottom_delegation_amount: Zero::zero(),
334
674
			lowest_bottom_delegation_amount: Zero::zero(),
335
674
			top_capacity: CapacityStatus::Empty,
336
674
			bottom_capacity: CapacityStatus::Empty,
337
674
			request: None,
338
674
			status: CollatorStatus::Active,
339
674
		}
340
674
	}
341
545
	pub fn is_active(&self) -> bool {
342
545
		matches!(self.status, CollatorStatus::Active)
343
545
	}
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)
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
			let _ = <Pallet<T>>::thaw_extended(&who, true);
407
7
		} else {
408
7
			let _ = <Pallet<T>>::freeze_extended(&who, self.bond.into(), true);
409
7
		}
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
	}
422

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

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

            
648
4
			Pallet::<T>::deposit_event(Event::DelegationKicked {
649
4
				delegator: lowest_bottom_to_be_kicked.owner.clone(),
650
4
				candidate: candidate.clone(),
651
4
				unstaked_amount: lowest_bottom_to_be_kicked.amount,
652
4
			});
653
4
			if leaving {
654
2
				<DelegatorState<T>>::remove(&lowest_bottom_to_be_kicked.owner);
655
2
				Pallet::<T>::deposit_event(Event::DelegatorLeft {
656
2
					delegator: lowest_bottom_to_be_kicked.owner,
657
2
					unstaked_amount: lowest_bottom_to_be_kicked.amount,
658
2
				});
659
2
			} else {
660
2
				<DelegatorState<T>>::insert(&lowest_bottom_to_be_kicked.owner, delegator_state);
661
2
			}
662
4
			false
663
		} else {
664
169
			!bumped_from_top
665
		};
666
		// only increase delegation count if new bottom delegation (1) doesn't come from top &&
667
		// (2) doesn't pop the lowest delegation from the bottom
668
173
		if increase_delegation_count {
669
154
			self.delegation_count = self.delegation_count.saturating_add(1u32);
670
154
		}
671
173
		bottom_delegations.insert_sorted_greatest_to_least(delegation);
672
173
		self.reset_bottom_data::<T>(&bottom_delegations);
673
173
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
674
173
	}
675
	/// Remove delegation
676
	/// Removes from top if amount is above lowest top or top is not full
677
	/// Return Ok(if_total_counted_changed)
678
23
	pub fn rm_delegation_if_exists<T: Config>(
679
23
		&mut self,
680
23
		candidate: &T::AccountId,
681
23
		delegator: T::AccountId,
682
23
		amount: Balance,
683
23
	) -> Result<bool, DispatchError>
684
23
	where
685
23
		BalanceOf<T>: Into<Balance> + From<Balance>,
686
23
	{
687
23
		let amount_geq_lowest_top = amount >= self.lowest_top_delegation_amount;
688
23
		let top_is_not_full = !matches!(self.top_capacity, CapacityStatus::Full);
689
23
		let lowest_top_eq_highest_bottom =
690
23
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
691
23
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
692
23
		if top_is_not_full || (amount_geq_lowest_top && !lowest_top_eq_highest_bottom) {
693
23
			self.rm_top_delegation::<T>(candidate, delegator)
694
		} else if amount_geq_lowest_top && lowest_top_eq_highest_bottom {
695
			let result = self.rm_top_delegation::<T>(candidate, delegator.clone());
696
			if result == Err(delegation_dne_err) {
697
				// worst case removal
698
				self.rm_bottom_delegation::<T>(candidate, delegator)
699
			} else {
700
				result
701
			}
702
		} else {
703
			self.rm_bottom_delegation::<T>(candidate, delegator)
704
		}
705
23
	}
706
	/// Remove top delegation, bumps top bottom delegation if exists
707
23
	pub fn rm_top_delegation<T: Config>(
708
23
		&mut self,
709
23
		candidate: &T::AccountId,
710
23
		delegator: T::AccountId,
711
23
	) -> Result<bool, DispatchError>
712
23
	where
713
23
		BalanceOf<T>: Into<Balance> + From<Balance>,
714
23
	{
715
23
		let old_total_counted = self.total_counted;
716
23
		// remove top delegation
717
23
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
718
23
			.expect("CandidateInfo exists => TopDelegations exists");
719
23
		let mut actual_amount_option: Option<BalanceOf<T>> = None;
720
23
		top_delegations.delegations = top_delegations
721
23
			.delegations
722
23
			.clone()
723
23
			.into_iter()
724
25
			.filter(|d| {
725
25
				if d.owner != delegator {
726
2
					true
727
				} else {
728
23
					actual_amount_option = Some(d.amount);
729
23
					false
730
				}
731
25
			})
732
23
			.collect();
733
23
		let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
734
23
		top_delegations.total = top_delegations.total.saturating_sub(actual_amount);
735
		// if bottom nonempty => bump top bottom to top
736
23
		if !matches!(self.bottom_capacity, CapacityStatus::Empty) {
737
			let mut bottom_delegations =
738
				<BottomDelegations<T>>::get(candidate).expect("bottom is nonempty as just checked");
739
			// expect already stored greatest to least by bond amount
740
			let highest_bottom_delegation = bottom_delegations.delegations.remove(0);
741
			bottom_delegations.total = bottom_delegations
742
				.total
743
				.saturating_sub(highest_bottom_delegation.amount);
744
			self.reset_bottom_data::<T>(&bottom_delegations);
745
			<BottomDelegations<T>>::insert(candidate, bottom_delegations);
746
			// insert highest bottom into top delegations
747
			top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
748
23
		}
749
		// update candidate info
750
23
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
751
23
		self.delegation_count = self.delegation_count.saturating_sub(1u32);
752
23
		<TopDelegations<T>>::insert(candidate, top_delegations);
753
23
		// return whether total counted changed
754
23
		Ok(old_total_counted == self.total_counted)
755
23
	}
756
	/// Remove bottom delegation
757
	/// Returns if_total_counted_changed: bool
758
	pub fn rm_bottom_delegation<T: Config>(
759
		&mut self,
760
		candidate: &T::AccountId,
761
		delegator: T::AccountId,
762
	) -> Result<bool, DispatchError>
763
	where
764
		BalanceOf<T>: Into<Balance>,
765
	{
766
		// remove bottom delegation
767
		let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
768
			.expect("CandidateInfo exists => BottomDelegations exists");
769
		let mut actual_amount_option: Option<BalanceOf<T>> = None;
770
		bottom_delegations.delegations = bottom_delegations
771
			.delegations
772
			.clone()
773
			.into_iter()
774
			.filter(|d| {
775
				if d.owner != delegator {
776
					true
777
				} else {
778
					actual_amount_option = Some(d.amount);
779
					false
780
				}
781
			})
782
			.collect();
783
		let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
784
		bottom_delegations.total = bottom_delegations.total.saturating_sub(actual_amount);
785
		// update candidate info
786
		self.reset_bottom_data::<T>(&bottom_delegations);
787
		self.delegation_count = self.delegation_count.saturating_sub(1u32);
788
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
789
		Ok(false)
790
	}
791
	/// Increase delegation amount
792
21
	pub fn increase_delegation<T: Config>(
793
21
		&mut self,
794
21
		candidate: &T::AccountId,
795
21
		delegator: T::AccountId,
796
21
		bond: BalanceOf<T>,
797
21
		more: BalanceOf<T>,
798
21
	) -> Result<bool, DispatchError>
799
21
	where
800
21
		BalanceOf<T>: Into<Balance> + From<Balance>,
801
21
	{
802
21
		let lowest_top_eq_highest_bottom =
803
21
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
804
21
		let bond_geq_lowest_top = bond.into() >= self.lowest_top_delegation_amount;
805
21
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
806
21
		if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
807
			// definitely in top
808
12
			self.increase_top_delegation::<T>(candidate, delegator.clone(), more)
809
9
		} else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
810
			// update top but if error then update bottom (because could be in bottom because
811
			// lowest_top_eq_highest_bottom)
812
			let result = self.increase_top_delegation::<T>(candidate, delegator.clone(), more);
813
			if result == Err(delegation_dne_err) {
814
				self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
815
			} else {
816
				result
817
			}
818
		} else {
819
9
			self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
820
		}
821
21
	}
822
	/// Increase top delegation
823
12
	pub fn increase_top_delegation<T: Config>(
824
12
		&mut self,
825
12
		candidate: &T::AccountId,
826
12
		delegator: T::AccountId,
827
12
		more: BalanceOf<T>,
828
12
	) -> Result<bool, DispatchError>
829
12
	where
830
12
		BalanceOf<T>: Into<Balance> + From<Balance>,
831
12
	{
832
12
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
833
12
			.expect("CandidateInfo exists => TopDelegations exists");
834
12
		let mut in_top = false;
835
12
		top_delegations.delegations = top_delegations
836
12
			.delegations
837
12
			.clone()
838
12
			.into_iter()
839
19
			.map(|d| {
840
19
				if d.owner != delegator {
841
7
					d
842
				} else {
843
12
					in_top = true;
844
12
					let new_amount = d.amount.saturating_add(more);
845
12
					Bond {
846
12
						owner: d.owner,
847
12
						amount: new_amount,
848
12
					}
849
				}
850
19
			})
851
12
			.collect();
852
12
		ensure!(in_top, Error::<T>::DelegationDNE);
853
12
		top_delegations.total = top_delegations.total.saturating_add(more);
854
12
		top_delegations.sort_greatest_to_least();
855
12
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
856
12
		<TopDelegations<T>>::insert(candidate, top_delegations);
857
12
		Ok(true)
858
12
	}
859
	/// Increase bottom delegation
860
9
	pub fn increase_bottom_delegation<T: Config>(
861
9
		&mut self,
862
9
		candidate: &T::AccountId,
863
9
		delegator: T::AccountId,
864
9
		bond: BalanceOf<T>,
865
9
		more: BalanceOf<T>,
866
9
	) -> Result<bool, DispatchError>
867
9
	where
868
9
		BalanceOf<T>: Into<Balance> + From<Balance>,
869
9
	{
870
9
		let mut bottom_delegations =
871
9
			<BottomDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
872
9
		let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
873
9
		let in_top_after = if (bond.saturating_add(more)).into() > self.lowest_top_delegation_amount
874
		{
875
			// bump it from bottom
876
7
			bottom_delegations.delegations = bottom_delegations
877
7
				.delegations
878
7
				.clone()
879
7
				.into_iter()
880
26
				.filter(|d| {
881
26
					if d.owner != delegator {
882
19
						true
883
					} else {
884
7
						delegation_option = Some(Bond {
885
7
							owner: d.owner.clone(),
886
7
							amount: d.amount.saturating_add(more),
887
7
						});
888
7
						false
889
					}
890
26
				})
891
7
				.collect();
892
7
			let delegation = delegation_option.ok_or(Error::<T>::DelegationDNE)?;
893
7
			bottom_delegations.total = bottom_delegations.total.saturating_sub(bond);
894
7
			// add it to top
895
7
			let mut top_delegations = <TopDelegations<T>>::get(candidate)
896
7
				.expect("CandidateInfo existence => TopDelegations existence");
897
			// if top is full, pop lowest top
898
7
			if matches!(top_delegations.top_capacity::<T>(), CapacityStatus::Full) {
899
7
				// pop lowest top delegation
900
7
				let new_bottom_delegation = top_delegations
901
7
					.delegations
902
7
					.pop()
903
7
					.expect("Top capacity full => Exists at least 1 top delegation");
904
7
				top_delegations.total = top_delegations
905
7
					.total
906
7
					.saturating_sub(new_bottom_delegation.amount);
907
7
				bottom_delegations.insert_sorted_greatest_to_least(new_bottom_delegation);
908
7
			}
909
			// insert into top
910
7
			top_delegations.insert_sorted_greatest_to_least(delegation);
911
7
			self.reset_top_data::<T>(candidate.clone(), &top_delegations);
912
7
			<TopDelegations<T>>::insert(candidate, top_delegations);
913
7
			true
914
		} else {
915
2
			let mut in_bottom = false;
916
2
			// just increase the delegation
917
2
			bottom_delegations.delegations = bottom_delegations
918
2
				.delegations
919
2
				.clone()
920
2
				.into_iter()
921
3
				.map(|d| {
922
3
					if d.owner != delegator {
923
1
						d
924
					} else {
925
2
						in_bottom = true;
926
2
						Bond {
927
2
							owner: d.owner,
928
2
							amount: d.amount.saturating_add(more),
929
2
						}
930
					}
931
3
				})
932
2
				.collect();
933
2
			ensure!(in_bottom, Error::<T>::DelegationDNE);
934
2
			bottom_delegations.total = bottom_delegations.total.saturating_add(more);
935
2
			bottom_delegations.sort_greatest_to_least();
936
2
			false
937
		};
938
9
		self.reset_bottom_data::<T>(&bottom_delegations);
939
9
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
940
9
		Ok(in_top_after)
941
9
	}
942
	/// Decrease delegation
943
14
	pub fn decrease_delegation<T: Config>(
944
14
		&mut self,
945
14
		candidate: &T::AccountId,
946
14
		delegator: T::AccountId,
947
14
		bond: Balance,
948
14
		less: BalanceOf<T>,
949
14
	) -> Result<bool, DispatchError>
950
14
	where
951
14
		BalanceOf<T>: Into<Balance> + From<Balance>,
952
14
	{
953
14
		let lowest_top_eq_highest_bottom =
954
14
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
955
14
		let bond_geq_lowest_top = bond >= self.lowest_top_delegation_amount;
956
14
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
957
14
		if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
958
			// definitely in top
959
12
			self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less)
960
2
		} else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
961
			// update top but if error then update bottom (because could be in bottom because
962
			// lowest_top_eq_highest_bottom)
963
1
			let result =
964
1
				self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less);
965
1
			if result == Err(delegation_dne_err) {
966
				self.decrease_bottom_delegation::<T>(candidate, delegator, less)
967
			} else {
968
1
				result
969
			}
970
		} else {
971
1
			self.decrease_bottom_delegation::<T>(candidate, delegator, less)
972
		}
973
14
	}
974
	/// Decrease top delegation
975
13
	pub fn decrease_top_delegation<T: Config>(
976
13
		&mut self,
977
13
		candidate: &T::AccountId,
978
13
		delegator: T::AccountId,
979
13
		bond: BalanceOf<T>,
980
13
		less: BalanceOf<T>,
981
13
	) -> Result<bool, DispatchError>
982
13
	where
983
13
		BalanceOf<T>: Into<Balance> + From<Balance>,
984
13
	{
985
13
		// The delegation after the `decrease-delegation` will be strictly less than the
986
13
		// highest bottom delegation
987
13
		let bond_after_less_than_highest_bottom =
988
13
			bond.saturating_sub(less).into() < self.highest_bottom_delegation_amount;
989
		// The top delegations is full and the bottom delegations has at least one delegation
990
13
		let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full)
991
5
			&& !matches!(self.bottom_capacity, CapacityStatus::Empty);
992
13
		let mut top_delegations =
993
13
			<TopDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
994
13
		let in_top_after = if bond_after_less_than_highest_bottom && full_top_and_nonempty_bottom {
995
3
			let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
996
3
			// take delegation from top
997
3
			top_delegations.delegations = top_delegations
998
3
				.delegations
999
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
10
			let mut is_in_top = false;
10
			top_delegations.delegations = top_delegations
10
				.delegations
10
				.clone()
10
				.into_iter()
16
				.map(|d| {
16
					if d.owner != delegator {
6
						d
					} else {
10
						is_in_top = true;
10
						Bond {
10
							owner: d.owner,
10
							amount: d.amount.saturating_sub(less),
10
						}
					}
16
				})
10
				.collect();
10
			ensure!(is_in_top, Error::<T>::DelegationDNE);
10
			top_delegations.total = top_delegations.total.saturating_sub(less);
10
			top_delegations.sort_greatest_to_least();
10
			true
		};
13
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
13
		<TopDelegations<T>>::insert(candidate, top_delegations);
13
		Ok(in_top_after)
13
	}
	/// 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
40
#[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 {
658
	/// 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),
}
100
#[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> {
2
	fn eq(&self, other: &Self) -> bool {
2
		let must_be_true = self.id == other.id
2
			&& self.total == other.total
2
			&& self.less_total == other.less_total
2
			&& self.status == other.status;
2
		if !must_be_true {
			return false;
2
		}
		for (
			Bond {
2
				owner: o1,
2
				amount: a1,
2
			},
2
			Bond {
2
				owner: o2,
2
				amount: a2,
			},
2
		) in self.delegations.0.iter().zip(other.delegations.0.iter())
		{
2
			if o1 != o2 || a1 != a2 {
				return false;
2
			}
		}
2
		true
2
	}
}
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>
{
9006
	pub fn new(id: AccountId, collator: AccountId, amount: Balance) -> Self {
9006
		Delegator {
9006
			id,
9006
			delegations: OrderedSet::from(vec![Bond {
9006
				owner: collator,
9006
				amount,
9006
			}]),
9006
			total: amount,
9006
			less_total: Balance::zero(),
9006
			status: DelegatorStatus::Active,
9006
		}
9006
	}
	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,
		}
	}
45
	pub fn total(&self) -> Balance {
45
		self.total
45
	}
14
	pub fn total_sub_if<T, F>(&mut self, amount: Balance, check: F) -> DispatchResult
14
	where
14
		T: Config,
14
		T::AccountId: From<AccountId>,
14
		BalanceOf<T>: From<Balance>,
14
		F: Fn(Balance) -> DispatchResult,
14
	{
14
		let total = self.total.saturating_sub(amount);
14
		check(total)?;
14
		self.total = total;
14
		self.adjust_bond_lock::<T>(BondAdjust::Decrease)?;
14
		Ok(())
14
	}
	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)
	}
65
	pub fn add_delegation(&mut self, bond: Bond<AccountId, Balance>) -> bool {
65
		let amt = bond.amount;
65
		if self.delegations.insert(bond) {
63
			self.total = self.total.saturating_add(amt);
63
			true
		} else {
2
			false
		}
65
	}
	// 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)`).
666
	pub fn adjust_bond_lock<T: Config>(
666
		&mut self,
666
		additional_required_balance: BondAdjust<Balance>,
666
	) -> DispatchResult
666
	where
666
		BalanceOf<T>: From<Balance>,
666
		T::AccountId: From<AccountId>,
666
	{
666
		let who: T::AccountId = self.id.clone().into();
666

            
666
		match additional_required_balance {
610
			BondAdjust::Increase(amount) => {
610
				ensure!(
610
					<Pallet<T>>::get_delegator_stakable_balance(&who) >= amount.into(),
					Error::<T>::InsufficientBalance,
				);
				// additional sanity check: shouldn't ever want to freeze more than total
610
				if amount > self.total {
					log::warn!("LOGIC ERROR: request to freeze more than bond total");
					return Err(DispatchError::Other("Invalid additional_required_balance"));
610
				}
			}
56
			BondAdjust::Decrease => {
56
				// Nothing special to do for decrease with freezes
56
			}
		}
		// Set the freeze to the total amount
666
		if self.total > Balance::zero() {
643
			<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
		}
666
		Ok(())
666
	}
	/// Retrieves the bond amount that a delegator has provided towards a collator.
	/// Returns `None` if missing.
77
	pub fn get_bond_amount(&self, collator: &AccountId) -> Option<Balance> {
77
		self.delegations
77
			.0
77
			.iter()
84
			.find(|b| &b.owner == collator)
77
			.map(|b| b.amount)
77
	}
}
80
#[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>
{
551
	pub fn new(current: RoundIndex, first: B, length: u32, first_slot: u64) -> RoundInfo<B> {
551
		RoundInfo {
551
			current,
551
			first,
551
			length,
551
			first_slot,
551
		}
551
	}
	/// Check if the round should be updated
30769
	pub fn should_update(&self, now: B) -> bool {
30769
		now - self.first >= self.length.into()
30769
	}
	/// New round
265
	pub fn update(&mut self, now: B, now_slot: u64) {
265
		self.current = self.current.saturating_add(1u32);
265
		self.first = now;
265
		self.first_slot = now_slot;
265
	}
}
impl<
		B: Copy + sp_std::ops::Add<Output = B> + sp_std::ops::Sub<Output = B> + From<u32> + PartialOrd,
	> Default for RoundInfo<B>
{
20
	fn default() -> RoundInfo<B> {
20
		RoundInfo::new(1u32, 1u32.into(), 20u32, 0)
20
	}
}
// Type which encapsulates the configuration for the inflation distribution.
20
#[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>
{
628
	fn from(configs: [InflationDistributionAccount<AccountId>; 2]) -> Self {
628
		InflationDistributionConfig(configs)
628
	}
}
impl<AccountId: Decode> Default for InflationDistributionConfig<AccountId> {
20
	fn default() -> InflationDistributionConfig<AccountId> {
20
		InflationDistributionConfig([
20
			InflationDistributionAccount {
20
				account: AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
20
					.expect("infinite length input; no invalid inputs for type; qed"),
20
				percent: Percent::zero(),
20
			},
20
			InflationDistributionAccount {
20
				account: AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
20
					.expect("infinite length input; no invalid inputs for type; qed"),
20
				percent: Percent::zero(),
20
			},
20
		])
20
	}
}
40
#[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,
}