@0x/contracts-staking: Starting MBF.
This commit is contained in:
committed by
Lawrence Forman
parent
29f4d6918a
commit
bbae6b3de2
169
contracts/staking/contracts/src/finalization/MixinFinalizer.sol
Normal file
169
contracts/staking/contracts/src/finalization/MixinFinalizer.sol
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../libs/LibStakingRichErrors.sol";
|
||||
import "../libs/LibFixedMath.sol";
|
||||
import "../immutable/MixinStorage.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "../stake/MixinStakeBalances.sol";
|
||||
import "../sys/MixinScheduler.sol";
|
||||
import "../staking_pools/MixinStakingPool.sol";
|
||||
import "../staking_pools/MixinStakingPoolRewardVault.sol";
|
||||
import "./MixinExchangeManager.sol";
|
||||
|
||||
|
||||
/// @dev This mixin contains functions related to finalizing epochs.
|
||||
/// Finalization occurs over multiple calls because we can only
|
||||
/// discover the `totalRewardsPaid` to all pools by summing the
|
||||
/// the reward function across all active pools at the end of an
|
||||
/// epoch. Until this value is known for epoch `e`, we cannot finalize
|
||||
/// epoch `e+1`, because the remaining balance (`balance - totalRewardsPaid`)
|
||||
/// is the reward pool for finalizing the next epoch.
|
||||
contract MixinFinalizer is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolRewardVault,
|
||||
MixinScheduler,
|
||||
MixinStakeBalances,
|
||||
MixinStakingPoolRewards
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
|
||||
/// @dev Begins a new epoch, preparing the prior one for finalization.
|
||||
/// Throws if not enough time has passed between epochs or if the
|
||||
/// previous epoch was not fully finalized.
|
||||
/// If there were no active pools in the closing epoch, the epoch
|
||||
/// will be instantly finalized here. Otherwise, `finalizePools()`
|
||||
/// should be called on each active pool afterwards.
|
||||
/// @return _unfinalizedPoolsRemaining The number of unfinalized pools.
|
||||
function endEpoch()
|
||||
external
|
||||
returns (uint256 _unfinalizedPoolsRemaining)
|
||||
{
|
||||
// Make sure the previous epoch has been fully finalized.
|
||||
if (unfinalizedPoolsRemaining != 0) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.PreviousEpochNotFinalized(
|
||||
currentEpoch - 1,
|
||||
unfinalizedPoolsRemaining
|
||||
));
|
||||
}
|
||||
// Populate finalization state.
|
||||
unfinalizedPoolsRemaining = numActivePoolsThisEpoch;
|
||||
unfinalizedRewardsAvailable = address(this).balance;
|
||||
unfinalizedTotalFeesCollected = totalFeesCollected;
|
||||
unfinalizedTotalWeightedStake = totalWeightedStake;
|
||||
totalRewardsPaid = 0;
|
||||
// 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 (unfinalizedPoolsRemaining == 0) {
|
||||
emit EpochFinalized();
|
||||
}
|
||||
return _unfinalizedPoolsRemaining = unfinalizedPoolsRemaining;
|
||||
}
|
||||
|
||||
/// @dev Finalizes a pool that was active in the previous epoch, paying out
|
||||
/// its rewards to the reward vault. Keepers should call this function
|
||||
/// 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.
|
||||
/// @param poolIds List of active pool IDs to finalize.
|
||||
/// @return _unfinalizedPoolsRemaining The number of unfinalized pools left.
|
||||
function finalizePools(bytes32[] memory poolIds) external {
|
||||
|
||||
}
|
||||
|
||||
/// @dev The cobb-douglas function used to compute fee-based rewards for staking pools in a given epoch.
|
||||
/// Note that in this function there is no limitation on alpha; we tend to get better rounding
|
||||
/// on the simplified versions below.
|
||||
/// @param totalRewards collected over an epoch.
|
||||
/// @param ownerFees Fees attributed to the owner of the staking pool.
|
||||
/// @param totalFees collected across all active staking pools in the epoch.
|
||||
/// @param ownerStake Stake attributed to the owner of the staking pool.
|
||||
/// @param totalStake collected across all active staking pools in the epoch.
|
||||
/// @param alphaNumerator Numerator of `alpha` in the cobb-dougles function.
|
||||
/// @param alphaDenominator Denominator of `alpha` in the cobb-douglas function.
|
||||
/// @return ownerRewards Rewards for the owner.
|
||||
function _cobbDouglas(
|
||||
uint256 totalRewards,
|
||||
uint256 ownerFees,
|
||||
uint256 totalFees,
|
||||
uint256 ownerStake,
|
||||
uint256 totalStake,
|
||||
uint256 alphaNumerator,
|
||||
uint256 alphaDenominator
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 ownerRewards)
|
||||
{
|
||||
int256 feeRatio = LibFixedMath._toFixed(ownerFees, totalFees);
|
||||
int256 stakeRatio = LibFixedMath._toFixed(ownerStake, totalStake);
|
||||
if (feeRatio == 0 || stakeRatio == 0) {
|
||||
return ownerRewards = 0;
|
||||
}
|
||||
|
||||
// The cobb-doublas function has the form:
|
||||
// `totalRewards * feeRatio ^ alpha * stakeRatio ^ (1-alpha)`
|
||||
// This is equivalent to:
|
||||
// `totalRewards * stakeRatio * e^(alpha * (ln(feeRatio / stakeRatio)))`
|
||||
// However, because `ln(x)` has the domain of `0 < x < 1`
|
||||
// and `exp(x)` has the domain of `x < 0`,
|
||||
// and fixed-point math easily overflows with multiplication,
|
||||
// we will choose the following if `stakeRatio > feeRatio`:
|
||||
// `totalRewards * stakeRatio / e^(alpha * (ln(stakeRatio / feeRatio)))`
|
||||
|
||||
// Compute
|
||||
// `e^(alpha * (ln(feeRatio/stakeRatio)))` if feeRatio <= stakeRatio
|
||||
// or
|
||||
// `e^(ln(stakeRatio/feeRatio))` if feeRatio > stakeRatio
|
||||
int256 n = feeRatio <= stakeRatio ?
|
||||
LibFixedMath._div(feeRatio, stakeRatio) :
|
||||
LibFixedMath._div(stakeRatio, feeRatio);
|
||||
n = LibFixedMath._exp(
|
||||
LibFixedMath._mulDiv(
|
||||
LibFixedMath._ln(n),
|
||||
int256(alphaNumerator),
|
||||
int256(alphaDenominator)
|
||||
)
|
||||
);
|
||||
// Compute
|
||||
// `totalRewards * n` if feeRatio <= stakeRatio
|
||||
// or
|
||||
// `totalRewards / n` if stakeRatio > feeRatio
|
||||
n = feeRatio <= stakeRatio ?
|
||||
LibFixedMath._mul(stakeRatio, n) :
|
||||
LibFixedMath._div(stakeRatio, n);
|
||||
// Multiply the above with totalRewards.
|
||||
ownerRewards = LibFixedMath._uintMul(n, totalRewards);
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,33 @@ contract MixinStorage is
|
||||
// Denominator for cobb douglas alpha factor.
|
||||
uint32 public cobbDouglasAlphaDenominator;
|
||||
|
||||
/* Finalization states */
|
||||
|
||||
/// @dev The total fees collected in the current epoch, built up iteratively
|
||||
/// in `payProtocolFee()`.
|
||||
uint256 totalFeesCollectedThisEpoch;
|
||||
/// @dev The total weighted stake in the current epoch, built up iteratively
|
||||
/// in `payProtocolFee()`.
|
||||
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;
|
||||
/// @dev Number of pools activated in the current epoch.
|
||||
uint256 numActivePoolsThisEpoch;
|
||||
/// @dev Rewards (ETH) available to the epoch being finalized (the previous
|
||||
/// epoch). This is simply the balance of the contract at the end of
|
||||
/// the epoch.
|
||||
uint256 unfinalizedRewardsAvailable;
|
||||
/// @dev The number of active pools in the last epoch that have yet to be
|
||||
/// finalized through `finalizePools()`.
|
||||
uint256 unfinalizedPoolsRemaining;
|
||||
/// @dev The total fees collected for the epoch being finalized.
|
||||
uint256 unfinalizedTotalFeesCollected;
|
||||
/// @dev The total fees collected for the epoch being finalized.
|
||||
uint256 unfinalizedTotalWeightedStake;
|
||||
/// @dev How many rewards were paid at the end of finalization.
|
||||
uint256 totalRewardsPaidLastEpoch;
|
||||
|
||||
/// @dev Adds owner as an authorized address.
|
||||
constructor()
|
||||
public
|
||||
|
||||
@@ -53,6 +53,30 @@ interface IStakingEvents {
|
||||
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(
|
||||
uint256 epoch,
|
||||
uint256 numActivePools,
|
||||
uint256 rewardsAvailable,
|
||||
uint256 totalWeightedStake,
|
||||
uint256 totalFeesCollected
|
||||
);
|
||||
|
||||
/// @dev Emitted by MixinFinalizer when an epoch is fully finalized.
|
||||
/// @param epoch The epoch being finalized.
|
||||
/// @param rewardsPaid Total amount of rewards paid out.
|
||||
/// @param rewardsRemaining Rewards left over.
|
||||
event EpochFinalized(
|
||||
uint256 epoch,
|
||||
uint256 rewardsPaid,
|
||||
uint256 rewardsRemaining
|
||||
);
|
||||
|
||||
/// @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.
|
||||
|
||||
@@ -23,14 +23,11 @@ interface IStructs {
|
||||
|
||||
/// @dev Status for a pool that actively traded during the current epoch.
|
||||
/// (see MixinExchangeFees).
|
||||
/// @param poolId Unique Id of staking pool.
|
||||
/// @param feesCollected Fees collected in ETH by this pool in the current epoch.
|
||||
/// @param weightedStake Amount of weighted stake currently held by the pool.
|
||||
struct ActivePool {
|
||||
bytes32 poolId;
|
||||
uint256 feesCollected;
|
||||
uint256 weightedStake;
|
||||
uint256 delegatedStake;
|
||||
}
|
||||
|
||||
/// @dev Encapsulates a balance for the current and next epochs.
|
||||
|
||||
Reference in New Issue
Block a user