Skip to content
This repository was archived by the owner on May 22, 2023. It is now read-only.

Commit 7827b53

Browse files
authored
Merge pull request #584 from keep-network/rewards-cap
Cap the maximum reward per ECDSA keep operator per interval
2 parents cab3d78 + 447ed01 commit 7827b53

4 files changed

Lines changed: 3250 additions & 3745 deletions

File tree

solidity/contracts/ECDSARewards.sol

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,25 @@ contract ECDSARewards is Rewards {
8787

8888
uint256 internal constant minimumECDSAKeepsPerInterval = 1000;
8989

90-
BondedECDSAKeepFactory factory;
90+
// The amount of tokens each individual beneficiary address
91+
// can receive in a single interval is capped.
92+
// TODO: set actual value
93+
uint256 internal constant beneficiaryRewardCap = 400000 * 10**18;
94+
// The total amount of rewards allocated to the given beneficiary address,
95+
// in the given interval.
96+
// `allocatedRewards[beneficiary][interval] -> amount`
97+
mapping(address => mapping(uint256 => uint256)) internal allocatedRewards;
98+
// The amount of interval rewards withdrawn to the given beneficiary.
99+
mapping(address => mapping(uint256 => uint256)) internal withdrawnRewards;
91100

92-
constructor(address _token, address payable _factoryAddress)
101+
BondedECDSAKeepFactory internal factory;
102+
TokenStaking internal tokenStaking;
103+
104+
constructor(
105+
address _token,
106+
address payable _factoryAddress,
107+
address _tokenStakingAddress
108+
)
93109
public
94110
Rewards(
95111
_token,
@@ -100,6 +116,70 @@ contract ECDSARewards is Rewards {
100116
)
101117
{
102118
factory = BondedECDSAKeepFactory(_factoryAddress);
119+
tokenStaking = TokenStaking(_tokenStakingAddress);
120+
}
121+
122+
/// @notice Get the amount of rewards allocated
123+
/// for the specified operator's beneficiary in the specified interval.
124+
/// @param interval The interval
125+
/// @param operator The operator
126+
/// @return The amount allocated
127+
function getAllocatedRewards(uint256 interval, address operator)
128+
external
129+
view
130+
returns (uint256)
131+
{
132+
address beneficiary = tokenStaking.beneficiaryOf(operator);
133+
return allocatedRewards[beneficiary][interval];
134+
}
135+
136+
/// @notice Get the amount of rewards already withdrawn
137+
/// for the specified operator's beneficiary in the specified interval.
138+
/// @param interval The interval
139+
/// @param operator The operator
140+
/// @return The amount already withdrawn
141+
function getWithdrawnRewards(uint256 interval, address operator)
142+
external
143+
view
144+
returns (uint256)
145+
{
146+
address beneficiary = tokenStaking.beneficiaryOf(operator);
147+
return withdrawnRewards[beneficiary][interval];
148+
}
149+
150+
/// @notice Get the amount of rewards withdrawable
151+
/// for the specified operator's beneficiary in the specified interval.
152+
/// @param interval The interval
153+
/// @param operator The operator
154+
/// @return The amount withdrawable
155+
function getWithdrawableRewards(uint256 interval, address operator)
156+
external
157+
view
158+
returns (uint256)
159+
{
160+
address beneficiary = tokenStaking.beneficiaryOf(operator);
161+
uint256 allocated = allocatedRewards[beneficiary][interval];
162+
uint256 withdrawn = withdrawnRewards[beneficiary][interval];
163+
return allocated.sub(withdrawn);
164+
}
165+
166+
/// @notice Withdraw all available rewards for the given interval.
167+
/// The rewards will be paid to the beneficiary of the specified operator.
168+
/// @param interval The interval
169+
/// @param operator The operator
170+
function withdrawRewards(uint256 interval, address operator) external {
171+
address beneficiary = tokenStaking.beneficiaryOf(operator);
172+
173+
uint256 allocated = allocatedRewards[beneficiary][interval];
174+
uint256 alreadyWithdrawn = withdrawnRewards[beneficiary][interval];
175+
176+
require(allocated > alreadyWithdrawn, "No rewards to withdraw");
177+
178+
uint256 withdrawableRewards = allocated.sub(alreadyWithdrawn);
179+
180+
withdrawnRewards[beneficiary][interval] = allocated;
181+
182+
token.safeTransfer(beneficiary, withdrawableRewards);
103183
}
104184

105185
/// @notice Stakers can receive KEEP rewards from multiple keeps of their choice
@@ -158,16 +238,43 @@ contract ECDSARewards is Rewards {
158238
return factory.getKeepOpenedTimestamp(toAddress(_keep)) != 0;
159239
}
160240

241+
/// @notice Get the members of the specified keep, and distribute the reward
242+
/// amount between them. The reward isn't paid out immediately,
243+
/// but is instead kept in the reward contract until each operator
244+
/// individually requests to withdraw the rewards.
161245
function _distributeReward(bytes32 _keep, uint256 amount)
162246
internal
163247
isAddress(_keep)
164248
{
165-
token.approve(toAddress(_keep), amount);
249+
address[] memory members = BondedECDSAKeep(toAddress(_keep))
250+
.getMembers();
251+
uint256 interval = intervalOf(_getCreationTime(_keep));
252+
253+
uint256 memberCount = members.length;
254+
uint256 dividend = amount.div(memberCount);
255+
uint256 remainder = amount.mod(memberCount);
256+
257+
uint256[] memory allocations = new uint256[](memberCount);
166258

167-
BondedECDSAKeep(toAddress(_keep)).distributeERC20Reward(
168-
address(token),
169-
amount
170-
);
259+
for (uint256 i = 0; i < memberCount - 1; i++) {
260+
allocations[i] = dividend;
261+
}
262+
allocations[memberCount - 1] = dividend.add(remainder);
263+
264+
for (uint256 i = 0; i < memberCount; i++) {
265+
address beneficiary = tokenStaking.beneficiaryOf(members[i]);
266+
uint256 addedAllocation = allocations[i];
267+
uint256 prevAllocated = allocatedRewards[beneficiary][interval];
268+
uint256 newAllocation = prevAllocated.add(addedAllocation);
269+
if (newAllocation > beneficiaryRewardCap) {
270+
uint256 deallocatedAmount = newAllocation.sub(
271+
beneficiaryRewardCap
272+
);
273+
newAllocation = beneficiaryRewardCap;
274+
deallocate(deallocatedAmount);
275+
}
276+
allocatedRewards[beneficiary][interval] = newAllocation;
277+
}
171278
}
172279

173280
function toAddress(bytes32 keepBytes) internal pure returns (address) {

0 commit comments

Comments
 (0)