Removed explicit dependency for delegator on the next epoch after staking.
This commit is contained in:
@@ -165,22 +165,22 @@ contract MixinCumulativeRewards is
|
||||
view
|
||||
returns (uint256 reward)
|
||||
{
|
||||
if (memberStakeOverInterval == 0) {
|
||||
// Sanity check if we can skip computation, as it will result in zero.
|
||||
if (memberStakeOverInterval == 0 || beginEpoch == endEpoch) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sanity check interval
|
||||
require(beginEpoch <= endEpoch, "CR_INTERVAL_INVALID");
|
||||
require(beginEpoch < endEpoch, "CR_INTERVAL_INVALID");
|
||||
|
||||
// Sanity check begin reward
|
||||
IStructs.Fraction memory beginReward =
|
||||
_cumulativeRewardsByPool[poolId][beginEpoch];
|
||||
require(_isCumulativeRewardSet(beginReward), "CR_INTERVAL_INVALID_BEGIN");
|
||||
(IStructs.Fraction memory beginReward, uint256 beginRewardStoredAt) = _getCumulativeRewardAtEpoch(poolId, beginEpoch);
|
||||
(IStructs.Fraction memory endReward, uint256 endRewardStoredAt) = _getCumulativeRewardAtEpoch(poolId, endEpoch);
|
||||
|
||||
// Sanity check end reward
|
||||
IStructs.Fraction memory endReward =
|
||||
_cumulativeRewardsByPool[poolId][endEpoch];
|
||||
require(_isCumulativeRewardSet(endReward), "CR_INTERVAL_INVALID_END");
|
||||
// If the rewards were stored at the same epoch then the computation will result in zero.
|
||||
if (beginRewardStoredAt == endRewardStoredAt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compute reward
|
||||
reward = LibFractions.scaleDifference(
|
||||
@@ -203,4 +203,45 @@ contract MixinCumulativeRewards is
|
||||
uint256 lastStoredEpoch = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
return _cumulativeRewardsByPool[poolId][lastStoredEpoch];
|
||||
}
|
||||
|
||||
/// @dev Fetch the cumulative reward for a given epoch.
|
||||
/// If the corresponding CR does not exist in state, then we backtrack
|
||||
/// to find its value by querying `epoch-1` and then most recent CR.
|
||||
/// @param poolId Unique ID of pool.
|
||||
/// @param epoch The epoch to find the
|
||||
/// @return cumulativeReward The cumulative reward for `poolId` at `epoch`.
|
||||
/// @return cumulativeRewardStoredAt Epoch that the `cumulativeReward` is stored at.
|
||||
function _getCumulativeRewardAtEpoch(bytes32 poolId, uint256 epoch)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
IStructs.Fraction memory cumulativeReward,
|
||||
uint256 cumulativeRewardStoredAt
|
||||
)
|
||||
{
|
||||
// Return CR at `epoch`, given it's set.
|
||||
cumulativeReward = _cumulativeRewardsByPool[poolId][epoch];
|
||||
if (_isCumulativeRewardSet(cumulativeReward)) {
|
||||
return (cumulativeReward, epoch);
|
||||
}
|
||||
|
||||
// Return CR at `epoch-1`, given it's set.
|
||||
uint256 lastEpoch = epoch.safeSub(1);
|
||||
cumulativeReward = _cumulativeRewardsByPool[poolId][lastEpoch];
|
||||
if (_isCumulativeRewardSet(cumulativeReward)) {
|
||||
return (cumulativeReward, lastEpoch);
|
||||
}
|
||||
|
||||
// Return the most recent CR, given it's less than `epoch`.
|
||||
uint256 mostRecentEpoch = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
if (mostRecentEpoch < epoch) {
|
||||
cumulativeReward = _cumulativeRewardsByPool[poolId][mostRecentEpoch];
|
||||
if (_isCumulativeRewardSet(cumulativeReward)) {
|
||||
return (cumulativeReward, mostRecentEpoch);
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find a CR for `epoch`
|
||||
revert("CR_INVALID_EPOCH");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,10 +294,9 @@ contract MixinStakingPoolRewards is
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param unsyncedStake Unsynced delegated stake to pool by owner
|
||||
/// @param currentEpoch The epoch in which this call is executing
|
||||
/// @param unfinalizedMembersReward Unfinalized total members reward
|
||||
/// (if any).
|
||||
/// @param unfinalizedMembersReward Unfinalized total members reward (if any).
|
||||
/// @param unfinalizedMembersStake Unfinalized total members stake (if any).
|
||||
/// @return totalReward Balance in ETH.
|
||||
/// @return reward Balance in WETH.
|
||||
function _computeDelegatorReward(
|
||||
bytes32 poolId,
|
||||
IStructs.StoredBalance memory unsyncedStake,
|
||||
@@ -322,56 +321,81 @@ contract MixinStakingPoolRewards is
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If there are unfinalized rewards this epoch, compute the member's
|
||||
// share.
|
||||
if (unfinalizedMembersReward != 0 && unfinalizedMembersStake != 0) {
|
||||
// Unfinalized rewards are always earned from stake in
|
||||
// the prior epoch so we want the stake at `currentEpoch-1`.
|
||||
uint256 _stake = unsyncedStake.currentEpoch >= currentEpoch.safeSub(1) ?
|
||||
unsyncedStake.currentEpochBalance :
|
||||
unsyncedStake.nextEpochBalance;
|
||||
if (_stake != 0) {
|
||||
reward = LibMath.getPartialAmountFloor(
|
||||
unfinalizedMembersReward,
|
||||
unfinalizedMembersStake,
|
||||
_stake
|
||||
);
|
||||
}
|
||||
}
|
||||
// We account for rewards over 3 intervals, below.
|
||||
|
||||
// Get the last epoch where a reward was credited to this pool, which
|
||||
// also happens to be when we last created a cumulative reward entry.
|
||||
uint256 lastRewardEpoch = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
// 1/3 Unfinalized rewards earned in `currentEpoch - 1`.
|
||||
reward = _computeUnfinalizedDelegatorReward(
|
||||
unsyncedStake,
|
||||
currentEpoch,
|
||||
unfinalizedMembersReward,
|
||||
unfinalizedMembersStake
|
||||
);
|
||||
|
||||
// If the stake has been touched since the last reward epoch,
|
||||
// it has already been claimed.
|
||||
if (unsyncedStake.currentEpoch >= lastRewardEpoch) {
|
||||
return reward;
|
||||
}
|
||||
// From here we know: `unsyncedStake.currentEpoch < currentEpoch > 0`.
|
||||
|
||||
uint256 nextStakeEpoch = uint256(unsyncedStake.currentEpoch).safeAdd(1);
|
||||
// 2/3 Finalized rewards earned in epochs [`unsyncedStake.currentEpoch + 1` .. `currentEpoch - 1`]
|
||||
uint256 unsyncedStakeNextEpoch = uint256(unsyncedStake.currentEpoch).safeAdd(1);
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
unsyncedStake.currentEpochBalance,
|
||||
unsyncedStake.currentEpoch,
|
||||
nextStakeEpoch
|
||||
unsyncedStakeNextEpoch
|
||||
)
|
||||
);
|
||||
if (nextStakeEpoch < lastRewardEpoch) {
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
unsyncedStake.nextEpochBalance,
|
||||
nextStakeEpoch,
|
||||
lastRewardEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 3/3 Finalized rewards earned in epoch `unsyncedStake.currentEpoch`.
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
unsyncedStake.nextEpochBalance,
|
||||
unsyncedStakeNextEpoch,
|
||||
currentEpoch
|
||||
)
|
||||
);
|
||||
|
||||
return reward;
|
||||
}
|
||||
|
||||
/// @dev Computes the unfinalized rewards earned by a delegator in the last epoch.
|
||||
/// @param unsyncedStake Unsynced delegated stake to pool by owner
|
||||
/// @param currentEpoch The epoch in which this call is executing
|
||||
/// @param unfinalizedMembersReward Unfinalized total members reward (if any).
|
||||
/// @param unfinalizedMembersStake Unfinalized total members stake (if any).
|
||||
/// @return reward Balance in WETH.
|
||||
function _computeUnfinalizedDelegatorReward(
|
||||
IStructs.StoredBalance memory unsyncedStake,
|
||||
uint256 currentEpoch,
|
||||
uint256 unfinalizedMembersReward,
|
||||
uint256 unfinalizedMembersStake
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
// If there are unfinalized rewards this epoch, compute the member's
|
||||
// share.
|
||||
if (unfinalizedMembersReward == 0 || unfinalizedMembersStake == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unfinalized rewards are always earned from stake in
|
||||
// the prior epoch so we want the stake at `currentEpoch-1`.
|
||||
uint256 unfinalizedStakeBalance = unsyncedStake.currentEpoch >= currentEpoch.safeSub(1) ?
|
||||
unsyncedStake.currentEpochBalance :
|
||||
unsyncedStake.nextEpochBalance;
|
||||
|
||||
// Sanity check to save gas on computation
|
||||
if (unfinalizedStakeBalance == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compute unfinalized reward
|
||||
return LibMath.getPartialAmountFloor(
|
||||
unfinalizedMembersReward,
|
||||
unfinalizedMembersStake,
|
||||
unfinalizedStakeBalance
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Adds or removes cumulative reward dependencies for a delegator.
|
||||
/// A delegator always depends on the cumulative reward for the current
|
||||
/// and next epoch, if they would still have stake in the next epoch.
|
||||
@@ -388,25 +412,18 @@ contract MixinStakingPoolRewards is
|
||||
// reference point when updating dependencies
|
||||
IStructs.Fraction memory mostRecentCumulativeReward = _getMostRecentCumulativeReward(poolId);
|
||||
|
||||
// Record dependency on current epoch.
|
||||
if (_delegatedStakeToPoolByOwner.currentEpochBalance != 0
|
||||
|| _delegatedStakeToPoolByOwner.nextEpochBalance != 0)
|
||||
{
|
||||
_trySetCumulativeReward(
|
||||
poolId,
|
||||
_delegatedStakeToPoolByOwner.currentEpoch,
|
||||
mostRecentCumulativeReward
|
||||
);
|
||||
// The delegator depends on the Cumulative Reward for this epoch
|
||||
// only if they are currently staked or will be staked next epoch.
|
||||
if (_delegatedStakeToPoolByOwner.currentEpochBalance == 0 && _delegatedStakeToPoolByOwner.nextEpochBalance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record dependency on the next epoch
|
||||
if (_delegatedStakeToPoolByOwner.nextEpochBalance != 0) {
|
||||
_trySetCumulativeReward(
|
||||
poolId,
|
||||
uint256(_delegatedStakeToPoolByOwner.currentEpoch).safeAdd(1),
|
||||
mostRecentCumulativeReward
|
||||
);
|
||||
}
|
||||
// Delegator depends on the Cumulative Reward for this epoch - ensure it is set.
|
||||
_trySetCumulativeReward(
|
||||
poolId,
|
||||
_delegatedStakeToPoolByOwner.currentEpoch,
|
||||
mostRecentCumulativeReward
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Increments rewards for a pool.
|
||||
|
||||
@@ -53,7 +53,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Creates CR for epoch 1
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 0 }, { event: 'SetCumulativeReward', epoch: 1 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 0 }],
|
||||
);
|
||||
});
|
||||
it('re-delegating in the same epoch', async () => {
|
||||
@@ -64,17 +64,13 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
],
|
||||
[
|
||||
// Updates CR for epoch 0
|
||||
// Creates CR for epoch 1
|
||||
TestAction.Delegate,
|
||||
// Updates CR for epoch 0
|
||||
// Updates CR for epoch 1
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 0 },
|
||||
{ event: 'SetCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetCumulativeReward', epoch: 0 },
|
||||
{ event: 'SetCumulativeReward', epoch: 1 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -91,13 +87,11 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Creates a CR for epoch 1
|
||||
// Sets MRCR to epoch 1
|
||||
// Unsets the CR for epoch 0
|
||||
// Creates a CR for epoch 2
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetCumulativeReward', epoch: 2 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -115,14 +109,11 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
// Updates CR for epoch 1
|
||||
// Sets MRCR to epoch 1
|
||||
// Creates CR for epoch 2
|
||||
// Clears CR for epoch 0
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetCumulativeReward', epoch: 2 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -135,8 +126,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
TestAction.Finalize,
|
||||
// Creates CR for epoch 1
|
||||
// Sets MRCR to epoch 1
|
||||
// Clears CR for epoch 0
|
||||
// Creates CR for epoch 2
|
||||
TestAction.Delegate,
|
||||
// Move to epoch 2
|
||||
TestAction.Finalize,
|
||||
@@ -151,7 +140,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 2 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 2 },
|
||||
{ event: 'SetCumulativeReward', epoch: 3 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -208,7 +196,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 2 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 2 },
|
||||
{ event: 'SetCumulativeReward', epoch: 3 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -241,7 +228,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 2 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 2 },
|
||||
{ event: 'SetCumulativeReward', epoch: 3 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -300,7 +286,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 2
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 3 }, { event: 'SetCumulativeReward', epoch: 4 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 3 }],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 0 and 1, earn reward in epoch 3, then undelegate half', async () => {
|
||||
@@ -335,7 +321,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 2
|
||||
TestAction.Undelegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 3 }, { event: 'SetCumulativeReward', epoch: 4 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 3 }],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 1, 2, earn rewards in epoch 3, skip to epoch 4, then delegate', async () => {
|
||||
@@ -376,7 +362,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 4 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 4 },
|
||||
{ event: 'SetCumulativeReward', epoch: 5 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -401,7 +386,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 1 },
|
||||
{ event: 'SetCumulativeReward', epoch: 2 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -440,7 +424,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 4 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 4 },
|
||||
{ event: 'SetCumulativeReward', epoch: 5 },
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -472,7 +455,6 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
[
|
||||
{ event: 'SetCumulativeReward', epoch: 3 },
|
||||
{ event: 'SetMostRecentCumulativeReward', epoch: 3 },
|
||||
{ event: 'SetCumulativeReward', epoch: 4 },
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user