@0x/contracts-staking: Got the solidity compiling.
This commit is contained in:
committed by
Lawrence Forman
parent
b57c0a2ebb
commit
294be37afc
@@ -19,7 +19,6 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../libs/LibStakingRichErrors.sol";
|
||||
@@ -90,20 +89,20 @@ contract MixinExchangeFees is
|
||||
// because we only need to remember state in the current epoch and the
|
||||
// epoch prior.
|
||||
uint256 currentEpoch = getCurrentEpoch();
|
||||
mapping (bytes32 => IStructs.ActivePool) activePoolsThisEpoch =
|
||||
mapping (bytes32 => IStructs.ActivePool) storage activePoolsThisEpoch =
|
||||
activePoolsByEpoch[currentEpoch % 2];
|
||||
IStructs.ActivePool memory pool = activePoolsThisEpoch[poolId]
|
||||
IStructs.ActivePool memory pool = activePoolsThisEpoch[poolId];
|
||||
// If the pool was previously inactive in this epoch, initialize it.
|
||||
if (pool.feesCollected) {
|
||||
if (pool.feesCollected == 0) {
|
||||
// Compute weighted stake.
|
||||
uint256 operatorStake = getStakeDelegatedToPoolByOwner(
|
||||
rewardVault.operatorOf(poolId),
|
||||
poolId
|
||||
).currentEpochBalance;
|
||||
pool.weightedStake = operatorStake.safeAdd(
|
||||
totalStakeDelegatedToPool
|
||||
poolStake
|
||||
.safeSub(operatorStake)
|
||||
.safeMul(delegatedStakeWeight)
|
||||
.safeMul(rewardDelegatedStakeWeight)
|
||||
.safeDiv(PPM_DENOMINATOR)
|
||||
);
|
||||
// Compute delegated (non-operator) stake.
|
||||
@@ -127,45 +126,38 @@ contract MixinExchangeFees is
|
||||
activePoolsThisEpoch[poolId] = pool;
|
||||
}
|
||||
|
||||
/// @dev Pays the rebates for to market making pool that was active this epoch,
|
||||
/// then updates the epoch and other time-based periods via the scheduler
|
||||
/// (see MixinScheduler). This is intentionally permissionless, and may be called by anyone.
|
||||
function finalizeFees()
|
||||
external
|
||||
{
|
||||
// distribute fees to market maker pools as a reward
|
||||
(uint256 totalActivePools,
|
||||
uint256 totalFeesCollected,
|
||||
uint256 totalWeightedStake,
|
||||
uint256 totalRewardsPaid,
|
||||
uint256 initialContractBalance,
|
||||
uint256 finalContractBalance) = _distributeFeesAmongMakerPools();
|
||||
emit RewardsPaid(
|
||||
totalActivePools,
|
||||
totalFeesCollected,
|
||||
totalWeightedStake,
|
||||
totalRewardsPaid,
|
||||
initialContractBalance,
|
||||
finalContractBalance
|
||||
);
|
||||
|
||||
_goToNextEpoch();
|
||||
}
|
||||
|
||||
/// @dev Returns the total amount of fees collected thus far, in the current epoch.
|
||||
/// @return Amount of fees.
|
||||
/// @return _totalFeesCollectedThisEpoch Total fees collected this epoch.
|
||||
function getTotalProtocolFeesThisEpoch()
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
returns (uint256 _totalFeesCollectedThisEpoch)
|
||||
{
|
||||
uint256 wethBalance = IEtherToken(WETH_ADDRESS).balanceOf(address(this));
|
||||
return address(this).balance.safeAdd(wethBalance);
|
||||
_totalFeesCollectedThisEpoch = totalFeesCollectedThisEpoch;
|
||||
}
|
||||
|
||||
/// @dev Returns the amount of fees attributed to the input pool this epoch.
|
||||
/// @param poolId Pool Id to query.
|
||||
/// @return feesCollectedByPool Amount of fees collected by the pool this epoch.
|
||||
function getProtocolFeesThisEpochByPool(bytes32 poolId)
|
||||
external
|
||||
view
|
||||
returns (uint256 feesCollected)
|
||||
{
|
||||
uint256 currentEpoch = getCurrentEpoch();
|
||||
// Look up the pool for this epoch. The epoch index is `currentEpoch % 2`
|
||||
// because we only need to remember state in the current epoch and the
|
||||
// epoch prior.
|
||||
IStructs.ActivePool memory pool = activePoolsByEpoch[getCurrentEpoch() % 2][poolId];
|
||||
feesCollected = pool.feesCollected;
|
||||
}
|
||||
|
||||
/// @dev Checks that the protocol fee passed into `payProtocolFee()` is valid.
|
||||
/// @param protocolFeePaid The `protocolFeePaid` parameter to `payProtocolFee.`
|
||||
function _assertValidProtocolFee(uint256 protocolFeePaid) private view {
|
||||
function _assertValidProtocolFee(uint256 protocolFeePaid)
|
||||
private
|
||||
view
|
||||
{
|
||||
if (protocolFeePaid == 0 || (msg.value != protocolFeePaid && msg.value != 0)) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.InvalidProtocolFeePaymentError(
|
||||
protocolFeePaid == 0 ?
|
||||
@@ -176,143 +168,4 @@ contract MixinExchangeFees is
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Pays rewards to market making pools that were active this epoch.
|
||||
/// Each pool receives a portion of the fees generated this epoch (see LibCobbDouglas) that is
|
||||
/// proportional to (i) the fee volume attributed to their pool over the epoch, and
|
||||
/// (ii) the amount of stake provided by the maker and their delegators. Rebates are paid
|
||||
/// into the Reward Vault where they can be withdraw by makers and
|
||||
/// the members of their pool. There will be a small amount of ETH leftover in this contract
|
||||
/// after paying out the rebates; at present, this rolls over into the next epoch. Eventually,
|
||||
/// we plan to deposit this leftover into a DAO managed by the 0x community.
|
||||
/// @return totalActivePools Total active pools this epoch.
|
||||
/// @return totalFeesCollected Total fees collected this epoch, across all active pools.
|
||||
/// @return totalWeightedStake Total weighted stake attributed to each pool. Delegated stake is weighted less.
|
||||
/// @return totalRewardsPaid Total rewards paid out across all active pools.
|
||||
/// @return initialContractBalance Balance of this contract before paying rewards.
|
||||
/// @return finalContractBalance Balance of this contract after paying rewards.
|
||||
function _distributeFeesAmongMakerPools()
|
||||
internal
|
||||
returns (
|
||||
uint256 totalActivePools,
|
||||
uint256 totalFeesCollected,
|
||||
uint256 totalWeightedStake,
|
||||
uint256 totalRewardsPaid,
|
||||
uint256 initialContractBalance,
|
||||
uint256 finalContractBalance
|
||||
)
|
||||
{
|
||||
// step 1/4 - withdraw the entire wrapped ether balance into this contract. WETH
|
||||
// is unwrapped here to keep `payProtocolFee()` calls relatively cheap,
|
||||
// and WETH is only withdrawn if this contract's WETH balance is nonzero.
|
||||
_unwrapWETH();
|
||||
|
||||
// Initialize initial values
|
||||
totalActivePools = activePoolsThisEpoch.length;
|
||||
totalFeesCollected = 0;
|
||||
totalWeightedStake = 0;
|
||||
totalRewardsPaid = 0;
|
||||
initialContractBalance = address(this).balance;
|
||||
finalContractBalance = initialContractBalance;
|
||||
|
||||
// sanity check - is there a balance to payout and were there any active pools?
|
||||
if (initialContractBalance == 0 || totalActivePools == 0) {
|
||||
return (
|
||||
totalActivePools,
|
||||
totalFeesCollected,
|
||||
totalWeightedStake,
|
||||
totalRewardsPaid,
|
||||
initialContractBalance,
|
||||
finalContractBalance
|
||||
);
|
||||
}
|
||||
|
||||
// step 2/4 - compute stats for active maker pools
|
||||
IStructs.ActivePool[] memory activePools = new IStructs.ActivePool[](totalActivePools);
|
||||
uint32 delegatedStakeWeight = rewardDelegatedStakeWeight;
|
||||
for (uint256 i = 0; i != totalActivePools; i++) {
|
||||
bytes32 poolId = activePoolsThisEpoch[i];
|
||||
|
||||
// compute weighted stake
|
||||
uint256 totalStakeDelegatedToPool = getTotalStakeDelegatedToPool(poolId).currentEpochBalance;
|
||||
uint256 stakeHeldByPoolOperator = getStakeDelegatedToPoolByOwner(poolById[poolId].operator, poolId).currentEpochBalance;
|
||||
uint256 weightedStake = stakeHeldByPoolOperator.safeAdd(
|
||||
totalStakeDelegatedToPool
|
||||
.safeSub(stakeHeldByPoolOperator)
|
||||
.safeMul(delegatedStakeWeight)
|
||||
.safeDiv(PPM_DENOMINATOR)
|
||||
);
|
||||
|
||||
// store pool stats
|
||||
activePools[i].poolId = poolId;
|
||||
activePools[i].feesCollected = protocolFeesThisEpochByPool[poolId];
|
||||
activePools[i].weightedStake = weightedStake;
|
||||
activePools[i].delegatedStake = totalStakeDelegatedToPool;
|
||||
|
||||
// update cumulative amounts
|
||||
totalFeesCollected = totalFeesCollected.safeAdd(activePools[i].feesCollected);
|
||||
totalWeightedStake = totalWeightedStake.safeAdd(activePools[i].weightedStake);
|
||||
}
|
||||
|
||||
// sanity check - this is a gas optimization that can be used because we assume a non-zero
|
||||
// split between stake and fees generated in the cobb-douglas computation (see below).
|
||||
if (totalFeesCollected == 0) {
|
||||
return (
|
||||
totalActivePools,
|
||||
totalFeesCollected,
|
||||
totalWeightedStake,
|
||||
totalRewardsPaid,
|
||||
initialContractBalance,
|
||||
finalContractBalance
|
||||
);
|
||||
}
|
||||
|
||||
// step 3/4 - record reward for each pool
|
||||
for (uint256 i = 0; i != totalActivePools; i++) {
|
||||
// compute reward using cobb-douglas formula
|
||||
uint256 reward = LibCobbDouglas.cobbDouglas(
|
||||
initialContractBalance,
|
||||
activePools[i].feesCollected,
|
||||
totalFeesCollected,
|
||||
totalWeightedStake != 0 ? activePools[i].weightedStake : 1, // only rewards are accounted for if no one has staked
|
||||
totalWeightedStake != 0 ? totalWeightedStake : 1, // this is to avoid divide-by-zero in cobb douglas
|
||||
cobbDouglasAlphaNumerator,
|
||||
cobbDouglasAlphaDenominator
|
||||
);
|
||||
|
||||
// pay reward to pool
|
||||
_handleStakingPoolReward(
|
||||
activePools[i].poolId,
|
||||
reward,
|
||||
activePools[i].delegatedStake,
|
||||
currentEpoch
|
||||
);
|
||||
|
||||
// clear state for gas refunds
|
||||
protocolFeesThisEpochByPool[activePools[i].poolId] = 0;
|
||||
activePoolsThisEpoch[i] = 0;
|
||||
}
|
||||
activePoolsThisEpoch.length = 0;
|
||||
|
||||
// step 4/4 send total payout to vault
|
||||
|
||||
// Sanity check rewards calculation
|
||||
if (totalRewardsPaid > initialContractBalance) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MiscalculatedRewardsError(
|
||||
totalRewardsPaid,
|
||||
initialContractBalance
|
||||
));
|
||||
}
|
||||
|
||||
finalContractBalance = address(this).balance;
|
||||
|
||||
return (
|
||||
totalActivePools,
|
||||
totalFeesCollected,
|
||||
totalWeightedStake,
|
||||
totalRewardsPaid,
|
||||
initialContractBalance,
|
||||
finalContractBalance
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,8 @@ contract MixinStorage is
|
||||
// Rebate Vault (stores rewards for pools before they are moved to the eth vault on a per-user basis)
|
||||
IStakingPoolRewardVault public rewardVault;
|
||||
|
||||
/* Tweakable parameters */
|
||||
|
||||
// Minimum seconds between epochs.
|
||||
uint256 public epochDurationInSeconds;
|
||||
|
||||
@@ -139,26 +141,34 @@ contract MixinStorage is
|
||||
|
||||
/// @dev The total fees collected in the current epoch, built up iteratively
|
||||
/// in `payProtocolFee()`.
|
||||
uint256 totalFeesCollectedThisEpoch;
|
||||
uint256 internal totalFeesCollectedThisEpoch;
|
||||
|
||||
/// @dev The total weighted stake in the current epoch, built up iteratively
|
||||
/// in `payProtocolFee()`.
|
||||
uint256 totalWeightedStakeThisEpoch;
|
||||
uint256 internal totalWeightedStakeThisEpoch;
|
||||
|
||||
/// @dev State information for each active pool in an epoch.
|
||||
/// In practice, we only store state for `currentEpoch % 2`.
|
||||
mapping(uint256 => mapping(bytes32 => IStructs.ActivePool)) activePoolsByEpoch;
|
||||
mapping(uint256 => mapping(bytes32 => IStructs.ActivePool)) internal activePoolsByEpoch;
|
||||
|
||||
/// @dev Number of pools activated in the current epoch.
|
||||
uint256 numActivePoolsThisEpoch;
|
||||
uint256 internal 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;
|
||||
uint256 internal unfinalizedRewardsAvailable;
|
||||
|
||||
/// @dev The number of active pools in the last epoch that have yet to be
|
||||
/// finalized through `finalizePools()`.
|
||||
uint256 unfinalizedPoolsRemaining;
|
||||
uint256 internal unfinalizedPoolsRemaining;
|
||||
|
||||
/// @dev The total fees collected for the epoch being finalized.
|
||||
uint256 unfinalizedTotalFeesCollected;
|
||||
uint256 internal unfinalizedTotalFeesCollected;
|
||||
|
||||
/// @dev The total fees collected for the epoch being finalized.
|
||||
uint256 unfinalizedTotalWeightedStake;
|
||||
uint256 internal unfinalizedTotalWeightedStake;
|
||||
|
||||
/// @dev How many rewards were paid at the end of finalization.
|
||||
uint256 totalRewardsPaidLastEpoch;
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ interface IStakingEvents {
|
||||
uint256 epoch,
|
||||
uint256 numActivePools,
|
||||
uint256 rewardsAvailable,
|
||||
uint256 totalWeightedStake,
|
||||
uint256 totalFeesCollected
|
||||
uint256 totalFeesCollected,
|
||||
uint256 totalWeightedStake
|
||||
);
|
||||
|
||||
/// @dev Emitted by MixinFinalizer when an epoch is fully finalized.
|
||||
@@ -81,9 +81,9 @@ interface IStakingEvents {
|
||||
/// @param poolId The pool's ID.
|
||||
/// @param reward Amount of reward paid.
|
||||
event RewardsPaid(
|
||||
uint255 epoch,
|
||||
uint256 epoch,
|
||||
bytes32 poolId,
|
||||
uint255 reward
|
||||
uint256 reward
|
||||
);
|
||||
|
||||
/// @dev Emitted whenever staking parameters are changed via the `setParams()` function.
|
||||
|
||||
@@ -162,6 +162,10 @@ library LibStakingRichErrors {
|
||||
bytes4 internal constant CUMULATIVE_REWARD_INTERVAL_ERROR_SELECTOR =
|
||||
0x1f806d55;
|
||||
|
||||
// bytes4(keccak256("PreviousEpochNotFinalizedError(uint256,uint256)"))
|
||||
bytes4 internal constant PREVIOUS_EPOCH_NOT_FINALIZED_ERROR_SELECTOR =
|
||||
0x614b800a;
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
function MiscalculatedRewardsError(
|
||||
uint256 totalRewardsPaid,
|
||||
@@ -491,4 +495,19 @@ library LibStakingRichErrors {
|
||||
endEpoch
|
||||
);
|
||||
}
|
||||
|
||||
function PreviousEpochNotFinalizedError(
|
||||
uint256 unfinalizedEpoch,
|
||||
uint256 unfinalizedPoolsRemaining
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
PREVIOUS_EPOCH_NOT_FINALIZED_ERROR_SELECTOR,
|
||||
unfinalizedEpoch,
|
||||
unfinalizedPoolsRemaining
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,19 +19,20 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
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 "../immutable/MixinDeploymentConstants.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";
|
||||
import "./MixinScheduler.sol";
|
||||
|
||||
|
||||
/// @dev This mixin contains functions related to finalizing epochs.
|
||||
@@ -42,6 +43,7 @@ import "./MixinExchangeManager.sol";
|
||||
contract MixinFinalizer is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
MixinDeploymentConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolRewardVault,
|
||||
@@ -65,7 +67,7 @@ contract MixinFinalizer is
|
||||
uint256 closingEpoch = getCurrentEpoch();
|
||||
// Make sure the previous epoch has been fully finalized.
|
||||
if (unfinalizedPoolsRemaining != 0) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.PreviousEpochNotFinalized(
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.PreviousEpochNotFinalizedError(
|
||||
closingEpoch.safeSub(1),
|
||||
unfinalizedPoolsRemaining
|
||||
));
|
||||
@@ -75,20 +77,20 @@ contract MixinFinalizer is
|
||||
// Populate finalization state.
|
||||
unfinalizedPoolsRemaining = numActivePoolsThisEpoch;
|
||||
unfinalizedRewardsAvailable = address(this).balance;
|
||||
unfinalizedTotalFeesCollected = totalFeesCollected;
|
||||
unfinalizedTotalWeightedStake = totalWeightedStake;
|
||||
totalRewardsPaid = 0;
|
||||
unfinalizedTotalFeesCollected = totalFeesCollectedThisEpoch;
|
||||
unfinalizedTotalWeightedStake = totalWeightedStakeThisEpoch;
|
||||
totalRewardsPaidLastEpoch = 0;
|
||||
// Emit an event.
|
||||
emit EpochEnded(
|
||||
closingEpoch,
|
||||
numActivePoolsThisEpoch,
|
||||
rewardsAvailable,
|
||||
totalWeightedStake,
|
||||
totalFeesCollected
|
||||
unfinalizedRewardsAvailable,
|
||||
unfinalizedTotalFeesCollected,
|
||||
unfinalizedTotalWeightedStake
|
||||
);
|
||||
// Reset current epoch state.
|
||||
totalFeesCollected = 0;
|
||||
totalWeightedStake = 0;
|
||||
totalFeesCollectedThisEpoch = 0;
|
||||
totalWeightedStakeThisEpoch = 0;
|
||||
numActivePoolsThisEpoch = 0;
|
||||
// Advance the epoch. This will revert if not enough time has passed.
|
||||
_goToNextEpoch();
|
||||
@@ -109,18 +111,19 @@ contract MixinFinalizer is
|
||||
/// @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)
|
||||
function finalizePools(bytes32[] calldata poolIds)
|
||||
external
|
||||
returns (uint256 rewardsPaid, uint256 _unfinalizedPoolsRemaining)
|
||||
{
|
||||
uint256 epoch = getCurrentEpoch().safeSub(1);
|
||||
uint256 epoch = getCurrentEpoch();
|
||||
uint256 priorEpoch = epoch.safeSub(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];
|
||||
activePoolsByEpoch[priorEpoch % 2];
|
||||
for (uint256 i = 0; i < numPoolIds && poolsRemaining != 0; i++) {
|
||||
bytes32 poolId = poolIds[i];
|
||||
IStructs.ActivePool memory pool = activePools[poolId];
|
||||
@@ -128,7 +131,10 @@ contract MixinFinalizer is
|
||||
if (pool.feesCollected != 0) {
|
||||
// Credit the pool with rewards.
|
||||
// We will transfer the total rewards to the vault at the end.
|
||||
rewardsPaid = rewardsPaid.safeAdd(_creditRewardsToPool(poolId, pool));
|
||||
// Note that we credit the pool the rewards at the current epoch
|
||||
// even though they were earned in the prior epoch.
|
||||
uint256 reward = _creditRewardToPool(epoch, poolId, pool);
|
||||
rewardsPaid = rewardsPaid.safeAdd(reward);
|
||||
// Clear the pool state so we don't finalize it again,
|
||||
// and to recoup some gas.
|
||||
activePools[poolId] = IStructs.ActivePool(0, 0, 0);
|
||||
@@ -147,62 +153,13 @@ contract MixinFinalizer is
|
||||
// finalized.
|
||||
if (poolsRemaining == 0) {
|
||||
emit EpochFinalized(
|
||||
epoch,
|
||||
priorEpoch,
|
||||
totalRewardsPaidLastEpoch,
|
||||
unfinalizedRewardsAvailable.safeSub(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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Converts the entire WETH balance of the contract into ETH.
|
||||
function _unwrapWETH() private {
|
||||
uint256 wethBalance = IEtherToken(WETH_ADDRESS).balanceOf(address(this));
|
||||
if (wethBalance != 0) {
|
||||
IEtherToken(WETH_ADDRESS).withdraw(wethBalance);
|
||||
}
|
||||
}
|
||||
|
||||
/// @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
|
||||
@@ -267,4 +224,54 @@ contract MixinFinalizer is
|
||||
// Multiply the above with totalRewards.
|
||||
ownerRewards = LibFixedMath._uintMul(n, totalRewards);
|
||||
}
|
||||
|
||||
/// @dev Computes the reward owed to a pool during finalization and
|
||||
/// credits it in the RewardVault.
|
||||
/// @param epoch The epoch being finalized.
|
||||
/// @param poolId The pool's ID.
|
||||
/// @param pool The pool.
|
||||
/// @return rewards Amount of rewards for this pool.
|
||||
function _creditRewardToPool(
|
||||
uint256 epoch,
|
||||
bytes32 poolId,
|
||||
IStructs.ActivePool memory pool
|
||||
)
|
||||
private
|
||||
returns (uint256 reward)
|
||||
{
|
||||
// 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Converts the entire WETH balance of the contract into ETH.
|
||||
function _unwrapWETH() private {
|
||||
uint256 wethBalance = IEtherToken(WETH_ADDRESS).balanceOf(address(this));
|
||||
if (wethBalance != 0) {
|
||||
IEtherToken(WETH_ADDRESS).withdraw(wethBalance);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,12 +88,6 @@ contract TestStorageLayout is
|
||||
if sub(currentEpochStartTimeInSeconds_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(protocolFeesThisEpochByPool_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(activePoolsThisEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(_cumulativeRewardsByPool_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
@@ -129,6 +123,33 @@ contract TestStorageLayout is
|
||||
|
||||
if sub(cobbDouglasAlphaDenominator_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(totalFeesCollectedThisEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(totalWeightedStakeThisEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(activePoolsByEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(numActivePoolsThisEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(unfinalizedRewardsAvailable_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(unfinalizedPoolsRemaining_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(unfinalizedTotalFeesCollected_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(unfinalizedTotalWeightedStake_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(totalRewardsPaidLastEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import * as MixinCumulativeRewards from '../generated-artifacts/MixinCumulativeR
|
||||
import * as MixinDeploymentConstants from '../generated-artifacts/MixinDeploymentConstants.json';
|
||||
import * as MixinExchangeFees from '../generated-artifacts/MixinExchangeFees.json';
|
||||
import * as MixinExchangeManager from '../generated-artifacts/MixinExchangeManager.json';
|
||||
import * as MixinFinalizer from '../generated-artifacts/MixinFinalizer.json';
|
||||
import * as MixinParams from '../generated-artifacts/MixinParams.json';
|
||||
import * as MixinScheduler from '../generated-artifacts/MixinScheduler.json';
|
||||
import * as MixinStake from '../generated-artifacts/MixinStake.json';
|
||||
@@ -63,6 +64,7 @@ export const artifacts = {
|
||||
StakingProxy: StakingProxy as ContractArtifact,
|
||||
MixinExchangeFees: MixinExchangeFees as ContractArtifact,
|
||||
MixinExchangeManager: MixinExchangeManager as ContractArtifact,
|
||||
MixinFinalizer: MixinFinalizer as ContractArtifact,
|
||||
MixinConstants: MixinConstants as ContractArtifact,
|
||||
MixinDeploymentConstants: MixinDeploymentConstants as ContractArtifact,
|
||||
MixinStorage: MixinStorage as ContractArtifact,
|
||||
|
||||
@@ -25,6 +25,7 @@ export * from '../generated-wrappers/mixin_cumulative_rewards';
|
||||
export * from '../generated-wrappers/mixin_deployment_constants';
|
||||
export * from '../generated-wrappers/mixin_exchange_fees';
|
||||
export * from '../generated-wrappers/mixin_exchange_manager';
|
||||
export * from '../generated-wrappers/mixin_finalizer';
|
||||
export * from '../generated-wrappers/mixin_params';
|
||||
export * from '../generated-wrappers/mixin_scheduler';
|
||||
export * from '../generated-wrappers/mixin_stake';
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"generated-artifacts/MixinDeploymentConstants.json",
|
||||
"generated-artifacts/MixinExchangeFees.json",
|
||||
"generated-artifacts/MixinExchangeManager.json",
|
||||
"generated-artifacts/MixinFinalizer.json",
|
||||
"generated-artifacts/MixinParams.json",
|
||||
"generated-artifacts/MixinScheduler.json",
|
||||
"generated-artifacts/MixinStake.json",
|
||||
|
||||
Reference in New Issue
Block a user