@@ -1002,6 +1002,9 @@ impl SingularStakingInfo {
1002
1002
// Keep the previous stake amount for future reference
1003
1003
self . previous_staked = self . staked ;
1004
1004
self . previous_staked . era = current_era;
1005
+ if self . previous_staked . total ( ) . is_zero ( ) {
1006
+ self . previous_staked = Default :: default ( ) ;
1007
+ }
1005
1008
1006
1009
// Stake is only valid from the next era so we keep it consistent here
1007
1010
self . staked . add ( amount, subperiod) ;
@@ -1021,22 +1024,6 @@ impl SingularStakingInfo {
1021
1024
/// This means that the returned value will at most cover two eras - the last staked era, and the one before it.
1022
1025
///
1023
1026
/// 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
- ///
1040
1027
pub fn unstake (
1041
1028
& mut self ,
1042
1029
amount : Balance ,
@@ -1046,51 +1033,64 @@ impl SingularStakingInfo {
1046
1033
let mut result = Vec :: new ( ) ;
1047
1034
let staked_snapshot = self . staked ;
1048
1035
1049
- // 1. Modify current staked amount
1036
+ // 1. Modify current staked amount, and update the result.
1050
1037
self . staked . subtract ( amount) ;
1051
1038
let unstaked_amount = staked_snapshot. total ( ) . saturating_sub ( self . staked . total ( ) ) ;
1052
1039
self . staked . era = self . staked . era . max ( current_era) ;
1053
1040
result. push ( ( self . staked . era , unstaked_amount) ) ;
1054
1041
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
-
1066
- // 2. Update loyal staker flag accordingly
1042
+ // 2. Update loyal staker flag accordingly.
1067
1043
self . loyal_staker = self . loyal_staker
1068
1044
&& match subperiod {
1069
1045
Subperiod :: Voting => !self . staked . voting . is_zero ( ) ,
1070
1046
Subperiod :: BuildAndEarn => self . staked . voting == staked_snapshot. voting ,
1071
1047
} ;
1072
1048
1073
- // 3. Move over the snapshot to the previous snapshot field and make sure
1074
- // to update era in case something was staked before.
1075
- if stake_delta. is_zero ( ) {
1076
- self . previous_staked = Default :: default ( ) ;
1049
+ // 3. Determine what was the previous staked amount.
1050
+ // This is done by simply comparing where does the _previous era_ fit in the current context.
1051
+ let previous_era = self . staked . era . saturating_sub ( 1 ) ;
1052
+
1053
+ self . previous_staked = if staked_snapshot. era <= previous_era {
1054
+ let mut previous_staked = staked_snapshot;
1055
+ previous_staked. era = previous_era;
1056
+ previous_staked
1057
+ } else if !self . previous_staked . is_empty ( ) && self . previous_staked . era <= previous_era {
1058
+ let mut previous_staked = self . previous_staked ;
1059
+ previous_staked. era = previous_era;
1060
+ previous_staked
1077
1061
} else {
1078
- self . previous_staked = staked_snapshot;
1079
- self . previous_staked . era = self . staked . era . saturating_sub ( 1 ) ;
1080
- }
1062
+ Default :: default ( )
1063
+ } ;
1081
1064
1082
- // 4. If something was unstaked from the previous era, add it to the result
1083
- if unstaked_amount > stake_delta {
1084
- // Doesn't need to be at 0-th index, but we still add it for clarity
1085
- result. insert (
1086
- 0 ,
1087
- (
1088
- self . staked . era . saturating_sub ( 1 ) ,
1089
- // The diff between unstake amount & stake delta tells us how much was
1090
- // actually unstaked from the previous era.
1091
- unstaked_amount. saturating_sub ( stake_delta) ,
1092
- ) ,
1093
- )
1065
+ // 4. Calculate how much is being unstaked from the previous staked era entry, in case its era equals the current era.
1066
+ //
1067
+ // Simples way to explain this is via an example.
1068
+ // Let's assume a simplification where stake amount entries are in `(era, amount)` format.
1069
+ //
1070
+ // 1. Values: previous_staked: **(2, 10)**, staked: **(3, 15)**
1071
+ // 2. User calls unstake during **era 2**, and unstakes amount **6**.
1072
+ // Clearly some amount was staked during era 2, which resulted in era 3 stake being increased by 5.
1073
+ // Calling unstake immediately in the same era should not necessarily reduce current era stake amount.
1074
+ // This should be allowed to happen only if the unstaked amount is larger than the difference between the staked amount of two eras.
1075
+ // 3. Values: previous_staked: **(2, 9)**, staked: **(3, 9)**
1076
+ //
1077
+ // An alternative scenario, where user calls unstake during **era 2**, and unstakes amount **4**.
1078
+ // 3. Values: previous_staked: **(2, 10)**, staked: **(3, 11)**
1079
+ //
1080
+ // Note that the unstake operation didn't chip away from the current era, only the next one.
1081
+ if self . previous_staked . era == current_era {
1082
+ let maybe_stake_delta = staked_snapshot
1083
+ . total ( )
1084
+ . checked_sub ( self . previous_staked . total ( ) ) ;
1085
+ match maybe_stake_delta {
1086
+ Some ( stake_delta) if unstaked_amount > stake_delta => {
1087
+ let overflow_amount = unstaked_amount - stake_delta;
1088
+ self . previous_staked . subtract ( overflow_amount) ;
1089
+
1090
+ result. insert ( 0 , ( self . previous_staked . era , overflow_amount) ) ;
1091
+ }
1092
+ _ => { }
1093
+ }
1094
1094
}
1095
1095
1096
1096
result
0 commit comments