@0x/contracts-staking: More work on MixinFinalizer.

This commit is contained in:
Lawrence Forman 2019-09-10 21:42:19 -04:00 committed by Lawrence Forman
parent bbae6b3de2
commit 73f1aca4a1
5 changed files with 115 additions and 41 deletions

View File

@ -65,10 +65,11 @@ contract MixinFinalizer is
external
returns (uint256 _unfinalizedPoolsRemaining)
{
uint256 closingEpoch = currentEpoch;
// Make sure the previous epoch has been fully finalized.
if (unfinalizedPoolsRemaining != 0) {
LibRichErrors.rrevert(LibStakingRichErrors.PreviousEpochNotFinalized(
currentEpoch - 1,
closingEpoch.sub(1),
unfinalizedPoolsRemaining
));
}
@ -78,15 +79,23 @@ contract MixinFinalizer is
unfinalizedTotalFeesCollected = totalFeesCollected;
unfinalizedTotalWeightedStake = totalWeightedStake;
totalRewardsPaid = 0;
// Emit an event.
emit EpochEnded(
closingEpoch,
numActivePoolsThisEpoch,
rewardsAvailable,
totalWeightedStake,
totalFeesCollected
);
// Reset current epoch state.
totalFeesCollected = 0;
totalWeightedStake = 0;
numActivePoolsThisEpoch = 0;
// Advance the epoch. This will revert if not enough time has passed.
_goToNextEpoch();
// If there were no active pools, finalize the epoch now.
// If there were no active pools, the epoch is already finalized.
if (unfinalizedPoolsRemaining == 0) {
emit EpochFinalized();
emit EpochFinalized(closingEpoch, 0, unfinalizedRewardsAvailable);
}
return _unfinalizedPoolsRemaining = unfinalizedPoolsRemaining;
}
@ -96,10 +105,96 @@ contract MixinFinalizer is
/// repeatedly until all active pools that were emitted in in a
/// `StakingPoolActivated` in the prior epoch have been finalized.
/// Pools that have already been finalized will be silently ignored.
/// We deliberately try not to revert here in case multiple parties
/// are finalizing pools.
/// @param poolIds List of active pool IDs to finalize.
/// @return rewardsPaid Total rewards paid to the pools passed in.
/// @return _unfinalizedPoolsRemaining The number of unfinalized pools left.
function finalizePools(bytes32[] memory poolIds) external {
function finalizePools(bytes32[] memory poolIds)
external
returns (uint256 rewardsPaid, uint256 _unfinalizedPoolsRemaining)
{
uint256 epoch = currentEpoch.sub(1);
uint256 poolsRemaining = unfinalizedPoolsRemaining;
uint256 numPoolIds = poolIds.length;
uint256 rewardsPaid = 0;
// Pointer to the active pools in the last epoch.
// We use `(currentEpoch - 1) % 2` as the index to reuse state.
mapping(bytes32 => IStructs.ActivePool) storage activePools =
activePoolsByEpoch[epoch % 2];
for (uint256 i = 0; i < numPoolIds && poolsRemaining != 0; i++) {
bytes32 poolId = poolIds[i];
IStructs.ActivePool memory pool = activePools[poolId];
// Ignore pools that aren't active.
if (pool.feesCollected != 0) {
// Credit the pool with rewards.
// We will transfer the total rewards to the vault at the end.
rewardsPaid = rewardsPaid.add(_creditRewardsToPool(poolId, pool));
// Clear the pool state so we don't finalize it again,
// and to recoup some gas.
activePools[poolId] = IStructs.ActivePool(0, 0);
// Decrease the number of unfinalized pools left.
poolsRemaining = poolsRemaining.sub(1);
// Emit an event.
emit RewardsPaid(epoch, poolId, reward);
}
}
// Deposit all the rewards at once into the RewardVault.
_depositIntoStakingPoolRewardVault(rewardsPaid);
// Update finalization state.
totalRewardsPaidLastEpoch = totalRewardsPaidLastEpoch.add(rewardsPaid);
_unfinalizedPoolsRemaining = unfinalizedPoolsRemaining = poolsRemaining;
// If there are no more unfinalized pools remaining, the epoch is
// finalized.
if (poolsRemaining == 0) {
emit EpochFinalized(
epoch,
totalRewardsPaidLastEpoch,
unfinalizedRewardsAvailable.sub(totalRewardsPaidLastEpoch)
);
}
}
/// @dev Computes the rewards owned for a pool during finalization and
/// credits it in the RewardVault.
/// @param The epoch being finalized.
/// @param poolId The pool's ID.
/// @param pool The pool.
/// @return rewards Amount of rewards for this pool.
function _creditRewardsToPool(
uint256 epoch,
bytes32 poolId,
IStructs.ActivePool memory pool
)
internal
returns (uint256 rewards)
{
// Use the cobb-douglas function to compute the reward.
reward = _cobbDouglas(
unfinalizedRewardsAvailable,
pool.feesCollected,
unfinalizedTotalFeesCollected,
pool.weightedStake,
unfinalizedTotalWeightedStake,
cobbDouglasAlphaNumerator,
cobbDouglasAlphaDenomintor
);
// Credit the pool the reward in the RewardVault.
(, uint256 membersPortionOfReward) = rewardVault.recordDepositFor(
poolId,
reward,
// If no delegated stake, all rewards go to the operator.
pool.delegatedStake == 0
);
// Sync delegator rewards.
if (membersPortionOfReward != 0) {
_recordRewardForDelegators(
poolId,
membersPortionOfReward,
pool.delegatedStake,
epoch
);
}
}
/// @dev The cobb-douglas function used to compute fee-based rewards for staking pools in a given epoch.

View File

@ -145,7 +145,7 @@ contract MixinStorage is
uint256 totalWeightedStakeThisEpoch;
/// @dev State information for each active pool in an epoch.
/// In practice, we only store state for `currentEpoch % 2`.
mapping(uint256 => mapping(bytes32 => ActivePool)) activePoolsByEpoch;
mapping(uint256 => mapping(bytes32 => IStructs.ActivePool)) activePoolsByEpoch;
/// @dev Number of pools activated in the current epoch.
uint256 numActivePoolsThisEpoch;
/// @dev Rewards (ETH) available to the epoch being finalized (the previous

View File

@ -43,23 +43,13 @@ interface IStakingEvents {
address exchangeAddress
);
/// @dev Emitted by MixinScheduler when the epoch is changed.
/// @param epoch The epoch we changed to.
/// @param startTimeInSeconds The start time of the epoch.
/// @param earliestEndTimeInSeconds The earliest this epoch can end.
event EpochChanged(
uint256 epoch,
uint256 startTimeInSeconds,
uint256 earliestEndTimeInSeconds
);
/// @dev Emitted by MixinFinalizer when an epoch has ended.
/// @param epoch The closing epoch.
/// @param numActivePools Number of active pools in the closing epoch.
/// @param rewardsAvailable Rewards available to all active pools.
/// @param totalWeightedStake Total weighted stake across all active pools.
/// @param totalFeesCollected Total fees collected across all active pools.
event EpochFinalized(
event EpochEnded(
uint256 epoch,
uint256 numActivePools,
uint256 rewardsAvailable,
@ -77,6 +67,16 @@ interface IStakingEvents {
uint256 rewardsRemaining
);
/// @dev Emitted by MixinFinalizer when rewards are paid out to a pool.
/// @param epoch The epoch when the rewards were earned.
/// @param poolId The pool's ID.
/// @param reward Amount of reward paid.
event RewardsPaid(
uint255 epoch,
bytes32 poolId,
uint255 reward
);
/// @dev Emitted whenever staking parameters are changed via the `setParams()` function.
/// @param epochDurationInSeconds Minimum seconds between epochs.
/// @param rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
@ -111,22 +111,6 @@ interface IStakingEvents {
uint256 endEpoch
);
/// @dev Emitted by MixinExchangeFees when rewards are paid out.
/// @param totalActivePools Total active pools this epoch.
/// @param totalFeesCollected Total fees collected this epoch, across all active pools.
/// @param totalWeightedStake Total weighted stake attributed to each pool. Delegated stake is weighted less.
/// @param totalRewardsPaid Total rewards paid out across all active pools.
/// @param initialContractBalance Balance of this contract before paying rewards.
/// @param finalContractBalance Balance of this contract after paying rewards.
event RewardsPaid(
uint256 totalActivePools,
uint256 totalFeesCollected,
uint256 totalWeightedStake,
uint256 totalRewardsPaid,
uint256 initialContractBalance,
uint256 finalContractBalance
);
/// @dev Emitted by MixinStakingPool when a new pool is created.
/// @param poolId Unique id generated for pool.
/// @param operator The operator (creator) of pool.

View File

@ -23,11 +23,13 @@ interface IStructs {
/// @dev Status for a pool that actively traded during the current epoch.
/// (see MixinExchangeFees).
/// @param feesCollected Fees collected in ETH by this pool in the current epoch.
/// @param weightedStake Amount of weighted stake currently held by the pool.
/// @param feesCollected Fees collected in ETH by this pool.
/// @param weightedStake Amount of weighted stake in the pool.
/// @param delegatedStake Amount of delegated, non-operator stake in the pool.
struct ActivePool {
uint256 feesCollected;
uint256 weightedStake;
uint256 delegatedStake;
}
/// @dev Encapsulates a balance for the current and next epochs.

View File

@ -87,13 +87,6 @@ contract MixinScheduler is
uint256 earliestEndTimeInSeconds = currentEpochStartTimeInSeconds.safeAdd(
epochDurationInSeconds
);
// notify of epoch change
emit EpochChanged(
currentEpoch,
currentEpochStartTimeInSeconds,
earliestEndTimeInSeconds
);
}
/// @dev Assert scheduler state before initializing it.