Skip to content

Commit 37c5e98

Browse files
committed
More tests & docs
1 parent f51febe commit 37c5e98

File tree

2 files changed

+60
-20
lines changed

2 files changed

+60
-20
lines changed

pallets/dapp-staking-v3/src/test/tests_types.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn period_info_basic_checks() {
5757
assert_eq!(info.subperiod, Subperiod::Voting);
5858
assert_eq!(info.next_subperiod_start_era, next_subperiod_start_era);
5959

60-
// Voting period checks
60+
// Voting subperiod checks
6161
assert!(!info.is_next_period(next_subperiod_start_era - 1));
6262
assert!(!info.is_next_period(next_subperiod_start_era));
6363
assert!(!info.is_next_period(next_subperiod_start_era + 1));
@@ -68,7 +68,7 @@ fn period_info_basic_checks() {
6868
] {
6969
assert!(
7070
!info.is_next_period(era),
71-
"Cannot trigger 'true' in the Voting period type."
71+
"Cannot trigger 'true' in the Voting subperiod type."
7272
);
7373
}
7474

@@ -520,7 +520,7 @@ fn account_ledger_add_stake_amount_basic_example_with_different_subperiods_works
520520
assert!(acc_ledger.staked.is_empty());
521521
assert!(acc_ledger.staked_future.is_none());
522522

523-
// 1st scenario - stake some amount in Voting period, and ensure values are as expected.
523+
// 1st scenario - stake some amount in Voting subperiod, and ensure values are as expected.
524524
let era_1 = 1;
525525
let period_1 = 1;
526526
let period_info_1 = PeriodInfo {
@@ -1775,7 +1775,7 @@ fn era_info_stake_works() {
17751775
// Sanity check
17761776
assert!(era_info.total_locked.is_zero());
17771777

1778-
// Add some voting period stake
1778+
// Add some voting subperiod stake
17791779
let vp_stake_amount = 7;
17801780
era_info.add_stake_amount(vp_stake_amount, Subperiod::Voting);
17811781
assert_eq!(era_info.total_staked_amount_next_era(), vp_stake_amount);
@@ -2080,7 +2080,7 @@ fn singular_staking_info_unstake_during_voting_is_ok() {
20802080
let vote_stake_amount_1 = 11;
20812081
staking_info.stake(vote_stake_amount_1, era_1, Subperiod::Voting);
20822082

2083-
// Unstake some amount during `Voting` period, loyalty should remain as expected.
2083+
// 1. Unstake some amount during `Voting` period, loyalty should remain as expected.
20842084
let unstake_amount_1 = 5;
20852085
assert_eq!(
20862086
staking_info.unstake(unstake_amount_1, era_1, Subperiod::Voting),
@@ -2097,7 +2097,7 @@ fn singular_staking_info_unstake_during_voting_is_ok() {
20972097
"Stake era should remain valid."
20982098
);
20992099

2100-
// Fully unstake, attempting to underflow, and ensure loyalty flag has been removed.
2100+
// 2. Fully unstake, attempting to underflow, and ensure loyalty flag has been removed.
21012101
let era_2 = era_1 + 2;
21022102
let remaining_stake = staking_info.total_staked_amount();
21032103
assert_eq!(
@@ -2151,8 +2151,21 @@ fn singular_staking_info_unstake_during_bep_is_ok() {
21512151
"Stake era should remain valid."
21522152
);
21532153

2154-
// 2nd scenario - unstake all of the amount staked during B&E period, and then some more.
2155-
// The point is to take a chunk from the voting period stake too.
2154+
// 2nd scenario - Ensure that staked amount is larger than the previous stake amount, and then
2155+
// unstake enough to result in some overflow of the stake delta.
2156+
let bep_stake_amount_2 = 13;
2157+
staking_info.stake(bep_stake_amount_2, era_1, Subperiod::BuildAndEarn);
2158+
let delta = staking_info.staked.total() - staking_info.previous_staked.total();
2159+
let overflow = 1;
2160+
let unstake_2 = delta + overflow;
2161+
2162+
assert_eq!(
2163+
staking_info.unstake(unstake_2, era_1, Subperiod::BuildAndEarn),
2164+
vec![(era_1, overflow), (era_1 + 1, unstake_2)]
2165+
);
2166+
2167+
// 3rd scenario - unstake all of the amount staked during B&E period, and then some more.
2168+
// The point is to take a chunk from the voting subperiod stake too.
21562169
let current_total_stake = staking_info.total_staked_amount();
21572170
let current_bep_stake = staking_info.staked_amount(Subperiod::BuildAndEarn);
21582171
let voting_stake_overflow = 2;
@@ -2161,7 +2174,7 @@ fn singular_staking_info_unstake_during_bep_is_ok() {
21612174

21622175
assert_eq!(
21632176
staking_info.unstake(unstake_2, era_2, Subperiod::BuildAndEarn),
2164-
// Both amounts are the same since current stake is less than the previous one
2177+
// Both amounts are the same since previous staked era is less than last staked era - 1
21652178
vec![(era_2 - 1, unstake_2), (era_2, unstake_2)]
21662179
);
21672180
assert_eq!(
@@ -2177,7 +2190,7 @@ fn singular_staking_info_unstake_during_bep_is_ok() {
21772190
.is_zero());
21782191
assert!(
21792192
!staking_info.is_loyal(),
2180-
"Loyalty flag should have been removed due to non-zero voting period unstake"
2193+
"Loyalty flag should have been removed due to non-zero voting subperiod unstake"
21812194
);
21822195
assert_eq!(staking_info.era(), era_2);
21832196
}

pallets/dapp-staking-v3/src/types.rs

+37-10
Original file line numberDiff line numberDiff line change
@@ -1015,40 +1015,67 @@ impl SingularStakingInfo {
10151015
///
10161016
/// Returns a vector of `(era, amount)` pairs, where `era` is the era in which the unstake happened,
10171017
/// and the amount is the corresponding amount.
1018+
///
1019+
/// ### NOTE
1020+
/// `SingularStakingInfo` always aims to keep track of the staked amount between two consecutive eras.
1021+
/// This means that the returned value will at most cover two eras - the last staked era, and the one before it.
1022+
///
1023+
/// Last staked era can be the current era, or the era after.
1024+
/// If after the unstake operation, delta between previous & current era is larger than 1, previous staked entry is ignored.
1025+
///
1026+
/// #### Example (simplified)
1027+
///
1028+
/// 1. previous_staked: (era: 4, amount: 50), staked: (era: 5, amount: 100)
1029+
/// 2. unstake 30 in era 6 is called
1030+
/// 3. Return value is: (era: 5, amount: 30), (era: 6, amount: 30)
1031+
/// 4. previous_staked: (era: 5, amount: 70), (era: 6, amount: 70)
1032+
///
1033+
/// In case same example is repeated, but unstake is done in era 5:
1034+
///
1035+
/// 1. previous_staked: (era: 4, amount: 50), staked: (era: 5, amount: 100)
1036+
/// 2. unstake 30 in era 5 is called
1037+
/// 3. Return value is: (era: 5, amount: 30)
1038+
/// 4. previous_staked: (era: 4, amount: 50), (era: 5, amount: 70)
1039+
///
10181040
pub fn unstake(
10191041
&mut self,
10201042
amount: Balance,
10211043
current_era: EraNumber,
10221044
subperiod: Subperiod,
10231045
) -> Vec<(EraNumber, Balance)> {
10241046
let mut result = Vec::new();
1025-
1026-
// 0. Prepare values for further calculations
1027-
let snapshot = self.staked;
1028-
let stake_delta = self
1029-
.staked
1030-
.total()
1031-
.saturating_sub(self.previous_staked.total());
1047+
let staked_snapshot = self.staked;
10321048

10331049
// 1. Modify current staked amount
10341050
self.staked.subtract(amount);
1035-
let unstaked_amount = snapshot.total().saturating_sub(self.staked.total());
1051+
let unstaked_amount = staked_snapshot.total().saturating_sub(self.staked.total());
10361052
self.staked.era = self.staked.era.max(current_era);
10371053
result.push((self.staked.era, unstaked_amount));
10381054

1055+
// 2. Calculate previous stake delta.
1056+
// In case new stake era is exactly one era after the previous stake era, we can calculate this as a delta.
1057+
// Otherwise, if it's further far away in the past, the previous era stake is equal to the snapshot.
1058+
let stake_delta = if self.staked.era == self.previous_staked.era.saturating_add(1) {
1059+
staked_snapshot
1060+
.total()
1061+
.saturating_sub(self.previous_staked.total())
1062+
} else {
1063+
Balance::zero()
1064+
};
1065+
10391066
// 2. Update loyal staker flag accordingly
10401067
self.loyal_staker = self.loyal_staker
10411068
&& match subperiod {
10421069
Subperiod::Voting => !self.staked.voting.is_zero(),
1043-
Subperiod::BuildAndEarn => self.staked.voting == snapshot.voting,
1070+
Subperiod::BuildAndEarn => self.staked.voting == staked_snapshot.voting,
10441071
};
10451072

10461073
// 3. Move over the snapshot to the previous snapshot field and make sure
10471074
// to update era in case something was staked before.
10481075
if stake_delta.is_zero() {
10491076
self.previous_staked = Default::default();
10501077
} else {
1051-
self.previous_staked = snapshot;
1078+
self.previous_staked = staked_snapshot;
10521079
self.previous_staked.era = self.staked.era.saturating_sub(1);
10531080
}
10541081

0 commit comments

Comments
 (0)