@0x/contracts-staking: Starting MBF.

This commit is contained in:
Lawrence Forman
2019-09-10 16:29:33 -04:00
committed by Lawrence Forman
parent 29f4d6918a
commit bbae6b3de2
4 changed files with 220 additions and 3 deletions

View 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);
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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.