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

Commit

Permalink
Merge branch 'master' into redemption-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasz-zimnoch authored Nov 9, 2020
2 parents 2dca954 + 24d3618 commit 8cb25fd
Show file tree
Hide file tree
Showing 22 changed files with 8,418 additions and 3,783 deletions.
3 changes: 1 addition & 2 deletions solidity/contracts/BondedECDSAKeep.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import "@keep-network/keep-core/contracts/TokenStaking.sol";
/// @title Bonded ECDSA Keep
/// @notice ECDSA keep with additional signer bond requirement.
/// @dev This contract is used as a master contract for clone factory in
/// BondedECDSAKeepFactory as per EIP-1167. It should never be removed after
/// initial deployment as this will break functionality for all created clones.
/// BondedECDSAKeepFactory as per EIP-1167.
contract BondedECDSAKeep is AbstractBondedECDSAKeep {
// Stake that was required from each keep member on keep creation.
// The value is used for keep members slashing.
Expand Down
128 changes: 112 additions & 16 deletions solidity/contracts/ECDSARewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ import "./BondedECDSAKeep.sol";
/// Reporting a terminated keep returns its allocated reward to the pool of
/// unallocated rewards.
contract ECDSARewards is Rewards {
// The amount of tokens each individual beneficiary address
// can receive in a single interval is capped to 3M tokens.
uint256 public beneficiaryRewardCap = 3000000 * 10**18;

// BondedECDSAKeepFactory deployment date, Sep-14-2020 interval started.
// https://etherscan.io/address/0xA7d9E842EFB252389d613dA88EDa3731512e40bD
uint256 internal constant ecdsaFirstIntervalStart = 1600041600;
Expand Down Expand Up @@ -87,9 +91,21 @@ contract ECDSARewards is Rewards {

uint256 internal constant minimumECDSAKeepsPerInterval = 1000;

BondedECDSAKeepFactory factory;
// The total amount of rewards allocated to the given beneficiary address,
// in the given interval.
// `allocatedRewards[beneficiary][interval] -> amount`
mapping(address => mapping(uint256 => uint256)) internal allocatedRewards;
// The amount of interval rewards withdrawn to the given beneficiary.
mapping(address => mapping(uint256 => uint256)) internal withdrawnRewards;

BondedECDSAKeepFactory internal factory;
TokenStaking internal tokenStaking;

constructor(address _token, address payable _factoryAddress)
constructor(
address _token,
address payable _factoryAddress,
address _tokenStakingAddress
)
public
Rewards(
_token,
Expand All @@ -100,17 +116,70 @@ contract ECDSARewards is Rewards {
)
{
factory = BondedECDSAKeepFactory(_factoryAddress);
tokenStaking = TokenStaking(_tokenStakingAddress);
}

/// @notice Stakers can receive KEEP rewards from multiple keeps of their choice
/// in one transaction to reduce total cost comparing to single calls for rewards.
/// It is a caller responsibility to determine the cost and consumed gas when
/// receiving rewards from multiple keeps.
/// @param keepIdentifiers An array of keep addresses.
function receiveRewards(bytes32[] memory keepIdentifiers) public {
for (uint256 i = 0; i < keepIdentifiers.length; i++) {
receiveReward(keepIdentifiers[i]);
}
/// @notice Get the amount of rewards allocated
/// for the specified operator's beneficiary in the specified interval.
/// @param interval The interval
/// @param operator The operator
/// @return The amount allocated
function getAllocatedRewards(uint256 interval, address operator)
external
view
returns (uint256)
{
address beneficiary = tokenStaking.beneficiaryOf(operator);
return allocatedRewards[beneficiary][interval];
}

/// @notice Get the amount of rewards already withdrawn
/// for the specified operator's beneficiary in the specified interval.
/// @param interval The interval
/// @param operator The operator
/// @return The amount already withdrawn
function getWithdrawnRewards(uint256 interval, address operator)
external
view
returns (uint256)
{
address beneficiary = tokenStaking.beneficiaryOf(operator);
return withdrawnRewards[beneficiary][interval];
}

/// @notice Get the amount of rewards withdrawable
/// for the specified operator's beneficiary in the specified interval.
/// @param interval The interval
/// @param operator The operator
/// @return The amount withdrawable
function getWithdrawableRewards(uint256 interval, address operator)
external
view
returns (uint256)
{
address beneficiary = tokenStaking.beneficiaryOf(operator);
uint256 allocated = allocatedRewards[beneficiary][interval];
uint256 withdrawn = withdrawnRewards[beneficiary][interval];
return allocated.sub(withdrawn);
}

/// @notice Withdraw all available rewards for the given interval.
/// The rewards will be paid to the beneficiary of the specified operator.
/// @param interval The interval
/// @param operator The operator
function withdrawRewards(uint256 interval, address operator) external {
address beneficiary = tokenStaking.beneficiaryOf(operator);

uint256 allocated = allocatedRewards[beneficiary][interval];
uint256 alreadyWithdrawn = withdrawnRewards[beneficiary][interval];

require(allocated > alreadyWithdrawn, "No rewards to withdraw");

uint256 withdrawableRewards = allocated.sub(alreadyWithdrawn);

withdrawnRewards[beneficiary][interval] = allocated;

token.safeTransfer(beneficiary, withdrawableRewards);
}

function _getKeepCount() internal view returns (uint256) {
Expand Down Expand Up @@ -158,16 +227,43 @@ contract ECDSARewards is Rewards {
return factory.getKeepOpenedTimestamp(toAddress(_keep)) != 0;
}

/// @notice Get the members of the specified keep, and distribute the reward
/// amount between them. The reward isn't paid out immediately,
/// but is instead kept in the reward contract until each operator
/// individually requests to withdraw the rewards.
function _distributeReward(bytes32 _keep, uint256 amount)
internal
isAddress(_keep)
{
token.approve(toAddress(_keep), amount);
address[] memory members = BondedECDSAKeep(toAddress(_keep))
.getMembers();
uint256 interval = intervalOf(_getCreationTime(_keep));

uint256 memberCount = members.length;
uint256 dividend = amount.div(memberCount);
uint256 remainder = amount.mod(memberCount);

uint256[] memory allocations = new uint256[](memberCount);

BondedECDSAKeep(toAddress(_keep)).distributeERC20Reward(
address(token),
amount
);
for (uint256 i = 0; i < memberCount - 1; i++) {
allocations[i] = dividend;
}
allocations[memberCount - 1] = dividend.add(remainder);

for (uint256 i = 0; i < memberCount; i++) {
address beneficiary = tokenStaking.beneficiaryOf(members[i]);
uint256 addedAllocation = allocations[i];
uint256 prevAllocated = allocatedRewards[beneficiary][interval];
uint256 newAllocation = prevAllocated.add(addedAllocation);
if (newAllocation > beneficiaryRewardCap) {
uint256 deallocatedAmount = newAllocation.sub(
beneficiaryRewardCap
);
newAllocation = beneficiaryRewardCap;
deallocate(deallocatedAmount);
}
allocatedRewards[beneficiary][interval] = newAllocation;
}
}

function toAddress(bytes32 keepBytes) internal pure returns (address) {
Expand Down
23 changes: 23 additions & 0 deletions solidity/contracts/ECDSARewardsEscrowBeneficiary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity 0.5.17;

import "@keep-network/keep-core/contracts/PhasedEscrow.sol";

/// @title ECDSARewardsEscrowBeneficiary
/// @notice Transfer the received tokens from PhasedEscrow to a designated
/// ECDSARewards contract.
contract ECDSARewardsEscrowBeneficiary is StakerRewardsBeneficiary {
constructor(IERC20 _token, IStakerRewards _stakerRewards)
public
StakerRewardsBeneficiary(_token, _stakerRewards)
{}
}

/// @title ECDSABackportRewardsEscrowBeneficiary
/// @notice Trasfer the received tokens from Phased Escrow to a designated
/// ECDSABackportRewards contract.
contract ECDSABackportRewardsEscrowBeneficiary is StakerRewardsBeneficiary {
constructor(IERC20 _token, IStakerRewards _stakerRewards)
public
StakerRewardsBeneficiary(_token, _stakerRewards)
{}
}
Loading

0 comments on commit 8cb25fd

Please sign in to comment.