@0x/contracts-staking
: More work on MixinFinalizer
.
This commit is contained in:
parent
bbae6b3de2
commit
73f1aca4a1
@ -65,10 +65,11 @@ contract MixinFinalizer is
|
|||||||
external
|
external
|
||||||
returns (uint256 _unfinalizedPoolsRemaining)
|
returns (uint256 _unfinalizedPoolsRemaining)
|
||||||
{
|
{
|
||||||
|
uint256 closingEpoch = currentEpoch;
|
||||||
// Make sure the previous epoch has been fully finalized.
|
// Make sure the previous epoch has been fully finalized.
|
||||||
if (unfinalizedPoolsRemaining != 0) {
|
if (unfinalizedPoolsRemaining != 0) {
|
||||||
LibRichErrors.rrevert(LibStakingRichErrors.PreviousEpochNotFinalized(
|
LibRichErrors.rrevert(LibStakingRichErrors.PreviousEpochNotFinalized(
|
||||||
currentEpoch - 1,
|
closingEpoch.sub(1),
|
||||||
unfinalizedPoolsRemaining
|
unfinalizedPoolsRemaining
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -78,15 +79,23 @@ contract MixinFinalizer is
|
|||||||
unfinalizedTotalFeesCollected = totalFeesCollected;
|
unfinalizedTotalFeesCollected = totalFeesCollected;
|
||||||
unfinalizedTotalWeightedStake = totalWeightedStake;
|
unfinalizedTotalWeightedStake = totalWeightedStake;
|
||||||
totalRewardsPaid = 0;
|
totalRewardsPaid = 0;
|
||||||
|
// Emit an event.
|
||||||
|
emit EpochEnded(
|
||||||
|
closingEpoch,
|
||||||
|
numActivePoolsThisEpoch,
|
||||||
|
rewardsAvailable,
|
||||||
|
totalWeightedStake,
|
||||||
|
totalFeesCollected
|
||||||
|
);
|
||||||
// Reset current epoch state.
|
// Reset current epoch state.
|
||||||
totalFeesCollected = 0;
|
totalFeesCollected = 0;
|
||||||
totalWeightedStake = 0;
|
totalWeightedStake = 0;
|
||||||
numActivePoolsThisEpoch = 0;
|
numActivePoolsThisEpoch = 0;
|
||||||
// Advance the epoch. This will revert if not enough time has passed.
|
// Advance the epoch. This will revert if not enough time has passed.
|
||||||
_goToNextEpoch();
|
_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) {
|
if (unfinalizedPoolsRemaining == 0) {
|
||||||
emit EpochFinalized();
|
emit EpochFinalized(closingEpoch, 0, unfinalizedRewardsAvailable);
|
||||||
}
|
}
|
||||||
return _unfinalizedPoolsRemaining = unfinalizedPoolsRemaining;
|
return _unfinalizedPoolsRemaining = unfinalizedPoolsRemaining;
|
||||||
}
|
}
|
||||||
@ -96,10 +105,96 @@ contract MixinFinalizer is
|
|||||||
/// repeatedly until all active pools that were emitted in in a
|
/// repeatedly until all active pools that were emitted in in a
|
||||||
/// `StakingPoolActivated` in the prior epoch have been finalized.
|
/// `StakingPoolActivated` in the prior epoch have been finalized.
|
||||||
/// Pools that have already been finalized will be silently ignored.
|
/// 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.
|
/// @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.
|
/// @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.
|
/// @dev The cobb-douglas function used to compute fee-based rewards for staking pools in a given epoch.
|
||||||
|
@ -145,7 +145,7 @@ contract MixinStorage is
|
|||||||
uint256 totalWeightedStakeThisEpoch;
|
uint256 totalWeightedStakeThisEpoch;
|
||||||
/// @dev State information for each active pool in an epoch.
|
/// @dev State information for each active pool in an epoch.
|
||||||
/// In practice, we only store state for `currentEpoch % 2`.
|
/// 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.
|
/// @dev Number of pools activated in the current epoch.
|
||||||
uint256 numActivePoolsThisEpoch;
|
uint256 numActivePoolsThisEpoch;
|
||||||
/// @dev Rewards (ETH) available to the epoch being finalized (the previous
|
/// @dev Rewards (ETH) available to the epoch being finalized (the previous
|
||||||
|
@ -43,23 +43,13 @@ interface IStakingEvents {
|
|||||||
address exchangeAddress
|
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.
|
/// @dev Emitted by MixinFinalizer when an epoch has ended.
|
||||||
/// @param epoch The closing epoch.
|
/// @param epoch The closing epoch.
|
||||||
/// @param numActivePools Number of active pools in the closing epoch.
|
/// @param numActivePools Number of active pools in the closing epoch.
|
||||||
/// @param rewardsAvailable Rewards available to all active pools.
|
/// @param rewardsAvailable Rewards available to all active pools.
|
||||||
/// @param totalWeightedStake Total weighted stake across all active pools.
|
/// @param totalWeightedStake Total weighted stake across all active pools.
|
||||||
/// @param totalFeesCollected Total fees collected across all active pools.
|
/// @param totalFeesCollected Total fees collected across all active pools.
|
||||||
event EpochFinalized(
|
event EpochEnded(
|
||||||
uint256 epoch,
|
uint256 epoch,
|
||||||
uint256 numActivePools,
|
uint256 numActivePools,
|
||||||
uint256 rewardsAvailable,
|
uint256 rewardsAvailable,
|
||||||
@ -77,6 +67,16 @@ interface IStakingEvents {
|
|||||||
uint256 rewardsRemaining
|
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.
|
/// @dev Emitted whenever staking parameters are changed via the `setParams()` function.
|
||||||
/// @param epochDurationInSeconds Minimum seconds between epochs.
|
/// @param epochDurationInSeconds Minimum seconds between epochs.
|
||||||
/// @param rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
|
/// @param rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
|
||||||
@ -111,22 +111,6 @@ interface IStakingEvents {
|
|||||||
uint256 endEpoch
|
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.
|
/// @dev Emitted by MixinStakingPool when a new pool is created.
|
||||||
/// @param poolId Unique id generated for pool.
|
/// @param poolId Unique id generated for pool.
|
||||||
/// @param operator The operator (creator) of pool.
|
/// @param operator The operator (creator) of pool.
|
||||||
|
@ -23,11 +23,13 @@ interface IStructs {
|
|||||||
|
|
||||||
/// @dev Status for a pool that actively traded during the current epoch.
|
/// @dev Status for a pool that actively traded during the current epoch.
|
||||||
/// (see MixinExchangeFees).
|
/// (see MixinExchangeFees).
|
||||||
/// @param feesCollected Fees collected in ETH by this pool in the current epoch.
|
/// @param feesCollected Fees collected in ETH by this pool.
|
||||||
/// @param weightedStake Amount of weighted stake currently held by the pool.
|
/// @param weightedStake Amount of weighted stake in the pool.
|
||||||
|
/// @param delegatedStake Amount of delegated, non-operator stake in the pool.
|
||||||
struct ActivePool {
|
struct ActivePool {
|
||||||
uint256 feesCollected;
|
uint256 feesCollected;
|
||||||
uint256 weightedStake;
|
uint256 weightedStake;
|
||||||
|
uint256 delegatedStake;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Encapsulates a balance for the current and next epochs.
|
/// @dev Encapsulates a balance for the current and next epochs.
|
||||||
|
@ -87,13 +87,6 @@ contract MixinScheduler is
|
|||||||
uint256 earliestEndTimeInSeconds = currentEpochStartTimeInSeconds.safeAdd(
|
uint256 earliestEndTimeInSeconds = currentEpochStartTimeInSeconds.safeAdd(
|
||||||
epochDurationInSeconds
|
epochDurationInSeconds
|
||||||
);
|
);
|
||||||
|
|
||||||
// notify of epoch change
|
|
||||||
emit EpochChanged(
|
|
||||||
currentEpoch,
|
|
||||||
currentEpochStartTimeInSeconds,
|
|
||||||
earliestEndTimeInSeconds
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Assert scheduler state before initializing it.
|
/// @dev Assert scheduler state before initializing it.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user