Skip to content

Commit

Permalink
feat: staker can now claim commission (#758)
Browse files Browse the repository at this point in the history
* feat: staker can now claim commission

* fix: test fix

* fix: scenarios test fix

* chore: semantic changes

* docs: natspec changes

* chore: lint fixes
  • Loading branch information
SamAg19 authored Mar 24, 2022
1 parent 2ec3d73 commit 6469129
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 179 deletions.
16 changes: 14 additions & 2 deletions contracts/Core/RewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./interface/IStakeManager.sol";
import "./interface/IVoteManager.sol";
import "./interface/IRewardManager.sol";
import "./interface/ICollectionManager.sol";
import "../tokenization/IStakedToken.sol";
import "../Initializable.sol";
import "./storage/Constants.sol";
import "./parameters/child/RewardManagerParams.sol";
Expand Down Expand Up @@ -46,8 +47,19 @@ contract RewardManager is Initializable, Constants, RewardManagerParams, IReward

/// @inheritdoc IRewardManager
function giveBlockReward(uint32 stakerId, uint32 epoch) external override onlyRole(REWARD_MODIFIER_ROLE) {
uint256 prevStake = stakeManager.getStake(stakerId);
stakeManager.setStakerStake(epoch, stakerId, StakeChanged.BlockReward, prevStake, prevStake + blockReward);
Structs.Staker memory staker = stakeManager.getStaker(stakerId);
if (!staker.acceptDelegation) {
stakeManager.setStakerStake(epoch, stakerId, StakeChanged.BlockReward, staker.stake, staker.stake + blockReward);
return;
}
IStakedToken sToken = IStakedToken(staker.tokenAddress);
uint256 totalSupply = sToken.totalSupply();
uint256 stakerSRZR = sToken.balanceOf(staker._address);
uint256 delegatorShare = blockReward - ((blockReward * stakerSRZR) / totalSupply);
uint8 commissionApplicable = staker.commission < maxCommission ? staker.commission : maxCommission;
uint256 commission = (delegatorShare * commissionApplicable) / 100;
stakeManager.setStakerStake(epoch, stakerId, StakeChanged.BlockReward, staker.stake, staker.stake + (blockReward - commission));
stakeManager.setStakerStakerReward(epoch, stakerId, StakerRewardChanged.StakerRewardAdded, staker.commission, commission);
}

/// @inheritdoc IRewardManager
Expand Down
91 changes: 61 additions & 30 deletions contracts/Core/StakeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
uint256 timestamp
);

/**
* @dev Emitted when stakerReward has been changed for the staker.
* @param epoch in which stakerReward was changed
* @param stakerId id of the staker whose stakerReward was changed
* @param reason reason why the the change in stake took place
* @param prevStakerReward stakerReward before the change took place
* @param newStakerReward updated stakerReward
* @param timestamp time at which the change took place
*/
event StakerRewardChange(
uint32 epoch,
uint32 indexed stakerId,
Constants.StakerRewardChanged reason,
uint256 prevStakerReward,
uint256 newStakerReward,
uint256 timestamp
);

/**
* @dev Emitted when there has been change in the age of the staker.
* @param epoch in which change of age took place
Expand Down Expand Up @@ -218,7 +236,7 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
stakerIds[msg.sender] = stakerId;
// slither-disable-next-line reentrancy-benign
IStakedToken sToken = IStakedToken(stakedTokenFactory.createStakedToken(address(this), numStakers));
stakers[numStakers] = Structs.Staker(false, false, 0, numStakers, 10000, msg.sender, address(sToken), epoch, 0, amount);
stakers[numStakers] = Structs.Staker(false, false, 0, numStakers, 10000, msg.sender, address(sToken), epoch, 0, amount, 0);
_setupRole(STOKEN_ROLE, address(sToken));
// Minting
// Ignoring below line for testing as this is standard erc20 function
Expand Down Expand Up @@ -306,11 +324,7 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake

require(sToken.balanceOf(msg.sender) >= sAmount, "Invalid Amount");

locks[msg.sender][staker.tokenAddress][LockType.Unstake] = Structs.Lock(
sAmount,
epoch + unstakeLockPeriod,
sToken.getRZRDeposited(msg.sender, sAmount)
);
locks[msg.sender][staker.tokenAddress][LockType.Unstake] = Structs.Lock(sAmount, epoch + unstakeLockPeriod);
emit Unstaked(msg.sender, epoch, stakerId, sAmount, staker.stake, block.timestamp);
// Ignoring below line for testing as this is standard erc20 function
require(sToken.transferFrom(msg.sender, address(this), sAmount), "sToken transfer failed");
Expand Down Expand Up @@ -346,18 +360,15 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
uint256 rAmount = _convertSRZRToRZR(lock.amount, staker.stake, sToken.totalSupply());
staker.stake = staker.stake - rAmount;

locks[msg.sender][staker.tokenAddress][LockType.Withdraw] = Structs.Lock(rAmount, epoch + withdrawLockPeriod, lock.initial);
locks[msg.sender][staker.tokenAddress][LockType.Withdraw] = Structs.Lock(rAmount, epoch + withdrawLockPeriod);
// Ignoring below line for testing as this is standard erc20 function
require(sToken.burn(address(this), lock.amount), "Token burn Failed");
//emit event here
emit WithdrawInitiated(msg.sender, epoch, stakerId, rAmount, staker.stake, sToken.totalSupply(), block.timestamp);
}

/** @notice staker/delegator can claim their locked RAZORS.
* if a staker is calling then no commission is calculated and can claim their funds
* if a delegator is calling then commission is calculated on the RAZOR amount being withdrawn
* and deducted from withdraw balance. the new balance is sent to the delegator and staker
* receives the commission
/**
* @notice staker/delegator can claim their locked RAZORS.
* @param stakerId The Id of staker associated with sRZR which user want to unlockWithdraw
*/
function unlockWithdraw(uint32 stakerId) external initialized whenNotPaused {
Expand All @@ -371,31 +382,29 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
// slither-disable-next-line timestamp
require(lock.unlockAfter <= epoch, "Withdraw epoch not reached");

// Transfer commission in case of delegators
// Check commission rate >0
uint256 commission = 0;
if (stakerIds[msg.sender] != stakerId && staker.commission > 0) {
// Calculate Gain
uint256 initial = lock.initial;
if (lock.amount > initial) {
uint256 gain = lock.amount - initial;
uint8 commissionApplicable = staker.commission < maxCommission ? staker.commission : maxCommission;
commission = (gain * commissionApplicable) / 100;
}
}

uint256 withdrawAmount = lock.amount - commission;
uint256 withdrawAmount = lock.amount;
// Reset lock
_resetLock(stakerId);

emit Withdrew(msg.sender, epoch, stakerId, withdrawAmount, staker.stake, block.timestamp);
// Ignoring below line for testing as this is standard erc20 function
require(razor.transfer(staker._address, commission), "couldnt transfer");
//Transfer Razor Back
// Ignoring below line for testing as this is standard erc20 function
require(razor.transfer(msg.sender, withdrawAmount), "couldnt transfer");
}

/**
* @notice staker can claim the rewards earned from delegator's pool share as commission.
*/
function claimStakerReward() external initialized whenNotPaused {
uint32 stakerId = stakerIds[msg.sender];
require(stakerId != 0, "staker doesnt exist");
require(stakers[stakerId].stakerReward != 0, "no stakerReward to transfer");
uint32 epoch = _getEpoch();
uint256 stakerRewardToBeClaimed = stakers[stakerId].stakerReward;
_setStakerStakerReward(epoch, stakerId, StakerRewardChanged.StakerRewardClaimed, stakers[stakerId].stakerReward, 0);
require(razor.transfer(msg.sender, stakerRewardToBeClaimed), "couldnt transfer");
}

/// @inheritdoc IStakeManager
function escape(address _address) external override initialized onlyRole(ESCAPE_HATCH_ROLE) whenPaused {
if (escapeHatchEnabled) {
Expand Down Expand Up @@ -484,6 +493,17 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
_setStakerStake(_epoch, _id, reason, prevStake, _stake);
}

/// @inheritdoc IStakeManager
function setStakerStakerReward(
uint32 _epoch,
uint32 _id,
Constants.StakerRewardChanged reason,
uint256 prevStakerReward,
uint256 _stakerReward
) external override onlyRole(STAKE_MODIFIER_ROLE) {
_setStakerStakerReward(_epoch, _id, reason, prevStakerReward, _stakerReward);
}

/// @inheritdoc IStakeManager
function slash(
uint32 epoch,
Expand Down Expand Up @@ -614,6 +634,17 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
emit StakeChange(_epoch, _id, reason, _prevStake, _stake, block.timestamp);
}

function _setStakerStakerReward(
uint32 _epoch,
uint32 _id,
Constants.StakerRewardChanged reason,
uint256 prevStakerReward,
uint256 _stakerReward
) internal {
stakers[_id].stakerReward = _stakerReward;
emit StakerRewardChange(_epoch, _id, reason, prevStakerReward, _stakerReward, block.timestamp);
}

/**
* @return isStakerActive : Activity < Grace
*/
Expand Down Expand Up @@ -670,8 +701,8 @@ contract StakeManager is Initializable, StakeStorage, StateManager, Pause, Stake
* @param stakerId of the staker
*/
function _resetLock(uint32 stakerId) private {
locks[msg.sender][stakers[stakerId].tokenAddress][LockType.Unstake] = Structs.Lock({amount: 0, unlockAfter: 0, initial: 0});
locks[msg.sender][stakers[stakerId].tokenAddress][LockType.Withdraw] = Structs.Lock({amount: 0, unlockAfter: 0, initial: 0});
locks[msg.sender][stakers[stakerId].tokenAddress][LockType.Unstake] = Structs.Lock({amount: 0, unlockAfter: 0});
locks[msg.sender][stakers[stakerId].tokenAddress][LockType.Withdraw] = Structs.Lock({amount: 0, unlockAfter: 0});
emit ResetLock(stakerId, msg.sender, _getEpoch());
}
}
3 changes: 2 additions & 1 deletion contracts/Core/interface/IRewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ interface IRewardManager {
/**
* @notice The function gives block reward for one valid proposer in the
* previous epoch by increasing stake of staker
* called from confirmBlock function of BlockManager contract
* called from confirmBlock function of BlockManager contract. Commission
* from the delegator's pool is given out to the staker from the block reward
* @param stakerId The ID of the staker
*/
function giveBlockReward(uint32 epoch, uint32 stakerId) external;
Expand Down
45 changes: 36 additions & 9 deletions contracts/Core/interface/IStakeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@ import "../../lib/Structs.sol";
import "../storage/Constants.sol";

interface IStakeManager {
/** @notice External function for setting stake of the staker
/**
* @notice External function for setting stake of the staker
* Used by RewardManager
* @param _epoch The epoch in which stake changes
* @param _id of the staker
* @param _stake the amount of Razor tokens staked
* @param reason the reason for stake to change
* @param prevStake previous stake of the staker
* @param _stake updated stake of the staker
*/
function setStakerStake(
uint32 _epoch,
uint32 _id,
Constants.StakeChanged reason,
uint256 _prevStake,
uint256 prevStake,
uint256 _stake
) external;

/** @notice The function is used by the Votemanager reveal function and BlockManager FinalizeDispute
/**
* @notice The function is used by the Votemanager reveal function and BlockManager FinalizeDispute
* to penalise the staker who lost his secret and make his stake less by "slashPenaltyAmount" and
* transfer to bounty hunter half the "slashPenaltyAmount" of the staker
* @param stakerId The ID of the staker who is penalised
Expand All @@ -30,7 +35,8 @@ interface IStakeManager {
address bountyHunter
) external;

/** @notice External function for setting staker age of the staker
/**
* @notice External function for setting staker age of the staker
* Used by RewardManager
* @param _epoch The epoch in which age changes
* @param _id of the staker
Expand All @@ -44,7 +50,25 @@ interface IStakeManager {
Constants.AgeChanged reason
) external;

/** @notice External function for setting epochLastPenalized of the staker
/**
* @notice External function for setting stakerReward of the staker
* Used by RewardManager
* @param _epoch The epoch in which stakerReward changes
* @param _id of the staker
* @param reason the reason for stakerReward to change
* @param prevStakerReward previous stakerReward of the staker
* @param _stakerReward updated stakerReward of the staker
*/
function setStakerStakerReward(
uint32 _epoch,
uint32 _id,
Constants.StakerRewardChanged reason,
uint256 prevStakerReward,
uint256 _stakerReward
) external;

/**
* @notice External function for setting epochLastPenalized of the staker
* Used by RewardManager
* @param _id of the staker
*/
Expand All @@ -55,7 +79,8 @@ interface IStakeManager {
*/
function escape(address _address) external;

/** @notice event being thrown after every successful sRZR transfer taking place
/**
* @notice event being thrown after every successful sRZR transfer taking place
* @param from sender
* @param to recepient
* @param amount srzr amount being transferred
Expand All @@ -68,12 +93,14 @@ interface IStakeManager {
uint32 stakerId
) external;

/** @param _address Address of the staker
/**
* @param _address Address of the staker
* @return The staker ID
*/
function getStakerId(address _address) external view returns (uint32);

/** @param _id The staker ID
/**
* @param _id The staker ID
* @return staker The Struct of staker information
*/
function getStaker(uint32 _id) external view returns (Structs.Staker memory staker);
Expand Down
1 change: 1 addition & 0 deletions contracts/Core/parameters/Governance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ contract Governance is Initializable, ACL, Constants {
require(_maxCommission <= 100, "Invalid Max Commission Update");
emit ParameterChanged(msg.sender, "maxCommission", _maxCommission, block.timestamp);
stakeManagerParams.setMaxCommission(_maxCommission);
rewardManagerParams.setMaxCommission(_maxCommission);
}

/**
Expand Down
9 changes: 9 additions & 0 deletions contracts/Core/parameters/child/RewardManagerParams.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ abstract contract RewardManagerParams is ACL, IRewardManagerParams, Constants {
/// @notice maximum percentage deviation allowed from medians for all collections
// slither-disable-next-line too-many-digits
uint32 public maxTolerance = 1000000;
/// @notice maximum commission stakers can charge from delegators on their profits
uint8 public maxCommission = 20;

/// @inheritdoc IRewardManagerParams
function setPenaltyNotRevealNum(uint32 _penaltyNotRevealNumerator) external override onlyRole(GOVERNANCE_ROLE) {
Expand Down Expand Up @@ -52,4 +54,11 @@ abstract contract RewardManagerParams is ACL, IRewardManagerParams, Constants {
// slither-disable-next-line events-maths
maxTolerance = _maxTolerance;
}

/// @inheritdoc IRewardManagerParams
function setMaxCommission(uint8 _maxCommission) external override onlyRole(GOVERNANCE_ROLE) {
require(_maxCommission <= 100, "Invalid Max Commission Update");
// slither-disable-next-line events-maths
maxCommission = _maxCommission;
}
}
7 changes: 7 additions & 0 deletions contracts/Core/parameters/interfaces/IRewardManagerParams.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,11 @@ interface IRewardManagerParams {
* @param _maxTolerance updated value for maxTolerance
*/
function setMaxTolerance(uint32 _maxTolerance) external;

/**
* @notice changing maximum commission stakers can charge from delegators on their profits
* @dev can be called only by the the address that has the governance role
* @param _maxCommission updated value to be set for maxCommission
*/
function setMaxCommission(uint8 _maxCommission) external;
}
6 changes: 5 additions & 1 deletion contracts/Core/storage/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ contract Constants {
enum StakeChanged {
BlockReward,
InactivityPenalty,
RandaoPenalty,
Slashed
}

enum StakerRewardChanged {
StakerRewardAdded,
StakerRewardClaimed
}

enum AgeChanged {
InactivityPenalty,
VotingRewardOrPenalty
Expand Down
2 changes: 1 addition & 1 deletion contracts/lib/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ library Structs {
uint32 epochCommissionLastUpdated;
// Slot 3
uint256 stake;
uint256 stakerReward;
}

struct Lock {
uint256 amount; //amount in sRZR/RZR
uint256 unlockAfter; // Can be made uint32 later if packing is possible
uint256 initial; // for unstake lock
}

struct BountyLock {
Expand Down
9 changes: 9 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ module.exports = {
},
},
},
'contracts/Core/StakeManager.sol': {
version: '0.8.4',
settings: {
optimizer: {
enabled: true,
runs: 2400,
},
},
},
},
},
networks: {
Expand Down
Loading

0 comments on commit 6469129

Please sign in to comment.