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::{Saturating, Zero},
31
	Perbill, Percent, RuntimeDebug,
32
};
33
use sp_std::{cmp::Ordering, 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
4445
	fn cmp(&self, other: &Self) -> Ordering {
69
4445
		self.owner.cmp(&other.owner)
70
4445
	}
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
180
#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
86
/// The activity status of the collator
87
pub enum CollatorStatus {
88
10208
	/// Committed to be online and producing valid blocks (not equivocating)
89
	Active,
90
7
	/// Temporarily inactive and excused for inactivity
91
	Idle,
92
	/// 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
40
#[derive(PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
185
/// Request scheduled to change the collator candidate self-bond
186
pub struct CandidateBondLessRequest<Balance> {
187
	pub amount: Balance,
188
	pub when_executable: RoundIndex,
189
}
190

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

            
198
impl<A, B: Default> Default for Delegations<A, B> {
199
648
	fn default() -> Delegations<A, B> {
200
648
		Delegations {
201
648
			delegations: Vec::new(),
202
648
			total: B::default(),
203
648
		}
204
648
	}
205
}
206

            
207
impl<AccountId, Balance: Copy + Ord + sp_std::ops::AddAssign + Zero + Saturating>
208
	Delegations<AccountId, Balance>
209
{
210
25
	pub fn sort_greatest_to_least(&mut self) {
211
25
		self.delegations.sort_by(|a, b| b.amount.cmp(&a.amount));
212
25
	}
213
	/// Insert sorted greatest to least and increase .total accordingly
214
	/// Insertion respects first come first serve so new delegations are pushed after existing
215
	/// delegations if the amount is the same
216
626
	pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond<AccountId, Balance>) {
217
626
		self.total = self.total.saturating_add(delegation.amount);
218
626
		// if delegations nonempty && last_element == delegation.amount => push input and return
219
626
		if !self.delegations.is_empty() {
220
			// if last_element == delegation.amount => push the delegation and return early
221
331
			if self.delegations[self.delegations.len() - 1].amount == delegation.amount {
222
260
				self.delegations.push(delegation);
223
260
				// early return
224
260
				return;
225
71
			}
226
295
		}
227
		// else binary search insertion
228
366
		match self
229
366
			.delegations
230
366
			.binary_search_by(|x| delegation.amount.cmp(&x.amount))
231
		{
232
			// sorted insertion on sorted vec
233
			// enforces first come first serve for equal bond amounts
234
11
			Ok(i) => {
235
11
				let mut new_index = i + 1;
236
11
				while new_index <= (self.delegations.len() - 1) {
237
11
					if self.delegations[new_index].amount == delegation.amount {
238
						new_index = new_index.saturating_add(1);
239
					} else {
240
11
						self.delegations.insert(new_index, delegation);
241
11
						return;
242
					}
243
				}
244
				self.delegations.push(delegation)
245
			}
246
355
			Err(i) => self.delegations.insert(i, delegation),
247
		}
248
626
	}
249
	/// Return the capacity status for top delegations
250
495
	pub fn top_capacity<T: Config>(&self) -> CapacityStatus {
251
495
		match &self.delegations {
252
495
			x if x.len() as u32 >= T::MaxTopDelegationsPerCandidate::get() => CapacityStatus::Full,
253
407
			x if x.is_empty() => CapacityStatus::Empty,
254
385
			_ => CapacityStatus::Partial,
255
		}
256
495
	}
257
	/// Return the capacity status for bottom delegations
258
186
	pub fn bottom_capacity<T: Config>(&self) -> CapacityStatus {
259
186
		match &self.delegations {
260
186
			x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => {
261
52
				CapacityStatus::Full
262
			}
263
134
			x if x.is_empty() => CapacityStatus::Empty,
264
134
			_ => CapacityStatus::Partial,
265
		}
266
186
	}
267
	/// Return last delegation amount without popping the delegation
268
674
	pub fn lowest_delegation_amount(&self) -> Balance {
269
674
		self.delegations
270
674
			.last()
271
674
			.map(|x| x.amount)
272
674
			.unwrap_or(Balance::zero())
273
674
	}
274
	/// Return highest delegation amount
275
186
	pub fn highest_delegation_amount(&self) -> Balance {
276
186
		self.delegations
277
186
			.first()
278
186
			.map(|x| x.amount)
279
186
			.unwrap_or(Balance::zero())
280
186
	}
281
}
282

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

            
294
200
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
295
/// All candidate info except the top and bottom delegations
296
pub struct CandidateMetadata<Balance> {
297
	/// This candidate's self bond amount
298
	pub bond: Balance,
299
	/// Total number of delegations to this candidate
300
	pub delegation_count: u32,
301
	/// Self bond + sum of top delegations
302
	pub total_counted: Balance,
303
	/// The smallest top delegation amount
304
	pub lowest_top_delegation_amount: Balance,
305
	/// The highest bottom delegation amount
306
	pub highest_bottom_delegation_amount: Balance,
307
	/// The smallest bottom delegation amount
308
	pub lowest_bottom_delegation_amount: Balance,
309
	/// Capacity status for top delegations
310
	pub top_capacity: CapacityStatus,
311
	/// Capacity status for bottom delegations
312
	pub bottom_capacity: CapacityStatus,
313
	/// Maximum 1 pending request to decrease candidate self bond at any given time
314
	pub request: Option<CandidateBondLessRequest<Balance>>,
315
	/// Current status of the collator
316
	pub status: CollatorStatus,
317
}
318

            
319
impl<
320
		Balance: Copy
321
			+ Zero
322
			+ PartialOrd
323
			+ sp_std::ops::AddAssign
324
			+ sp_std::ops::SubAssign
325
			+ sp_std::ops::Sub<Output = Balance>
326
			+ sp_std::fmt::Debug
327
			+ Saturating,
328
	> CandidateMetadata<Balance>
329
{
330
648
	pub fn new(bond: Balance) -> Self {
331
648
		CandidateMetadata {
332
648
			bond,
333
648
			delegation_count: 0u32,
334
648
			total_counted: bond,
335
648
			lowest_top_delegation_amount: Zero::zero(),
336
648
			highest_bottom_delegation_amount: Zero::zero(),
337
648
			lowest_bottom_delegation_amount: Zero::zero(),
338
648
			top_capacity: CapacityStatus::Empty,
339
648
			bottom_capacity: CapacityStatus::Empty,
340
648
			request: None,
341
648
			status: CollatorStatus::Active,
342
648
		}
343
648
	}
344
542
	pub fn is_active(&self) -> bool {
345
542
		matches!(self.status, CollatorStatus::Active)
346
542
	}
347
57
	pub fn is_leaving(&self) -> bool {
348
57
		matches!(self.status, CollatorStatus::Leaving(_))
349
57
	}
350
46
	pub fn schedule_leave<T: Config>(&mut self) -> Result<(RoundIndex, RoundIndex), DispatchError> {
351
46
		ensure!(!self.is_leaving(), Error::<T>::CandidateAlreadyLeaving);
352
45
		let now = <Round<T>>::get().current;
353
45
		let when = now + T::LeaveCandidatesDelay::get();
354
45
		self.status = CollatorStatus::Leaving(when);
355
45
		Ok((now, when))
356
46
	}
357
23
	pub fn can_leave<T: Config>(&self) -> DispatchResult {
358
23
		if let CollatorStatus::Leaving(when) = self.status {
359
23
			ensure!(
360
23
				<Round<T>>::get().current >= when,
361
2
				Error::<T>::CandidateCannotLeaveYet
362
			);
363
21
			Ok(())
364
		} else {
365
			Err(Error::<T>::CandidateNotLeaving.into())
366
		}
367
23
	}
368
14
	pub fn go_offline(&mut self) {
369
14
		self.status = CollatorStatus::Idle;
370
14
	}
371
8
	pub fn go_online(&mut self) {
372
8
		self.status = CollatorStatus::Active;
373
8
	}
374
6
	pub fn bond_more<T: Config>(&mut self, who: T::AccountId, more: Balance) -> DispatchResult
375
6
	where
376
6
		BalanceOf<T>: From<Balance>,
377
6
	{
378
6
		ensure!(
379
6
			<Pallet<T>>::get_collator_stakable_free_balance(&who) >= more.into(),
380
			Error::<T>::InsufficientBalance
381
		);
382
6
		let new_total = <Total<T>>::get().saturating_add(more.into());
383
6
		<Total<T>>::put(new_total);
384
6
		self.bond = self.bond.saturating_add(more);
385
6
		T::Currency::set_lock(
386
6
			COLLATOR_LOCK_ID,
387
6
			&who.clone(),
388
6
			self.bond.into(),
389
6
			WithdrawReasons::all(),
390
6
		);
391
6
		self.total_counted = self.total_counted.saturating_add(more);
392
6
		<Pallet<T>>::deposit_event(Event::CandidateBondedMore {
393
6
			candidate: who.clone(),
394
6
			amount: more.into(),
395
6
			new_total_bond: self.bond.into(),
396
6
		});
397
6
		Ok(())
398
6
	}
399

            
400
6
	pub fn bond_less<T: Config>(&mut self, who: T::AccountId, amount: Balance)
401
6
	where
402
6
		BalanceOf<T>: From<Balance>,
403
6
	{
404
6
		let new_total_staked = <Total<T>>::get().saturating_sub(amount.into());
405
6
		<Total<T>>::put(new_total_staked);
406
6
		self.bond = self.bond.saturating_sub(amount);
407
6
		if self.bond.is_zero() {
408
			T::Currency::remove_lock(COLLATOR_LOCK_ID, &who);
409
6
		} else {
410
6
			T::Currency::set_lock(
411
6
				COLLATOR_LOCK_ID,
412
6
				&who,
413
6
				self.bond.into(),
414
6
				WithdrawReasons::all(),
415
6
			);
416
6
		}
417
6
		self.total_counted = self.total_counted.saturating_sub(amount);
418
6
		let event = Event::CandidateBondedLess {
419
6
			candidate: who.clone(),
420
6
			amount: amount.into(),
421
6
			new_bond: self.bond.into(),
422
6
		};
423
6
		// update candidate pool value because it must change if self bond changes
424
6
		if self.is_active() {
425
6
			Pallet::<T>::update_active(who, self.total_counted.into());
426
6
		}
427
6
		Pallet::<T>::deposit_event(event);
428
6
	}
429

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

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

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