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, COLLATOR_LOCK_ID, DELEGATOR_LOCK_ID,
23
};
24
use frame_support::{
25
	pallet_prelude::*,
26
	traits::{tokens::WithdrawReasons, LockableCurrency},
27
};
28
use parity_scale_codec::{Decode, Encode};
29
use sp_runtime::{
30
	traits::{AtLeast32BitUnsigned, Saturating, Zero},
31
	Perbill, Percent, RuntimeDebug,
32
};
33
use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*};
34

            
35
pub struct CountedDelegations<T: Config> {
36
	pub uncounted_stake: BalanceOf<T>,
37
	pub rewardable_delegations: Vec<Bond<T::AccountId, BalanceOf<T>>>,
38
}
39

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

            
46
impl<A: Decode, B: Default> Default for Bond<A, B> {
47
	fn default() -> Bond<A, B> {
48
		Bond {
49
			owner: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
50
				.expect("infinite length input; no invalid inputs for type; qed"),
51
			amount: B::default(),
52
		}
53
	}
54
}
55

            
56
impl<A, B: Default> Bond<A, B> {
57
568
	pub fn from_owner(owner: A) -> Self {
58
568
		Bond {
59
568
			owner,
60
568
			amount: B::default(),
61
568
		}
62
568
	}
63
}
64

            
65
impl<AccountId: Ord, Balance> Eq for Bond<AccountId, Balance> {}
66

            
67
impl<AccountId: Ord, Balance> Ord for Bond<AccountId, Balance> {
68
3408
	fn cmp(&self, other: &Self) -> Ordering {
69
3408
		self.owner.cmp(&other.owner)
70
3408
	}
71
}
72

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

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

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

            
96
impl Default for CollatorStatus {
97
	fn default() -> CollatorStatus {
98
		CollatorStatus::Active
99
	}
100
}
101

            
102
60
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
103
pub struct BondWithAutoCompound<AccountId, Balance> {
104
	pub owner: AccountId,
105
	pub amount: Balance,
106
	pub auto_compound: Percent,
107
}
108

            
109
impl<A: Decode, B: Default> Default for BondWithAutoCompound<A, B> {
110
	fn default() -> BondWithAutoCompound<A, B> {
111
		BondWithAutoCompound {
112
			owner: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
113
				.expect("infinite length input; no invalid inputs for type; qed"),
114
			amount: B::default(),
115
			auto_compound: Percent::zero(),
116
		}
117
	}
118
}
119

            
120
60
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
121
/// Snapshot of collator state at the start of the round for which they are selected
122
pub struct CollatorSnapshot<AccountId, Balance> {
123
	/// The total value locked by the collator.
124
	pub bond: Balance,
125

            
126
	/// The rewardable delegations. This list is a subset of total delegators, where certain
127
	/// delegators are adjusted based on their scheduled
128
	/// [DelegationChange::Revoke] or [DelegationChange::Decrease] action.
129
	pub delegations: Vec<BondWithAutoCompound<AccountId, Balance>>,
130

            
131
	/// The total counted value locked for the collator, including the self bond + total staked by
132
	/// top delegators.
133
	pub total: Balance,
134
}
135

            
136
impl<A: PartialEq, B: PartialEq> PartialEq for CollatorSnapshot<A, B> {
137
5
	fn eq(&self, other: &Self) -> bool {
138
5
		let must_be_true = self.bond == other.bond && self.total == other.total;
139
5
		if !must_be_true {
140
			return false;
141
5
		}
142
		for (
143
			BondWithAutoCompound {
144
				owner: o1,
145
				amount: a1,
146
				auto_compound: c1,
147
			},
148
			BondWithAutoCompound {
149
				owner: o2,
150
				amount: a2,
151
				auto_compound: c2,
152
			},
153
5
		) in self.delegations.iter().zip(other.delegations.iter())
154
		{
155
			if o1 != o2 || a1 != a2 || c1 != c2 {
156
				return false;
157
			}
158
		}
159
5
		true
160
5
	}
161
}
162

            
163
impl<A, B: Default> Default for CollatorSnapshot<A, B> {
164
	fn default() -> CollatorSnapshot<A, B> {
165
		CollatorSnapshot {
166
			bond: B::default(),
167
			delegations: Vec::new(),
168
			total: B::default(),
169
		}
170
	}
171
}
172

            
173
60
#[derive(Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
174
/// Info needed to make delayed payments to stakers after round end
175
pub struct DelayedPayout<Balance> {
176
	/// Total round reward (result of compute_issuance() at round end)
177
	pub round_issuance: Balance,
178
	/// The total inflation paid this round to stakers (e.g. less parachain bond fund)
179
	pub total_staking_reward: Balance,
180
	/// Snapshot of collator commission rate at the end of the round
181
	pub collator_commission: Perbill,
182
}
183

            
184
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
185
/// DEPRECATED
186
/// Collator state with commission fee, bonded stake, and delegations
187
pub struct Collator2<AccountId, Balance> {
188
	/// The account of this collator
189
	pub id: AccountId,
190
	/// This collator's self stake.
191
	pub bond: Balance,
192
	/// Set of all nominator AccountIds (to prevent >1 nomination per AccountId)
193
	pub nominators: OrderedSet<AccountId>,
194
	/// Top T::MaxDelegatorsPerCollator::get() nominators, ordered greatest to least
195
	pub top_nominators: Vec<Bond<AccountId, Balance>>,
196
	/// Bottom nominators (unbounded), ordered least to greatest
197
	pub bottom_nominators: Vec<Bond<AccountId, Balance>>,
198
	/// Sum of top delegations + self.bond
199
	pub total_counted: Balance,
200
	/// Sum of all delegations + self.bond = (total_counted + uncounted)
201
	pub total_backing: Balance,
202
	/// Current status of the collator
203
	pub state: CollatorStatus,
204
}
205

            
206
impl<A, B> From<Collator2<A, B>> for CollatorCandidate<A, B> {
207
	fn from(other: Collator2<A, B>) -> CollatorCandidate<A, B> {
208
		CollatorCandidate {
209
			id: other.id,
210
			bond: other.bond,
211
			delegators: other.nominators,
212
			top_delegations: other.top_nominators,
213
			bottom_delegations: other.bottom_nominators,
214
			total_counted: other.total_counted,
215
			total_backing: other.total_backing,
216
			request: None,
217
			state: other.state,
218
		}
219
	}
220
}
221

            
222
40
#[derive(PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
223
/// Request scheduled to change the collator candidate self-bond
224
pub struct CandidateBondLessRequest<Balance> {
225
	pub amount: Balance,
226
	pub when_executable: RoundIndex,
227
}
228

            
229
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
230
/// DEPRECATED, replaced by `CandidateMetadata` and two storage instances of `Delegations`
231
/// Collator candidate state with self bond + delegations
232
pub struct CollatorCandidate<AccountId, Balance> {
233
	/// The account of this collator
234
	pub id: AccountId,
235
	/// This collator's self stake.
236
	pub bond: Balance,
237
	/// Set of all delegator AccountIds (to prevent >1 delegation per AccountId)
238
	pub delegators: OrderedSet<AccountId>,
239
	/// Top T::MaxDelegatorsPerCollator::get() delegations, ordered greatest to least
240
	pub top_delegations: Vec<Bond<AccountId, Balance>>,
241
	/// Bottom delegations (unbounded), ordered least to greatest
242
	pub bottom_delegations: Vec<Bond<AccountId, Balance>>,
243
	/// Sum of top delegations + self.bond
244
	pub total_counted: Balance,
245
	/// Sum of all delegations + self.bond = (total_counted + uncounted)
246
	pub total_backing: Balance,
247
	/// Maximum 1 pending request to decrease candidate self bond at any given time
248
	pub request: Option<CandidateBondLessRequest<Balance>>,
249
	/// Current status of the collator
250
	pub state: CollatorStatus,
251
}
252

            
253
40
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
254
/// Type for top and bottom delegation storage item
255
pub struct Delegations<AccountId, Balance> {
256
	pub delegations: Vec<Bond<AccountId, Balance>>,
257
	pub total: Balance,
258
}
259

            
260
impl<A, B: Default> Default for Delegations<A, B> {
261
652
	fn default() -> Delegations<A, B> {
262
652
		Delegations {
263
652
			delegations: Vec::new(),
264
652
			total: B::default(),
265
652
		}
266
652
	}
267
}
268

            
269
impl<AccountId, Balance: Copy + Ord + sp_std::ops::AddAssign + Zero + Saturating>
270
	Delegations<AccountId, Balance>
271
{
272
25
	pub fn sort_greatest_to_least(&mut self) {
273
25
		self.delegations.sort_by(|a, b| b.amount.cmp(&a.amount));
274
25
	}
275
	/// Insert sorted greatest to least and increase .total accordingly
276
	/// Insertion respects first come first serve so new delegations are pushed after existing
277
	/// delegations if the amount is the same
278
626
	pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond<AccountId, Balance>) {
279
626
		self.total = self.total.saturating_add(delegation.amount);
280
626
		// if delegations nonempty && last_element == delegation.amount => push input and return
281
626
		if !self.delegations.is_empty() {
282
			// if last_element == delegation.amount => push the delegation and return early
283
331
			if self.delegations[self.delegations.len() - 1].amount == delegation.amount {
284
260
				self.delegations.push(delegation);
285
260
				// early return
286
260
				return;
287
71
			}
288
295
		}
289
		// else binary search insertion
290
366
		match self
291
366
			.delegations
292
366
			.binary_search_by(|x| delegation.amount.cmp(&x.amount))
293
		{
294
			// sorted insertion on sorted vec
295
			// enforces first come first serve for equal bond amounts
296
11
			Ok(i) => {
297
11
				let mut new_index = i + 1;
298
11
				while new_index <= (self.delegations.len() - 1) {
299
11
					if self.delegations[new_index].amount == delegation.amount {
300
						new_index = new_index.saturating_add(1);
301
					} else {
302
11
						self.delegations.insert(new_index, delegation);
303
11
						return;
304
					}
305
				}
306
				self.delegations.push(delegation)
307
			}
308
355
			Err(i) => self.delegations.insert(i, delegation),
309
		}
310
626
	}
311
	/// Return the capacity status for top delegations
312
495
	pub fn top_capacity<T: Config>(&self) -> CapacityStatus {
313
495
		match &self.delegations {
314
495
			x if x.len() as u32 >= T::MaxTopDelegationsPerCandidate::get() => CapacityStatus::Full,
315
407
			x if x.is_empty() => CapacityStatus::Empty,
316
385
			_ => CapacityStatus::Partial,
317
		}
318
495
	}
319
	/// Return the capacity status for bottom delegations
320
186
	pub fn bottom_capacity<T: Config>(&self) -> CapacityStatus {
321
186
		match &self.delegations {
322
186
			x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => {
323
52
				CapacityStatus::Full
324
			}
325
134
			x if x.is_empty() => CapacityStatus::Empty,
326
134
			_ => CapacityStatus::Partial,
327
		}
328
186
	}
329
	/// Return last delegation amount without popping the delegation
330
674
	pub fn lowest_delegation_amount(&self) -> Balance {
331
674
		self.delegations
332
674
			.last()
333
674
			.map(|x| x.amount)
334
674
			.unwrap_or(Balance::zero())
335
674
	}
336
	/// Return highest delegation amount
337
186
	pub fn highest_delegation_amount(&self) -> Balance {
338
186
		self.delegations
339
186
			.first()
340
186
			.map(|x| x.amount)
341
186
			.unwrap_or(Balance::zero())
342
186
	}
343
}
344

            
345
189
#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
346
/// Capacity status for top or bottom delegations
347
pub enum CapacityStatus {
348
17268
	/// Reached capacity
349
	Full,
350
2461
	/// Empty aka contains no delegations
351
	Empty,
352
837
	/// Partially full (nonempty and not full)
353
	Partial,
354
}
355

            
356
200
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
357
/// All candidate info except the top and bottom delegations
358
pub struct CandidateMetadata<Balance> {
359
	/// This candidate's self bond amount
360
	pub bond: Balance,
361
	/// Total number of delegations to this candidate
362
	pub delegation_count: u32,
363
	/// Self bond + sum of top delegations
364
	pub total_counted: Balance,
365
	/// The smallest top delegation amount
366
	pub lowest_top_delegation_amount: Balance,
367
	/// The highest bottom delegation amount
368
	pub highest_bottom_delegation_amount: Balance,
369
	/// The smallest bottom delegation amount
370
	pub lowest_bottom_delegation_amount: Balance,
371
	/// Capacity status for top delegations
372
	pub top_capacity: CapacityStatus,
373
	/// Capacity status for bottom delegations
374
	pub bottom_capacity: CapacityStatus,
375
	/// Maximum 1 pending request to decrease candidate self bond at any given time
376
	pub request: Option<CandidateBondLessRequest<Balance>>,
377
	/// Current status of the collator
378
	pub status: CollatorStatus,
379
}
380

            
381
impl<
382
		Balance: Copy
383
			+ Zero
384
			+ PartialOrd
385
			+ sp_std::ops::AddAssign
386
			+ sp_std::ops::SubAssign
387
			+ sp_std::ops::Sub<Output = Balance>
388
			+ sp_std::fmt::Debug
389
			+ Saturating,
390
	> CandidateMetadata<Balance>
391
{
392
652
	pub fn new(bond: Balance) -> Self {
393
652
		CandidateMetadata {
394
652
			bond,
395
652
			delegation_count: 0u32,
396
652
			total_counted: bond,
397
652
			lowest_top_delegation_amount: Zero::zero(),
398
652
			highest_bottom_delegation_amount: Zero::zero(),
399
652
			lowest_bottom_delegation_amount: Zero::zero(),
400
652
			top_capacity: CapacityStatus::Empty,
401
652
			bottom_capacity: CapacityStatus::Empty,
402
652
			request: None,
403
652
			status: CollatorStatus::Active,
404
652
		}
405
652
	}
406
542
	pub fn is_active(&self) -> bool {
407
542
		matches!(self.status, CollatorStatus::Active)
408
542
	}
409
57
	pub fn is_leaving(&self) -> bool {
410
57
		matches!(self.status, CollatorStatus::Leaving(_))
411
57
	}
412
46
	pub fn schedule_leave<T: Config>(&mut self) -> Result<(RoundIndex, RoundIndex), DispatchError> {
413
46
		ensure!(!self.is_leaving(), Error::<T>::CandidateAlreadyLeaving);
414
45
		let now = <Round<T>>::get().current;
415
45
		let when = now + T::LeaveCandidatesDelay::get();
416
45
		self.status = CollatorStatus::Leaving(when);
417
45
		Ok((now, when))
418
46
	}
419
23
	pub fn can_leave<T: Config>(&self) -> DispatchResult {
420
23
		if let CollatorStatus::Leaving(when) = self.status {
421
23
			ensure!(
422
23
				<Round<T>>::get().current >= when,
423
2
				Error::<T>::CandidateCannotLeaveYet
424
			);
425
21
			Ok(())
426
		} else {
427
			Err(Error::<T>::CandidateNotLeaving.into())
428
		}
429
23
	}
430
14
	pub fn go_offline(&mut self) {
431
14
		self.status = CollatorStatus::Idle;
432
14
	}
433
8
	pub fn go_online(&mut self) {
434
8
		self.status = CollatorStatus::Active;
435
8
	}
436
6
	pub fn bond_more<T: Config>(&mut self, who: T::AccountId, more: Balance) -> DispatchResult
437
6
	where
438
6
		BalanceOf<T>: From<Balance>,
439
6
	{
440
6
		ensure!(
441
6
			<Pallet<T>>::get_collator_stakable_free_balance(&who) >= more.into(),
442
			Error::<T>::InsufficientBalance
443
		);
444
6
		let new_total = <Total<T>>::get().saturating_add(more.into());
445
6
		<Total<T>>::put(new_total);
446
6
		self.bond = self.bond.saturating_add(more);
447
6
		T::Currency::set_lock(
448
6
			COLLATOR_LOCK_ID,
449
6
			&who.clone(),
450
6
			self.bond.into(),
451
6
			WithdrawReasons::all(),
452
6
		);
453
6
		self.total_counted = self.total_counted.saturating_add(more);
454
6
		<Pallet<T>>::deposit_event(Event::CandidateBondedMore {
455
6
			candidate: who.clone(),
456
6
			amount: more.into(),
457
6
			new_total_bond: self.bond.into(),
458
6
		});
459
6
		Ok(())
460
6
	}
461

            
462
6
	pub fn bond_less<T: Config>(&mut self, who: T::AccountId, amount: Balance)
463
6
	where
464
6
		BalanceOf<T>: From<Balance>,
465
6
	{
466
6
		let new_total_staked = <Total<T>>::get().saturating_sub(amount.into());
467
6
		<Total<T>>::put(new_total_staked);
468
6
		self.bond = self.bond.saturating_sub(amount);
469
6
		if self.bond.is_zero() {
470
			T::Currency::remove_lock(COLLATOR_LOCK_ID, &who);
471
6
		} else {
472
6
			T::Currency::set_lock(
473
6
				COLLATOR_LOCK_ID,
474
6
				&who,
475
6
				self.bond.into(),
476
6
				WithdrawReasons::all(),
477
6
			);
478
6
		}
479
6
		self.total_counted = self.total_counted.saturating_sub(amount);
480
6
		let event = Event::CandidateBondedLess {
481
6
			candidate: who.clone(),
482
6
			amount: amount.into(),
483
6
			new_bond: self.bond.into(),
484
6
		};
485
6
		// update candidate pool value because it must change if self bond changes
486
6
		if self.is_active() {
487
6
			Pallet::<T>::update_active(who, self.total_counted.into());
488
6
		}
489
6
		Pallet::<T>::deposit_event(event);
490
6
	}
491

            
492
	/// Schedule executable decrease of collator candidate self bond
493
	/// Returns the round at which the collator can execute the pending request
494
17
	pub fn schedule_bond_less<T: Config>(
495
17
		&mut self,
496
17
		less: Balance,
497
17
	) -> Result<RoundIndex, DispatchError>
498
17
	where
499
17
		BalanceOf<T>: Into<Balance>,
500
17
	{
501
17
		// ensure no pending request
502
17
		ensure!(
503
17
			self.request.is_none(),
504
1
			Error::<T>::PendingCandidateRequestAlreadyExists
505
		);
506
		// ensure bond above min after decrease
507
16
		ensure!(self.bond > less, Error::<T>::CandidateBondBelowMin);
508
16
		ensure!(
509
16
			self.bond - less >= T::MinCandidateStk::get().into(),
510
1
			Error::<T>::CandidateBondBelowMin
511
		);
512
15
		let when_executable = <Round<T>>::get().current + T::CandidateBondLessDelay::get();
513
15
		self.request = Some(CandidateBondLessRequest {
514
15
			amount: less,
515
15
			when_executable,
516
15
		});
517
15
		Ok(when_executable)
518
17
	}
519
	/// Execute pending request to decrease the collator self bond
520
	/// Returns the event to be emitted
521
6
	pub fn execute_bond_less<T: Config>(&mut self, who: T::AccountId) -> DispatchResult
522
6
	where
523
6
		BalanceOf<T>: From<Balance>,
524
6
	{
525
6
		let request = self
526
6
			.request
527
6
			.ok_or(Error::<T>::PendingCandidateRequestsDNE)?;
528
6
		ensure!(
529
6
			request.when_executable <= <Round<T>>::get().current,
530
			Error::<T>::PendingCandidateRequestNotDueYet
531
		);
532
6
		self.bond_less::<T>(who.clone(), request.amount);
533
6
		// reset s.t. no pending request
534
6
		self.request = None;
535
6
		Ok(())
536
6
	}
537

            
538
	/// Cancel candidate bond less request
539
3
	pub fn cancel_bond_less<T: Config>(&mut self, who: T::AccountId) -> DispatchResult
540
3
	where
541
3
		BalanceOf<T>: From<Balance>,
542
3
	{
543
3
		let request = self
544
3
			.request
545
3
			.ok_or(Error::<T>::PendingCandidateRequestsDNE)?;
546
3
		let event = Event::CancelledCandidateBondLess {
547
3
			candidate: who.clone().into(),
548
3
			amount: request.amount.into(),
549
3
			execute_round: request.when_executable,
550
3
		};
551
3
		self.request = None;
552
3
		Pallet::<T>::deposit_event(event);
553
3
		Ok(())
554
3
	}
555
	/// Reset top delegations metadata
556
488
	pub fn reset_top_data<T: Config>(
557
488
		&mut self,
558
488
		candidate: T::AccountId,
559
488
		top_delegations: &Delegations<T::AccountId, BalanceOf<T>>,
560
488
	) where
561
488
		BalanceOf<T>: Into<Balance> + From<Balance>,
562
488
	{
563
488
		self.lowest_top_delegation_amount = top_delegations.lowest_delegation_amount().into();
564
488
		self.top_capacity = top_delegations.top_capacity::<T>();
565
488
		let old_total_counted = self.total_counted;
566
488
		self.total_counted = self.bond.saturating_add(top_delegations.total.into());
567
488
		// CandidatePool value for candidate always changes if top delegations total changes
568
488
		// so we moved the update into this function to deduplicate code and patch a bug that
569
488
		// forgot to apply the update when increasing top delegation
570
488
		if old_total_counted != self.total_counted && self.is_active() {
571
484
			Pallet::<T>::update_active(candidate, self.total_counted.into());
572
484
		}
573
488
	}
574
	/// Reset bottom delegations metadata
575
186
	pub fn reset_bottom_data<T: Config>(
576
186
		&mut self,
577
186
		bottom_delegations: &Delegations<T::AccountId, BalanceOf<T>>,
578
186
	) where
579
186
		BalanceOf<T>: Into<Balance>,
580
186
	{
581
186
		self.lowest_bottom_delegation_amount = bottom_delegations.lowest_delegation_amount().into();
582
186
		self.highest_bottom_delegation_amount =
583
186
			bottom_delegations.highest_delegation_amount().into();
584
186
		self.bottom_capacity = bottom_delegations.bottom_capacity::<T>();
585
186
	}
586
	/// Add delegation
587
	/// Returns whether delegator was added and an optional negative total counted remainder
588
	/// for if a bottom delegation was kicked
589
	/// MUST ensure no delegation exists for this candidate in the `DelegatorState` before call
590
9059
	pub fn add_delegation<T: Config>(
591
9059
		&mut self,
592
9059
		candidate: &T::AccountId,
593
9059
		delegation: Bond<T::AccountId, BalanceOf<T>>,
594
9059
	) -> Result<(DelegatorAdded<Balance>, Option<Balance>), DispatchError>
595
9059
	where
596
9059
		BalanceOf<T>: Into<Balance> + From<Balance>,
597
9059
	{
598
9059
		let mut less_total_staked = None;
599
9059
		let delegator_added = match self.top_capacity {
600
			CapacityStatus::Full => {
601
				// top is full, insert into top iff the lowest_top < amount
602
8643
				if self.lowest_top_delegation_amount < delegation.amount.into() {
603
					// bumps lowest top to the bottom inside this function call
604
17
					less_total_staked = self.add_top_delegation::<T>(candidate, delegation);
605
17
					DelegatorAdded::AddedToTop {
606
17
						new_total: self.total_counted,
607
17
					}
608
				} else {
609
					// if bottom is full, only insert if greater than lowest bottom (which will
610
					// be bumped out)
611
8626
					if matches!(self.bottom_capacity, CapacityStatus::Full) {
612
8472
						ensure!(
613
8472
							delegation.amount.into() > self.lowest_bottom_delegation_amount,
614
8470
							Error::<T>::CannotDelegateLessThanOrEqualToLowestBottomWhenFull
615
						);
616
						// need to subtract from total staked
617
2
						less_total_staked = Some(self.lowest_bottom_delegation_amount);
618
154
					}
619
					// insert into bottom
620
156
					self.add_bottom_delegation::<T>(false, candidate, delegation);
621
156
					DelegatorAdded::AddedToBottom
622
				}
623
			}
624
			// top is either empty or partially full
625
			_ => {
626
416
				self.add_top_delegation::<T>(candidate, delegation);
627
416
				DelegatorAdded::AddedToTop {
628
416
					new_total: self.total_counted,
629
416
				}
630
			}
631
		};
632
589
		Ok((delegator_added, less_total_staked))
633
9059
	}
634
	/// Add delegation to top delegation
635
	/// Returns Option<negative_total_staked_remainder>
636
	/// Only call if lowest top delegation is less than delegation.amount || !top_full
637
433
	pub fn add_top_delegation<T: Config>(
638
433
		&mut self,
639
433
		candidate: &T::AccountId,
640
433
		delegation: Bond<T::AccountId, BalanceOf<T>>,
641
433
	) -> Option<Balance>
642
433
	where
643
433
		BalanceOf<T>: Into<Balance> + From<Balance>,
644
433
	{
645
433
		let mut less_total_staked = None;
646
433
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
647
433
			.expect("CandidateInfo existence => TopDelegations existence");
648
433
		let max_top_delegations_per_candidate = T::MaxTopDelegationsPerCandidate::get();
649
433
		if top_delegations.delegations.len() as u32 == max_top_delegations_per_candidate {
650
			// pop lowest top delegation
651
17
			let new_bottom_delegation = top_delegations.delegations.pop().expect("");
652
17
			top_delegations.total = top_delegations
653
17
				.total
654
17
				.saturating_sub(new_bottom_delegation.amount);
655
17
			if matches!(self.bottom_capacity, CapacityStatus::Full) {
656
2
				less_total_staked = Some(self.lowest_bottom_delegation_amount);
657
15
			}
658
17
			self.add_bottom_delegation::<T>(true, candidate, new_bottom_delegation);
659
416
		}
660
		// insert into top
661
433
		top_delegations.insert_sorted_greatest_to_least(delegation);
662
433
		// update candidate info
663
433
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
664
433
		if less_total_staked.is_none() {
665
431
			// only increment delegation count if we are not kicking a bottom delegation
666
431
			self.delegation_count = self.delegation_count.saturating_add(1u32);
667
431
		}
668
433
		<TopDelegations<T>>::insert(&candidate, top_delegations);
669
433
		less_total_staked
670
433
	}
671
	/// Add delegation to bottom delegations
672
	/// Check before call that if capacity is full, inserted delegation is higher than lowest
673
	/// bottom delegation (and if so, need to adjust the total storage item)
674
	/// CALLER MUST ensure(lowest_bottom_to_be_kicked.amount < delegation.amount)
675
173
	pub fn add_bottom_delegation<T: Config>(
676
173
		&mut self,
677
173
		bumped_from_top: bool,
678
173
		candidate: &T::AccountId,
679
173
		delegation: Bond<T::AccountId, BalanceOf<T>>,
680
173
	) where
681
173
		BalanceOf<T>: Into<Balance> + From<Balance>,
682
173
	{
683
173
		let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
684
173
			.expect("CandidateInfo existence => BottomDelegations existence");
685
		// if bottom is full, kick the lowest bottom (which is expected to be lower than input
686
		// as per check)
687
173
		let increase_delegation_count = if bottom_delegations.delegations.len() as u32
688
173
			== T::MaxBottomDelegationsPerCandidate::get()
689
		{
690
4
			let lowest_bottom_to_be_kicked = bottom_delegations
691
4
				.delegations
692
4
				.pop()
693
4
				.expect("if at full capacity (>0), then >0 bottom delegations exist; qed");
694
4
			// EXPECT lowest_bottom_to_be_kicked.amount < delegation.amount enforced by caller
695
4
			// if lowest_bottom_to_be_kicked.amount == delegation.amount, we will still kick
696
4
			// the lowest bottom to enforce first come first served
697
4
			bottom_delegations.total = bottom_delegations
698
4
				.total
699
4
				.saturating_sub(lowest_bottom_to_be_kicked.amount);
700
4
			// update delegator state
701
4
			// total staked is updated via propagation of lowest bottom delegation amount prior
702
4
			// to call
703
4
			let mut delegator_state = <DelegatorState<T>>::get(&lowest_bottom_to_be_kicked.owner)
704
4
				.expect("Delegation existence => DelegatorState existence");
705
4
			let leaving = delegator_state.delegations.0.len() == 1usize;
706
4
			delegator_state.rm_delegation::<T>(candidate);
707
4
			<Pallet<T>>::delegation_remove_request_with_state(
708
4
				&candidate,
709
4
				&lowest_bottom_to_be_kicked.owner,
710
4
				&mut delegator_state,
711
4
			);
712
4
			<AutoCompoundDelegations<T>>::remove_auto_compound(
713
4
				&candidate,
714
4
				&lowest_bottom_to_be_kicked.owner,
715
4
			);
716
4

            
717
4
			Pallet::<T>::deposit_event(Event::DelegationKicked {
718
4
				delegator: lowest_bottom_to_be_kicked.owner.clone(),
719
4
				candidate: candidate.clone(),
720
4
				unstaked_amount: lowest_bottom_to_be_kicked.amount,
721
4
			});
722
4
			if leaving {
723
2
				<DelegatorState<T>>::remove(&lowest_bottom_to_be_kicked.owner);
724
2
				Pallet::<T>::deposit_event(Event::DelegatorLeft {
725
2
					delegator: lowest_bottom_to_be_kicked.owner,
726
2
					unstaked_amount: lowest_bottom_to_be_kicked.amount,
727
2
				});
728
2
			} else {
729
2
				<DelegatorState<T>>::insert(&lowest_bottom_to_be_kicked.owner, delegator_state);
730
2
			}
731
4
			false
732
		} else {
733
169
			!bumped_from_top
734
		};
735
		// only increase delegation count if new bottom delegation (1) doesn't come from top &&
736
		// (2) doesn't pop the lowest delegation from the bottom
737
173
		if increase_delegation_count {
738
154
			self.delegation_count = self.delegation_count.saturating_add(1u32);
739
154
		}
740
173
		bottom_delegations.insert_sorted_greatest_to_least(delegation);
741
173
		self.reset_bottom_data::<T>(&bottom_delegations);
742
173
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
743
173
	}
744
	/// Remove delegation
745
	/// Removes from top if amount is above lowest top or top is not full
746
	/// Return Ok(if_total_counted_changed)
747
23
	pub fn rm_delegation_if_exists<T: Config>(
748
23
		&mut self,
749
23
		candidate: &T::AccountId,
750
23
		delegator: T::AccountId,
751
23
		amount: Balance,
752
23
	) -> Result<bool, DispatchError>
753
23
	where
754
23
		BalanceOf<T>: Into<Balance> + From<Balance>,
755
23
	{
756
23
		let amount_geq_lowest_top = amount >= self.lowest_top_delegation_amount;
757
23
		let top_is_not_full = !matches!(self.top_capacity, CapacityStatus::Full);
758
23
		let lowest_top_eq_highest_bottom =
759
23
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
760
23
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
761
23
		if top_is_not_full || (amount_geq_lowest_top && !lowest_top_eq_highest_bottom) {
762
23
			self.rm_top_delegation::<T>(candidate, delegator)
763
		} else if amount_geq_lowest_top && lowest_top_eq_highest_bottom {
764
			let result = self.rm_top_delegation::<T>(candidate, delegator.clone());
765
			if result == Err(delegation_dne_err) {
766
				// worst case removal
767
				self.rm_bottom_delegation::<T>(candidate, delegator)
768
			} else {
769
				result
770
			}
771
		} else {
772
			self.rm_bottom_delegation::<T>(candidate, delegator)
773
		}
774
23
	}
775
	/// Remove top delegation, bumps top bottom delegation if exists
776
23
	pub fn rm_top_delegation<T: Config>(
777
23
		&mut self,
778
23
		candidate: &T::AccountId,
779
23
		delegator: T::AccountId,
780
23
	) -> Result<bool, DispatchError>
781
23
	where
782
23
		BalanceOf<T>: Into<Balance> + From<Balance>,
783
23
	{
784
23
		let old_total_counted = self.total_counted;
785
23
		// remove top delegation
786
23
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
787
23
			.expect("CandidateInfo exists => TopDelegations exists");
788
23
		let mut actual_amount_option: Option<BalanceOf<T>> = None;
789
23
		top_delegations.delegations = top_delegations
790
23
			.delegations
791
23
			.clone()
792
23
			.into_iter()
793
25
			.filter(|d| {
794
25
				if d.owner != delegator {
795
2
					true
796
				} else {
797
23
					actual_amount_option = Some(d.amount);
798
23
					false
799
				}
800
25
			})
801
23
			.collect();
802
23
		let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
803
23
		top_delegations.total = top_delegations.total.saturating_sub(actual_amount);
804
		// if bottom nonempty => bump top bottom to top
805
23
		if !matches!(self.bottom_capacity, CapacityStatus::Empty) {
806
			let mut bottom_delegations =
807
				<BottomDelegations<T>>::get(candidate).expect("bottom is nonempty as just checked");
808
			// expect already stored greatest to least by bond amount
809
			let highest_bottom_delegation = bottom_delegations.delegations.remove(0);
810
			bottom_delegations.total = bottom_delegations
811
				.total
812
				.saturating_sub(highest_bottom_delegation.amount);
813
			self.reset_bottom_data::<T>(&bottom_delegations);
814
			<BottomDelegations<T>>::insert(candidate, bottom_delegations);
815
			// insert highest bottom into top delegations
816
			top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation);
817
23
		}
818
		// update candidate info
819
23
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
820
23
		self.delegation_count = self.delegation_count.saturating_sub(1u32);
821
23
		<TopDelegations<T>>::insert(candidate, top_delegations);
822
23
		// return whether total counted changed
823
23
		Ok(old_total_counted == self.total_counted)
824
23
	}
825
	/// Remove bottom delegation
826
	/// Returns if_total_counted_changed: bool
827
	pub fn rm_bottom_delegation<T: Config>(
828
		&mut self,
829
		candidate: &T::AccountId,
830
		delegator: T::AccountId,
831
	) -> Result<bool, DispatchError>
832
	where
833
		BalanceOf<T>: Into<Balance>,
834
	{
835
		// remove bottom delegation
836
		let mut bottom_delegations = <BottomDelegations<T>>::get(candidate)
837
			.expect("CandidateInfo exists => BottomDelegations exists");
838
		let mut actual_amount_option: Option<BalanceOf<T>> = None;
839
		bottom_delegations.delegations = bottom_delegations
840
			.delegations
841
			.clone()
842
			.into_iter()
843
			.filter(|d| {
844
				if d.owner != delegator {
845
					true
846
				} else {
847
					actual_amount_option = Some(d.amount);
848
					false
849
				}
850
			})
851
			.collect();
852
		let actual_amount = actual_amount_option.ok_or(Error::<T>::DelegationDNE)?;
853
		bottom_delegations.total = bottom_delegations.total.saturating_sub(actual_amount);
854
		// update candidate info
855
		self.reset_bottom_data::<T>(&bottom_delegations);
856
		self.delegation_count = self.delegation_count.saturating_sub(1u32);
857
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
858
		Ok(false)
859
	}
860
	/// Increase delegation amount
861
21
	pub fn increase_delegation<T: Config>(
862
21
		&mut self,
863
21
		candidate: &T::AccountId,
864
21
		delegator: T::AccountId,
865
21
		bond: BalanceOf<T>,
866
21
		more: BalanceOf<T>,
867
21
	) -> Result<bool, DispatchError>
868
21
	where
869
21
		BalanceOf<T>: Into<Balance> + From<Balance>,
870
21
	{
871
21
		let lowest_top_eq_highest_bottom =
872
21
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
873
21
		let bond_geq_lowest_top = bond.into() >= self.lowest_top_delegation_amount;
874
21
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
875
21
		if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
876
			// definitely in top
877
12
			self.increase_top_delegation::<T>(candidate, delegator.clone(), more)
878
9
		} else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
879
			// update top but if error then update bottom (because could be in bottom because
880
			// lowest_top_eq_highest_bottom)
881
			let result = self.increase_top_delegation::<T>(candidate, delegator.clone(), more);
882
			if result == Err(delegation_dne_err) {
883
				self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
884
			} else {
885
				result
886
			}
887
		} else {
888
9
			self.increase_bottom_delegation::<T>(candidate, delegator, bond, more)
889
		}
890
21
	}
891
	/// Increase top delegation
892
12
	pub fn increase_top_delegation<T: Config>(
893
12
		&mut self,
894
12
		candidate: &T::AccountId,
895
12
		delegator: T::AccountId,
896
12
		more: BalanceOf<T>,
897
12
	) -> Result<bool, DispatchError>
898
12
	where
899
12
		BalanceOf<T>: Into<Balance> + From<Balance>,
900
12
	{
901
12
		let mut top_delegations = <TopDelegations<T>>::get(candidate)
902
12
			.expect("CandidateInfo exists => TopDelegations exists");
903
12
		let mut in_top = false;
904
12
		top_delegations.delegations = top_delegations
905
12
			.delegations
906
12
			.clone()
907
12
			.into_iter()
908
19
			.map(|d| {
909
19
				if d.owner != delegator {
910
7
					d
911
				} else {
912
12
					in_top = true;
913
12
					let new_amount = d.amount.saturating_add(more);
914
12
					Bond {
915
12
						owner: d.owner,
916
12
						amount: new_amount,
917
12
					}
918
				}
919
19
			})
920
12
			.collect();
921
12
		ensure!(in_top, Error::<T>::DelegationDNE);
922
12
		top_delegations.total = top_delegations.total.saturating_add(more);
923
12
		top_delegations.sort_greatest_to_least();
924
12
		self.reset_top_data::<T>(candidate.clone(), &top_delegations);
925
12
		<TopDelegations<T>>::insert(candidate, top_delegations);
926
12
		Ok(true)
927
12
	}
928
	/// Increase bottom delegation
929
9
	pub fn increase_bottom_delegation<T: Config>(
930
9
		&mut self,
931
9
		candidate: &T::AccountId,
932
9
		delegator: T::AccountId,
933
9
		bond: BalanceOf<T>,
934
9
		more: BalanceOf<T>,
935
9
	) -> Result<bool, DispatchError>
936
9
	where
937
9
		BalanceOf<T>: Into<Balance> + From<Balance>,
938
9
	{
939
9
		let mut bottom_delegations =
940
9
			<BottomDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
941
9
		let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
942
9
		let in_top_after = if (bond.saturating_add(more)).into() > self.lowest_top_delegation_amount
943
		{
944
			// bump it from bottom
945
7
			bottom_delegations.delegations = bottom_delegations
946
7
				.delegations
947
7
				.clone()
948
7
				.into_iter()
949
26
				.filter(|d| {
950
26
					if d.owner != delegator {
951
19
						true
952
					} else {
953
7
						delegation_option = Some(Bond {
954
7
							owner: d.owner.clone(),
955
7
							amount: d.amount.saturating_add(more),
956
7
						});
957
7
						false
958
					}
959
26
				})
960
7
				.collect();
961
7
			let delegation = delegation_option.ok_or(Error::<T>::DelegationDNE)?;
962
7
			bottom_delegations.total = bottom_delegations.total.saturating_sub(bond);
963
7
			// add it to top
964
7
			let mut top_delegations = <TopDelegations<T>>::get(candidate)
965
7
				.expect("CandidateInfo existence => TopDelegations existence");
966
			// if top is full, pop lowest top
967
7
			if matches!(top_delegations.top_capacity::<T>(), CapacityStatus::Full) {
968
7
				// pop lowest top delegation
969
7
				let new_bottom_delegation = top_delegations
970
7
					.delegations
971
7
					.pop()
972
7
					.expect("Top capacity full => Exists at least 1 top delegation");
973
7
				top_delegations.total = top_delegations
974
7
					.total
975
7
					.saturating_sub(new_bottom_delegation.amount);
976
7
				bottom_delegations.insert_sorted_greatest_to_least(new_bottom_delegation);
977
7
			}
978
			// insert into top
979
7
			top_delegations.insert_sorted_greatest_to_least(delegation);
980
7
			self.reset_top_data::<T>(candidate.clone(), &top_delegations);
981
7
			<TopDelegations<T>>::insert(candidate, top_delegations);
982
7
			true
983
		} else {
984
2
			let mut in_bottom = false;
985
2
			// just increase the delegation
986
2
			bottom_delegations.delegations = bottom_delegations
987
2
				.delegations
988
2
				.clone()
989
2
				.into_iter()
990
3
				.map(|d| {
991
3
					if d.owner != delegator {
992
1
						d
993
					} else {
994
2
						in_bottom = true;
995
2
						Bond {
996
2
							owner: d.owner,
997
2
							amount: d.amount.saturating_add(more),
998
2
						}
999
					}
3
				})
2
				.collect();
2
			ensure!(in_bottom, Error::<T>::DelegationDNE);
2
			bottom_delegations.total = bottom_delegations.total.saturating_add(more);
2
			bottom_delegations.sort_greatest_to_least();
2
			false
		};
9
		self.reset_bottom_data::<T>(&bottom_delegations);
9
		<BottomDelegations<T>>::insert(candidate, bottom_delegations);
9
		Ok(in_top_after)
9
	}
	/// Decrease delegation
14
	pub fn decrease_delegation<T: Config>(
14
		&mut self,
14
		candidate: &T::AccountId,
14
		delegator: T::AccountId,
14
		bond: Balance,
14
		less: BalanceOf<T>,
14
	) -> Result<bool, DispatchError>
14
	where
14
		BalanceOf<T>: Into<Balance> + From<Balance>,
14
	{
14
		let lowest_top_eq_highest_bottom =
14
			self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount;
14
		let bond_geq_lowest_top = bond >= self.lowest_top_delegation_amount;
14
		let delegation_dne_err: DispatchError = Error::<T>::DelegationDNE.into();
14
		if bond_geq_lowest_top && !lowest_top_eq_highest_bottom {
			// definitely in top
12
			self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less)
2
		} else if bond_geq_lowest_top && lowest_top_eq_highest_bottom {
			// update top but if error then update bottom (because could be in bottom because
			// lowest_top_eq_highest_bottom)
1
			let result =
1
				self.decrease_top_delegation::<T>(candidate, delegator.clone(), bond.into(), less);
1
			if result == Err(delegation_dne_err) {
				self.decrease_bottom_delegation::<T>(candidate, delegator, less)
			} else {
1
				result
			}
		} else {
1
			self.decrease_bottom_delegation::<T>(candidate, delegator, less)
		}
14
	}
	/// Decrease top delegation
13
	pub fn decrease_top_delegation<T: Config>(
13
		&mut self,
13
		candidate: &T::AccountId,
13
		delegator: T::AccountId,
13
		bond: BalanceOf<T>,
13
		less: BalanceOf<T>,
13
	) -> Result<bool, DispatchError>
13
	where
13
		BalanceOf<T>: Into<Balance> + From<Balance>,
13
	{
13
		// The delegation after the `decrease-delegation` will be strictly less than the
13
		// highest bottom delegation
13
		let bond_after_less_than_highest_bottom =
13
			bond.saturating_sub(less).into() < self.highest_bottom_delegation_amount;
		// The top delegations is full and the bottom delegations has at least one delegation
13
		let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full)
5
			&& !matches!(self.bottom_capacity, CapacityStatus::Empty);
13
		let mut top_delegations =
13
			<TopDelegations<T>>::get(candidate).ok_or(Error::<T>::CandidateDNE)?;
13
		let in_top_after = if bond_after_less_than_highest_bottom && full_top_and_nonempty_bottom {
3
			let mut delegation_option: Option<Bond<T::AccountId, BalanceOf<T>>> = None;
3
			// take delegation from top
3
			top_delegations.delegations = top_delegations
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
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
	}
}
// Temporary manual implementation for migration testing purposes
impl<A: PartialEq, B: PartialEq> PartialEq for CollatorCandidate<A, B> {
	fn eq(&self, other: &Self) -> bool {
		let must_be_true = self.id == other.id
			&& self.bond == other.bond
			&& self.total_counted == other.total_counted
			&& self.total_backing == other.total_backing
			&& self.request == other.request
			&& self.state == other.state;
		if !must_be_true {
			return false;
		}
		for (x, y) in self.delegators.0.iter().zip(other.delegators.0.iter()) {
			if x != y {
				return false;
			}
		}
		for (
			Bond {
				owner: o1,
				amount: a1,
			},
			Bond {
				owner: o2,
				amount: a2,
			},
		) in self
			.top_delegations
			.iter()
			.zip(other.top_delegations.iter())
		{
			if o1 != o2 || a1 != a2 {
				return false;
			}
		}
		for (
			Bond {
				owner: o1,
				amount: a1,
			},
			Bond {
				owner: o2,
				amount: a2,
			},
		) in self
			.bottom_delegations
			.iter()
			.zip(other.bottom_delegations.iter())
		{
			if o1 != o2 || a1 != a2 {
				return false;
			}
		}
		true
	}
}
/// 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)]
pub enum DelegatorAdded<B> {
24
	AddedToTop { new_total: B },
6
	AddedToBottom,
}
impl<
		A: Ord + Clone + sp_std::fmt::Debug,
		B: AtLeast32BitUnsigned
			+ Ord
			+ Copy
			+ sp_std::ops::AddAssign
			+ sp_std::ops::SubAssign
			+ sp_std::fmt::Debug,
	> CollatorCandidate<A, B>
{
	pub fn is_active(&self) -> bool {
		self.state == CollatorStatus::Active
	}
}
impl<A: Clone, B: Copy> From<CollatorCandidate<A, B>> for CollatorSnapshot<A, B> {
	fn from(other: CollatorCandidate<A, B>) -> CollatorSnapshot<A, B> {
		CollatorSnapshot {
			bond: other.bond,
			delegations: other
				.top_delegations
				.into_iter()
				.map(|d| BondWithAutoCompound {
					owner: d.owner,
					amount: d.amount,
					auto_compound: Percent::zero(),
				})
				.collect(),
			total: other.total_counted,
		}
	}
}
#[allow(deprecated)]
126
#[derive(Clone, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum DelegatorStatus {
545
	/// 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 balance locked 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,
		}
	}
271
	pub fn total(&self) -> Balance {
271
		self.total
271
	}
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 locks for this delegator.
	///
	/// This will take the current self.total and ensure that a lock of the same amount is applied
	/// and when increasing the bond lock will also ensure that the account has enough free balance.
	///
	/// `additional_required_balance` should reflect the change to the amount that should be locked if
	/// positive, 0 otherwise (e.g. `min(0, change_in_total_bond)`). This is necessary because it is
	/// not possible to query the amount that is locked for a given lock id.
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
		match additional_required_balance {
610
			BondAdjust::Increase(amount) => {
610
				ensure!(
610
					<Pallet<T>>::get_delegator_stakable_balance(&self.id.clone().into())
610
						>= amount.into(),
					Error::<T>::InsufficientBalance,
				);
				// additional sanity check: shouldn't ever want to lock more than total
610
				if amount > self.total {
					log::warn!("LOGIC ERROR: request to reserve more than bond total");
					return Err(DispatchError::Other("Invalid additional_required_balance"));
610
				}
			}
56
			BondAdjust::Decrease => (), // do nothing on decrease
		};
666
		if self.total.is_zero() {
23
			T::Currency::remove_lock(DELEGATOR_LOCK_ID, &self.id.clone().into());
643
		} else {
643
			T::Currency::set_lock(
643
				DELEGATOR_LOCK_ID,
643
				&self.id.clone().into(),
643
				self.total.into(),
643
				WithdrawReasons::all(),
643
			);
643
		}
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
	}
}
pub mod deprecated {
	#![allow(deprecated)]
	use super::*;
	#[deprecated(note = "use DelegationAction")]
	#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
	/// Changes requested by the delegator
	/// - limit of 1 ongoing change per delegation
	pub enum DelegationChange {
		Revoke,
		Decrease,
	}
	#[deprecated(note = "use ScheduledRequest")]
	#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
	pub struct DelegationRequest<AccountId, Balance> {
		pub collator: AccountId,
		pub amount: Balance,
		pub when_executable: RoundIndex,
		pub action: DelegationChange,
	}
	#[deprecated(note = "use DelegationScheduledRequests storage item")]
	#[derive(Clone, Encode, PartialEq, Decode, RuntimeDebug, TypeInfo)]
	/// Pending requests to mutate delegations for each delegator
	pub struct PendingDelegationRequests<AccountId, Balance> {
		/// Number of pending revocations (necessary for determining whether revoke is exit)
		pub revocations_count: u32,
		/// Map from collator -> Request (enforces at most 1 pending request per delegation)
		pub requests: BTreeMap<AccountId, DelegationRequest<AccountId, Balance>>,
		/// Sum of pending revocation amounts + bond less amounts
		pub less_total: Balance,
	}
	impl<A: Ord, B: Zero> Default for PendingDelegationRequests<A, B> {
		fn default() -> PendingDelegationRequests<A, B> {
			PendingDelegationRequests {
				revocations_count: 0u32,
				requests: BTreeMap::new(),
				less_total: B::zero(),
			}
		}
	}
	impl<
			A: Ord + Clone,
			B: Zero
				+ Ord
				+ Copy
				+ Clone
				+ sp_std::ops::AddAssign
				+ sp_std::ops::Add<Output = B>
				+ sp_std::ops::SubAssign
				+ sp_std::ops::Sub<Output = B>
				+ Saturating,
		> PendingDelegationRequests<A, B>
	{
		/// New default (empty) pending requests
		pub fn new() -> Self {
			Self::default()
		}
	}
	#[deprecated(note = "use new crate::types::Delegator struct")]
	#[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 balance locked for this delegator
		pub total: Balance,
		/// Requests to change delegations, relevant iff active
		pub requests: PendingDelegationRequests<AccountId, Balance>,
		/// Status for this delegator
		pub status: DelegatorStatus,
	}
	// CollatorSnapshot
	#[deprecated(note = "use CollatorSnapshot with BondWithAutoCompound delegations")]
	#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
	/// Snapshot of collator state at the start of the round for which they are selected
	pub struct CollatorSnapshot<AccountId, Balance> {
		/// The total value locked by the collator.
		pub bond: Balance,
		/// The rewardable delegations. This list is a subset of total delegators, where certain
		/// delegators are adjusted based on their scheduled
		/// [DelegationChange::Revoke] or [DelegationChange::Decrease] action.
		pub delegations: Vec<Bond<AccountId, Balance>>,
		/// The total counted value locked for the collator, including the self bond + total staked by
		/// top delegators.
		pub total: Balance,
	}
	impl<A: PartialEq, B: PartialEq> PartialEq for CollatorSnapshot<A, B> {
		fn eq(&self, other: &Self) -> bool {
			let must_be_true = self.bond == other.bond && self.total == other.total;
			if !must_be_true {
				return false;
			}
			for (
				Bond {
					owner: o1,
					amount: a1,
				},
				Bond {
					owner: o2,
					amount: a2,
				},
			) in self.delegations.iter().zip(other.delegations.iter())
			{
				if o1 != o2 || a1 != a2 {
					return false;
				}
			}
			true
		}
	}
	impl<A, B: Default> Default for CollatorSnapshot<A, B> {
		fn default() -> CollatorSnapshot<A, B> {
			CollatorSnapshot {
				bond: B::default(),
				delegations: Vec::new(),
				total: B::default(),
			}
		}
	}
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
/// DEPRECATED in favor of Delegator
/// Nominator state
pub struct Nominator2<AccountId, Balance> {
	/// All current delegations
	pub delegations: OrderedSet<Bond<AccountId, Balance>>,
	/// Delegations scheduled to be revoked
	pub revocations: OrderedSet<AccountId>,
	/// Total balance locked for this nominator
	pub total: Balance,
	/// Total number of revocations scheduled to be executed
	pub scheduled_revocations_count: u32,
	/// Total amount to be unbonded once revocations are executed
	pub scheduled_revocations_total: Balance,
	/// Status for this nominator
	pub status: DelegatorStatus,
}
// /// Temporary function to migrate state
// pub(crate) fn migrate_nominator_to_delegator_state<T: Config>(
// 	id: T::AccountId,
// 	nominator: Nominator2<T::AccountId, BalanceOf<T>>,
// ) -> Delegator<T::AccountId, BalanceOf<T>> {
// 	Delegator {
// 		id,
// 		delegations: nominator.delegations,
// 		total: nominator.total,
// 		requests: PendingDelegationRequests::new(),
// 		status: nominator.status,
// 	}
// }
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>
{
541
	pub fn new(current: RoundIndex, first: B, length: u32, first_slot: u64) -> RoundInfo<B> {
541
		RoundInfo {
541
			current,
541
			first,
541
			length,
541
			first_slot,
541
		}
541
	}
	/// Check if the round should be updated
30751
	pub fn should_update(&self, now: B) -> bool {
30751
		now - self.first >= self.length.into()
30751
	}
	/// New round
261
	pub fn update(&mut self, now: B, now_slot: u64) {
261
		self.current = self.current.saturating_add(1u32);
261
		self.first = now;
261
		self.first_slot = now_slot;
261
	}
}
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)]
pub struct InflationDistributionConfig<AccountId>(
	pub(crate) [InflationDistributionAccount<AccountId>; 2],
);
impl<AccountId> From<[InflationDistributionAccount<AccountId>; 2]>
	for InflationDistributionConfig<AccountId>
{
639
	fn from(configs: [InflationDistributionAccount<AccountId>; 2]) -> Self {
639
		InflationDistributionConfig(configs)
639
	}
}
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)]
/// 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> {
	fn default() -> InflationDistributionAccount<A> {
		InflationDistributionAccount {
			account: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
				.expect("infinite length input; no invalid inputs for type; qed"),
			percent: Percent::zero(),
		}
	}
}
pub enum BondAdjust<Balance> {
	Increase(Balance),
	Decrease,
}