@0x/contracts-staking: Fix stake accounting.
This commit is contained in:
@@ -187,10 +187,9 @@ contract MixinStake is
|
||||
// Sanity check the pool we're delegating to exists.
|
||||
_assertStakingPoolExists(poolId);
|
||||
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
staker
|
||||
);
|
||||
// Cache amount delegated to pool by staker.
|
||||
IStructs.StoredBalance memory initDelegatedStakeToPoolByOwner =
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[staker][poolId]);
|
||||
|
||||
// Increment how much stake the staker has delegated to the input pool.
|
||||
_increaseNextBalance(
|
||||
@@ -200,6 +199,12 @@ contract MixinStake is
|
||||
|
||||
// Increment how much stake has been delegated to pool.
|
||||
_increaseNextBalance(_delegatedStakeByPoolId[poolId], amount);
|
||||
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
staker,
|
||||
initDelegatedStakeToPoolByOwner
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Un-Delegates a owners stake from a staking pool.
|
||||
@@ -216,10 +221,9 @@ contract MixinStake is
|
||||
// sanity check the pool we're undelegating from exists
|
||||
_assertStakingPoolExists(poolId);
|
||||
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
staker
|
||||
);
|
||||
// Cache amount delegated to pool by staker.
|
||||
IStructs.StoredBalance memory initDelegatedStakeToPoolByOwner =
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[staker][poolId]);
|
||||
|
||||
// decrement how much stake the staker has delegated to the input pool
|
||||
_decreaseNextBalance(
|
||||
@@ -229,6 +233,12 @@ contract MixinStake is
|
||||
|
||||
// decrement how much stake has been delegated to pool
|
||||
_decreaseNextBalance(_delegatedStakeByPoolId[poolId], amount);
|
||||
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
staker,
|
||||
initDelegatedStakeToPoolByOwner
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Returns a storage pointer to a user's stake in a given status.
|
||||
|
||||
@@ -46,7 +46,8 @@ contract MixinStakingPoolRewards is
|
||||
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
member
|
||||
member,
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[member][poolId])
|
||||
);
|
||||
|
||||
// Update stored balance with synchronized version; this prevents
|
||||
@@ -103,6 +104,7 @@ contract MixinStakingPoolRewards is
|
||||
return _computeDelegatorReward(
|
||||
poolId,
|
||||
member,
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[member][poolId]),
|
||||
unfinalizedMembersReward,
|
||||
unfinalizedMembersStake
|
||||
);
|
||||
@@ -112,9 +114,12 @@ contract MixinStakingPoolRewards is
|
||||
/// withdrawing rewards and adding/removing dependencies on cumulative rewards.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param member of the pool.
|
||||
/// @param unsyncedStake The member's unsynced delegated balance at the
|
||||
/// beginning of this transaction.
|
||||
function _withdrawAndSyncDelegatorRewards(
|
||||
bytes32 poolId,
|
||||
address member
|
||||
address member,
|
||||
IStructs.StoredBalance memory unsyncedStake
|
||||
)
|
||||
internal
|
||||
{
|
||||
@@ -125,6 +130,7 @@ contract MixinStakingPoolRewards is
|
||||
uint256 balance = _computeDelegatorReward(
|
||||
poolId,
|
||||
member,
|
||||
unsyncedStake,
|
||||
// No unfinalized values because we ensured the pool is already
|
||||
// finalized.
|
||||
0,
|
||||
@@ -247,12 +253,14 @@ contract MixinStakingPoolRewards is
|
||||
/// @dev Computes the reward balance in ETH of a specific member of a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param member of the pool.
|
||||
/// @param unsyncedStake Unsynced delegated stake to pool by staker
|
||||
/// @param unfinalizedMembersReward Unfinalized total members reward (if any).
|
||||
/// @param unfinalizedMembersStake Unfinalized total members stake (if any).
|
||||
/// @return reward Balance in WETH.
|
||||
function _computeDelegatorReward(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
IStructs.StoredBalance memory unsyncedStake,
|
||||
uint256 unfinalizedMembersReward,
|
||||
uint256 unfinalizedMembersStake
|
||||
)
|
||||
@@ -267,9 +275,6 @@ contract MixinStakingPoolRewards is
|
||||
return 0;
|
||||
}
|
||||
|
||||
IStructs.StoredBalance memory unsyncedStake =
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[member][poolId]);
|
||||
|
||||
// There can be no rewards if the last epoch when stake was synced is
|
||||
// equal to the current epoch, because all prior rewards, including
|
||||
// rewards finalized this epoch have been claimed.
|
||||
|
||||
@@ -86,7 +86,7 @@ contract TestDelegatorRewards is
|
||||
_poolById[poolId].operator = operatorAddress;
|
||||
_setOperatorShare(poolId, operatorReward, membersReward);
|
||||
_initGenesisCumulativeRewards(poolId);
|
||||
|
||||
|
||||
_syncPoolRewards(
|
||||
poolId,
|
||||
operatorReward + membersReward,
|
||||
@@ -110,6 +110,7 @@ contract TestDelegatorRewards is
|
||||
external
|
||||
{
|
||||
_initGenesisCumulativeRewards(poolId);
|
||||
IStructs.StoredBalance memory initialStake = _delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
IStructs.StoredBalance storage _stake = _delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
_stake.isInitialized = true;
|
||||
_stake.currentEpochBalance += uint96(stake);
|
||||
@@ -117,7 +118,8 @@ contract TestDelegatorRewards is
|
||||
_stake.currentEpoch = uint32(currentEpoch);
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
delegator
|
||||
delegator,
|
||||
initialStake
|
||||
);
|
||||
}
|
||||
|
||||
@@ -132,6 +134,7 @@ contract TestDelegatorRewards is
|
||||
external
|
||||
{
|
||||
_initGenesisCumulativeRewards(poolId);
|
||||
IStructs.StoredBalance memory initialStake = _delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
IStructs.StoredBalance storage _stake = _delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
if (_stake.currentEpoch < currentEpoch) {
|
||||
_stake.currentEpochBalance = _stake.nextEpochBalance;
|
||||
@@ -141,7 +144,8 @@ contract TestDelegatorRewards is
|
||||
_stake.currentEpoch = uint32(currentEpoch);
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
delegator
|
||||
delegator,
|
||||
initialStake
|
||||
);
|
||||
}
|
||||
|
||||
@@ -156,6 +160,7 @@ contract TestDelegatorRewards is
|
||||
external
|
||||
{
|
||||
_initGenesisCumulativeRewards(poolId);
|
||||
IStructs.StoredBalance memory initialStake = _delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
IStructs.StoredBalance storage _stake = _delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
if (_stake.currentEpoch < currentEpoch) {
|
||||
_stake.currentEpochBalance = _stake.nextEpochBalance;
|
||||
@@ -165,7 +170,8 @@ contract TestDelegatorRewards is
|
||||
_stake.currentEpoch = uint32(currentEpoch);
|
||||
_withdrawAndSyncDelegatorRewards(
|
||||
poolId,
|
||||
delegator
|
||||
delegator,
|
||||
initialStake
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
await simulation.runTestAsync(
|
||||
[TestAction.Finalize],
|
||||
[TestAction.CreatePool],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }, { event: 'SetMostRecentCumulativeReward', epoch: 1 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }],
|
||||
);
|
||||
});
|
||||
it('delegating in the same epoch pool is created', async () => {
|
||||
@@ -86,7 +86,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Unsets the CR for epoch 0
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }, { event: 'SetMostRecentCumulativeReward', epoch: 1 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }],
|
||||
);
|
||||
});
|
||||
it('re-delegating in a new epoch', async () => {
|
||||
@@ -105,7 +105,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Sets MRCR to epoch 1
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }, { event: 'SetMostRecentCumulativeReward', epoch: 1 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }],
|
||||
);
|
||||
});
|
||||
it('delegating in epoch 1 then again in epoch 2', async () => {
|
||||
@@ -128,7 +128,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 1
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 }, { event: 'SetMostRecentCumulativeReward', epoch: 2 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 } ],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 1 then undelegate in epoch 2', async () => {
|
||||
@@ -152,7 +152,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clear CR for epoch 1
|
||||
TestAction.Undelegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 }, { event: 'SetMostRecentCumulativeReward', epoch: 2 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 } ],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 0 and epoch 1, then undelegate half in epoch 2', async () => {
|
||||
@@ -181,7 +181,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 1
|
||||
TestAction.Undelegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 }, { event: 'SetMostRecentCumulativeReward', epoch: 2 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 } ],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 1 and 2 then again in 3', async () => {
|
||||
@@ -210,7 +210,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 1
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 }, { event: 'SetMostRecentCumulativeReward', epoch: 2 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 } ],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 0, earn reward in epoch 1', async () => {
|
||||
@@ -233,7 +233,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Sets MRCR to epoch 2
|
||||
TestAction.Finalize,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 }, { event: 'SetMostRecentCumulativeReward', epoch: 2 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 2 } ],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 0, epoch 2, earn reward in epoch 3, then delegate', async () => {
|
||||
@@ -341,7 +341,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 1
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 4 }, { event: 'SetMostRecentCumulativeReward', epoch: 4 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 4 } ],
|
||||
);
|
||||
});
|
||||
it('earn reward in epoch 1 with no stake, then delegate', async () => {
|
||||
@@ -362,7 +362,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Creates CR for epoch 2
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }, { event: 'SetMostRecentCumulativeReward', epoch: 1 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 1 }],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 1, 3, then delegate in epoch 4', async () => {
|
||||
@@ -397,7 +397,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Creates CR for epoch 5
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 4 }, { event: 'SetMostRecentCumulativeReward', epoch: 4 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 4 } ],
|
||||
);
|
||||
});
|
||||
it('delegate in epoch 1, then epoch 3', async () => {
|
||||
@@ -425,7 +425,7 @@ blockchainTests.resets('Cumulative Reward Tracking', env => {
|
||||
// Clears CR for epoch 2
|
||||
TestAction.Delegate,
|
||||
],
|
||||
[{ event: 'SetCumulativeReward', epoch: 3 }, { event: 'SetMostRecentCumulativeReward', epoch: 3 }],
|
||||
[{ event: 'SetCumulativeReward', epoch: 3 } ],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user