@@ -398,6 +398,7 @@ contract SFCLib is SFCBase {
398
398
delete getLockupInfo[delegator][toValidatorID];
399
399
delete getStashedLockupRewards[delegator][toValidatorID];
400
400
}
401
+ _truncateLegacyPenalty (delegator, toValidatorID);
401
402
return nonStashedReward.lockupBaseReward != 0 || nonStashedReward.lockupExtraReward != 0 || nonStashedReward.unlockedReward != 0 ;
402
403
}
403
404
@@ -484,7 +485,6 @@ contract SFCLib is SFCBase {
484
485
// stash the previous penalty and clean getStashedLockupRewards
485
486
LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
486
487
if (relock) {
487
- _truncateLegacyNonStashedPenalty (delegator, toValidatorID, ld.lockedStake, ld.duration);
488
488
Penalty[] storage penalties = getStashedPenalties[delegator][toValidatorID];
489
489
490
490
uint256 penalty = _popNonStashedUnlockPenalty (delegator, toValidatorID, ld.lockedStake, ld.lockedStake);
@@ -519,20 +519,6 @@ contract SFCLib is SFCBase {
519
519
_lockStake (delegator, toValidatorID, lockupDuration, amount, true );
520
520
}
521
521
522
- function _truncateLegacyNonStashedPenalty (address delegator , uint256 toValidatorID , uint256 lockupAmount , uint256 duration ) internal {
523
- Rewards storage r = getStashedLockupRewards[delegator][toValidatorID];
524
- { // this block of code can be removed after a year from implementing multi penalty
525
- uint256 avgFullReward = lockupAmount.mul (2219685438 ).mul (duration).div (1e18 ); // 0.000000002219685438 is reward per second per wei at 7% APR
526
- Rewards memory avgReward = _scaleLockupReward (avgFullReward, duration);
527
- uint256 maxReasonablePenalty = avgReward.lockupBaseReward / 2 + avgReward.lockupExtraReward;
528
- uint256 storedPenalty = r.lockupExtraReward + r.lockupBaseReward / 2 ;
529
- if (storedPenalty > 0 && storedPenalty > maxReasonablePenalty) {
530
- r.lockupExtraReward = r.lockupExtraReward.mul (maxReasonablePenalty).div (storedPenalty);
531
- r.lockupBaseReward = r.lockupBaseReward.mul (maxReasonablePenalty).div (storedPenalty);
532
- }
533
- }
534
- }
535
-
536
522
function _popNonStashedUnlockPenalty (address delegator , uint256 toValidatorID , uint256 unlockAmount , uint256 totalAmount ) internal returns (uint256 ) {
537
523
Rewards storage r = getStashedLockupRewards[delegator][toValidatorID];
538
524
uint256 lockupExtraRewardShare = r.lockupExtraReward.mul (unlockAmount).div (totalAmount);
@@ -572,7 +558,6 @@ contract SFCLib is SFCBase {
572
558
573
559
_stashRewards (delegator, toValidatorID);
574
560
575
- _truncateLegacyNonStashedPenalty (delegator, toValidatorID, ld.lockedStake, ld.duration);
576
561
uint256 penalty = _popWholeUnlockPenalty (delegator, toValidatorID, amount, ld.lockedStake);
577
562
if (penalty > amount) {
578
563
penalty = amount;
@@ -605,4 +590,100 @@ contract SFCLib is SFCBase {
605
590
}
606
591
}
607
592
}
593
+
594
+ // code below can be erased after 1 year since deployment of multipenalties
595
+
596
+ function _getAvgEpochStep (uint256 duration ) internal view returns (uint256 ) {
597
+ // estimate number of epochs such that we would make approximately 15 iterations
598
+ uint256 tryEpochs = currentSealedEpoch / 5 ;
599
+ if (tryEpochs > 10000 ) {
600
+ tryEpochs = 10000 ;
601
+ }
602
+ uint256 tryEndTime = getEpochSnapshot[currentSealedEpoch - tryEpochs].endTime;
603
+ if (tryEndTime == 0 || tryEpochs == 0 ) {
604
+ return 0 ;
605
+ }
606
+ uint256 secondsPerEpoch = _now ().sub (tryEndTime) / tryEpochs;
607
+ return duration / (secondsPerEpoch * 15 + 1 );
608
+ }
609
+
610
+ function _getAvgReceivedStake (uint256 validatorID , uint256 duration , uint256 step ) internal view returns (uint256 ) {
611
+ uint256 receivedStakeSum = getValidator[validatorID].receivedStake;
612
+ uint256 samples = 1 ;
613
+
614
+ uint256 until = _now ().sub (duration);
615
+ for (uint256 i = 1 ; i <= 30 ; i++ ) {
616
+ uint256 e = currentSealedEpoch - i * step;
617
+ EpochSnapshot storage s = getEpochSnapshot[e];
618
+ if (s.endTime < until) {
619
+ break ;
620
+ }
621
+ uint256 sample = s.receivedStake[validatorID];
622
+ if (sample != 0 ) {
623
+ samples++ ;
624
+ receivedStakeSum += sample;
625
+ }
626
+ }
627
+ return receivedStakeSum / samples;
628
+ }
629
+
630
+ function _getAvgUptime (uint256 validatorID , uint256 duration , uint256 step ) internal view returns (uint256 ) {
631
+ uint256 until = _now ().sub (duration);
632
+ uint256 oldUptimeCounter = 0 ;
633
+ uint256 newUptimeCounter = 0 ;
634
+ for (uint256 i = 0 ; i <= 30 ; i++ ) {
635
+ uint256 e = currentSealedEpoch - i * step;
636
+ EpochSnapshot storage s = getEpochSnapshot[e];
637
+ uint256 endTime = s.endTime;
638
+ if (endTime < until) {
639
+ if (i <= 2 ) {
640
+ return duration;
641
+ }
642
+ break ;
643
+ }
644
+ uint256 uptimeCounter = s.accumulatedUptime[validatorID];
645
+ if (uptimeCounter != 0 ) {
646
+ oldUptimeCounter = uptimeCounter;
647
+ if (newUptimeCounter == 0 ) {
648
+ newUptimeCounter = uptimeCounter;
649
+ }
650
+ }
651
+ }
652
+ uint256 uptime = newUptimeCounter - oldUptimeCounter;
653
+ if (uptime > duration* 4 / 5 ) {
654
+ return duration;
655
+ }
656
+ return uptime;
657
+ }
658
+
659
+ function _truncateLegacyPenalty (address delegator , uint256 toValidatorID ) internal {
660
+ Rewards storage r = getStashedLockupRewards[delegator][toValidatorID];
661
+ uint256 storedPenalty = r.lockupExtraReward + r.lockupBaseReward / 2 ;
662
+ if (storedPenalty == 0 ) {
663
+ return ;
664
+ }
665
+ LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
666
+ uint256 duration = ld.duration;
667
+ uint256 lockedStake = ld.lockedStake;
668
+ uint256 step = _getAvgEpochStep (duration);
669
+ if (step == 0 ) {
670
+ return ;
671
+ }
672
+ uint256 RPS = _getAvgUptime (toValidatorID, duration, step).mul (2092846271 ).div (duration); // corresponds to 6.6% APR
673
+ uint256 selfStake = getStake[delegator][toValidatorID];
674
+
675
+ uint256 avgFullReward = selfStake.mul (RPS).mul (duration).div (1e18 ).mul (Decimal.unit ().sub (c.validatorCommission ())).div (Decimal.unit ()); // reward for self-stake
676
+ if (getValidator[toValidatorID].auth == delegator) { // reward for received portion of stake
677
+ uint256 receivedStakeAvg = _getAvgReceivedStake (toValidatorID, duration, step).mul (11 ).div (10 );
678
+ avgFullReward += receivedStakeAvg.mul (RPS).mul (duration).div (1e18 ).mul (c.validatorCommission ()).div (Decimal.unit ());
679
+ }
680
+ avgFullReward = avgFullReward.mul (lockedStake).div (selfStake);
681
+ Rewards memory avgReward = _scaleLockupReward (avgFullReward, duration);
682
+ uint256 maxReasonablePenalty = avgReward.lockupBaseReward / 2 + avgReward.lockupExtraReward;
683
+ maxReasonablePenalty = maxReasonablePenalty;
684
+ if (storedPenalty > maxReasonablePenalty) {
685
+ r.lockupExtraReward = r.lockupExtraReward.mul (maxReasonablePenalty).div (storedPenalty);
686
+ r.lockupBaseReward = r.lockupBaseReward.mul (maxReasonablePenalty).div (storedPenalty);
687
+ }
688
+ }
608
689
}
0 commit comments