@@ -23,7 +23,13 @@ import {IGovernance} from "../../common/governance/IGovernance.sol";
2323import {Initializable} from "../../common/mixin/Initializable.sol " ;
2424import {ValidatorAuction} from "./ValidatorAuction.sol " ;
2525
26- contract StakeManager is StakeManagerStorage , Initializable , IStakeManager , DelegateProxyForwarder , StakeManagerStorageExtension {
26+ contract StakeManager is
27+ StakeManagerStorage ,
28+ Initializable ,
29+ IStakeManager ,
30+ DelegateProxyForwarder ,
31+ StakeManagerStorageExtension
32+ {
2733 using SafeMath for uint256 ;
2834 using Merkle for bytes32 ;
2935 using RLPReader for bytes ;
@@ -105,7 +111,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
105111 Public View Methods
106112 */
107113
108- function getRegistry () public view returns (address ) {
114+ function getRegistry () public view returns (address ) {
109115 return registry;
110116 }
111117
@@ -213,12 +219,22 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
213219 CHECKPOINT_REWARD = newReward;
214220 }
215221
222+ function updateCheckpointRewardParams (
223+ uint256 _rewardDecreasePerCheckpoint ,
224+ uint256 _maxSkippedCheckpoints ,
225+ uint256 _checkpointRewardDelta
226+ ) public onlyGovernance {
227+ require (_maxSkippedCheckpoints.mul (_rewardDecreasePerCheckpoint) <= CHK_REWARD_PRECISION);
228+ require (_checkpointRewardDelta <= CHK_REWARD_PRECISION);
229+
230+ rewardDecreasePerCheckpoint = _rewardDecreasePerCheckpoint;
231+ maxSkippedCheckpoints = _maxSkippedCheckpoints;
232+ checkpointRewardDelta = _checkpointRewardDelta;
233+ }
234+
216235 // New implementation upgrade
217236
218- function migrateValidatorsData (
219- uint256 validatorIdFrom ,
220- uint256 validatorIdTo
221- ) public onlyOwner {
237+ function migrateValidatorsData (uint256 validatorIdFrom , uint256 validatorIdTo ) public onlyOwner {
222238 for (uint256 i = validatorIdFrom; i < validatorIdTo; ++ i) {
223239 ValidatorShare contractAddress = ValidatorShare (validators[i].contractAddress);
224240 if (contractAddress != ValidatorShare (0 )) {
@@ -230,9 +246,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
230246 }
231247 }
232248
233- function insertSigners (
234- address [] calldata _signers
235- ) external onlyOwner {
249+ function insertSigners (address [] memory _signers ) public onlyOwner {
236250 signers = _signers;
237251 }
238252
@@ -664,11 +678,12 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
664678
665679 address delegationContract = validators[validatorId].contractAddress;
666680 if (delegationContract != address (0x0 )) {
667- uint256 delSlashedAmount = IValidatorShare (delegationContract).slash (
668- validators[validatorId].amount,
669- validators[validatorId].delegatedAmount,
670- _amount
671- );
681+ uint256 delSlashedAmount =
682+ IValidatorShare (delegationContract).slash (
683+ validators[validatorId].amount,
684+ validators[validatorId].delegatedAmount,
685+ _amount
686+ );
672687 _amount = _amount.sub (delSlashedAmount);
673688 }
674689
@@ -790,6 +805,59 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
790805 return context;
791806 }
792807
808+ function _calculateCheckpointReward (
809+ uint256 blockInterval ,
810+ uint256 signedStakePower ,
811+ uint256 currentTotalStake
812+ ) internal returns (uint256 ) {
813+ // checkpoint rewards are based on BlockInterval multiplied on `CHECKPOINT_REWARD`
814+ // for bigger checkpoints reward is reduced by rewardDecreasePerCheckpoint for each subsequent interval
815+
816+ // for smaller checkpoints
817+ // if interval is 50% of checkPointBlockInterval then reward R is half of `CHECKPOINT_REWARD`
818+ // and then stakePower is 90% of currentValidatorSetTotalStake then final reward is 90% of R
819+
820+ uint256 targetBlockInterval = checkPointBlockInterval;
821+ uint256 ckpReward = CHECKPOINT_REWARD;
822+ uint256 fullIntervals = Math.min (blockInterval / targetBlockInterval, maxSkippedCheckpoints);
823+
824+ // only apply to full checkpoints
825+ if (fullIntervals > 0 && fullIntervals != prevBlockInterval) {
826+ if (prevBlockInterval != 0 ) {
827+ // give more reward for faster and less for slower checkpoint
828+ uint256 delta = (ckpReward * checkpointRewardDelta / CHK_REWARD_PRECISION);
829+
830+ if (prevBlockInterval > fullIntervals) {
831+ // checkpoint is faster
832+ ckpReward += delta;
833+ } else {
834+ ckpReward -= delta;
835+ }
836+ }
837+
838+ prevBlockInterval = fullIntervals;
839+ }
840+
841+ uint256 reward;
842+
843+ if (blockInterval > targetBlockInterval) {
844+ // count how many full intervals
845+ uint256 _rewardDecreasePerCheckpoint = rewardDecreasePerCheckpoint;
846+
847+ // calculate reward for full intervals
848+ reward = ckpReward.mul (fullIntervals).sub (ckpReward.mul (((fullIntervals - 1 ) * fullIntervals / 2 ).mul (_rewardDecreasePerCheckpoint)).div (CHK_REWARD_PRECISION));
849+ // adjust block interval, in case last interval is not full
850+ blockInterval = blockInterval.sub (fullIntervals.mul (targetBlockInterval));
851+ // adjust checkpoint reward by the amount it suppose to decrease
852+ ckpReward = ckpReward.sub (ckpReward.mul (fullIntervals).mul (_rewardDecreasePerCheckpoint).div (CHK_REWARD_PRECISION));
853+ }
854+
855+ // give proportionally less for the rest
856+ reward = reward.add (blockInterval.mul (ckpReward).div (targetBlockInterval));
857+ reward = reward.mul (signedStakePower).div (currentTotalStake);
858+ return reward;
859+ }
860+
793861 function _increaseRewardAndAssertConsensus (
794862 uint256 blockInterval ,
795863 address proposer ,
@@ -803,13 +871,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
803871 uint256 currentTotalStake = validatorState.amount;
804872 require (signedStakePower >= currentTotalStake.mul (2 ).div (3 ).add (1 ), "2/3+1 non-majority! " );
805873
806- // checkpoint rewards are based on BlockInterval multiplied on `CHECKPOINT_REWARD`
807- // for bigger checkpoints reward is capped at `CHECKPOINT_REWARD`
808- // if interval is 50% of checkPointBlockInterval then reward R is half of `CHECKPOINT_REWARD`
809- // and then stakePower is 90% of currentValidatorSetTotalStake then final reward is 90% of R
810- uint256 reward = blockInterval.mul (CHECKPOINT_REWARD).div (checkPointBlockInterval);
811- reward = reward.mul (signedStakePower).div (currentTotalStake);
812- reward = Math.min (CHECKPOINT_REWARD, reward);
874+ uint256 reward = _calculateCheckpointReward (blockInterval, signedStakePower, currentTotalStake);
813875
814876 uint256 _proposerBonus = reward.mul (proposerBonus).div (MAX_PROPOSER_BONUS);
815877 uint256 proposerId = signerToValidator[proposer];
@@ -825,9 +887,8 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
825887 // update stateMerkleTree root for accounts balance on heimdall chain
826888 accountStateRoot = stateRoot;
827889
828- uint256 newRewardPerStake = rewardPerStake.add (
829- reward.sub (_proposerBonus).mul (REWARD_PRECISION).div (signedStakePower)
830- );
890+ uint256 newRewardPerStake =
891+ rewardPerStake.add (reward.sub (_proposerBonus).mul (REWARD_PRECISION).div (signedStakePower));
831892
832893 // evaluate rewards for validator who did't sign and set latest reward per stake to new value to avoid them from getting new rewards.
833894 _updateValidatorsRewards (unsignedValidators, totalUnsignedValidators, newRewardPerStake);
@@ -860,7 +921,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
860921 ) private {
861922 uint256 initialRewardPerStake = validators[validatorId].initialRewardPerStake;
862923
863- // attempt to save gas in case if rewards were updated previosuly
924+ // attempt to save gas in case if rewards were updated previosuly
864925 if (initialRewardPerStake < currentRewardPerStake) {
865926 uint256 validatorsStake = validators[validatorId].amount;
866927 uint256 delegatedAmount = validators[validatorId].delegatedAmount;
@@ -870,12 +931,22 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
870931 validatorId,
871932 validatorsStake,
872933 delegatedAmount,
873- _getEligibleValidatorReward (validatorId, combinedStakePower, currentRewardPerStake, initialRewardPerStake)
934+ _getEligibleValidatorReward (
935+ validatorId,
936+ combinedStakePower,
937+ currentRewardPerStake,
938+ initialRewardPerStake
939+ )
874940 );
875941 } else {
876942 _increaseValidatorReward (
877943 validatorId,
878- _getEligibleValidatorReward (validatorId, validatorsStake, currentRewardPerStake, initialRewardPerStake)
944+ _getEligibleValidatorReward (
945+ validatorId,
946+ validatorsStake,
947+ currentRewardPerStake,
948+ initialRewardPerStake
949+ )
879950 );
880951 }
881952 }
@@ -910,12 +981,8 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
910981 uint256 reward
911982 ) private {
912983 uint256 combinedStakePower = delegatedAmount.add (validatorsStake);
913- (uint256 validatorReward , uint256 delegatorsReward ) = _getValidatorAndDelegationReward (
914- validatorId,
915- validatorsStake,
916- reward,
917- combinedStakePower
918- );
984+ (uint256 validatorReward , uint256 delegatorsReward ) =
985+ _getValidatorAndDelegationReward (validatorId, validatorsStake, reward, combinedStakePower);
919986
920987 if (delegatorsReward > 0 ) {
921988 validators[validatorId].delegatorsReward = validators[validatorId].delegatorsReward.add (delegatorsReward);
@@ -1134,7 +1201,7 @@ contract StakeManager is StakeManagerStorage, Initializable, IStakeManager, Dele
11341201 delete signers[totalSigners - 1 ];
11351202
11361203 // bubble last element to the beginning until target signer is met
1137- for (uint i = totalSigners - 1 ; i > 0 ; -- i) {
1204+ for (uint256 i = totalSigners - 1 ; i > 0 ; -- i) {
11381205 if (swapSigner == signerToDelete) {
11391206 break ;
11401207 }
0 commit comments