Merge pull request #2276 from 0xProject/cleanup/staking/finalizationDataStructures
Refactored finalization state.
This commit is contained in:
		@@ -81,49 +81,46 @@ contract MixinExchangeFees is
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Look up the pool for this epoch.
 | 
					        // Look up the pool stats and aggregated stats for this epoch.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        uint256 currentEpoch_ = currentEpoch;
 | 
					        uint256 currentEpoch_ = currentEpoch;
 | 
				
			||||||
        mapping (bytes32 => IStructs.ActivePool) storage activePoolsThisEpoch =
 | 
					        IStructs.PoolStats storage poolStatsPtr = poolStatsByEpoch[poolId][currentEpoch_];
 | 
				
			||||||
            _getActivePoolsFromEpoch(currentEpoch_);
 | 
					        IStructs.AggregatedStats storage aggregatedStatsPtr = aggregatedStatsByEpoch[currentEpoch_];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        IStructs.ActivePool memory pool = activePoolsThisEpoch[poolId];
 | 
					        // Perform some initialization if this is the pool's first protocol fee in this epoch.
 | 
				
			||||||
 | 
					        uint256 feesCollectedByPool = poolStatsPtr.feesCollected;
 | 
				
			||||||
        // If the pool was previously inactive in this epoch, initialize it.
 | 
					        if (feesCollectedByPool == 0) {
 | 
				
			||||||
        if (pool.feesCollected == 0) {
 | 
					 | 
				
			||||||
            // Compute member and total weighted stake.
 | 
					            // Compute member and total weighted stake.
 | 
				
			||||||
            (pool.membersStake, pool.weightedStake) = _computeMembersAndWeightedStake(poolId, poolStake);
 | 
					            (uint256 membersStakeInPool, uint256 weightedStakeInPool) = _computeMembersAndWeightedStake(poolId, poolStake);
 | 
				
			||||||
 | 
					            poolStatsPtr.membersStake = membersStakeInPool;
 | 
				
			||||||
 | 
					            poolStatsPtr.weightedStake = weightedStakeInPool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Increase the total weighted stake.
 | 
					            // Increase the total weighted stake.
 | 
				
			||||||
            totalWeightedStakeThisEpoch = totalWeightedStakeThisEpoch.safeAdd(pool.weightedStake);
 | 
					            aggregatedStatsPtr.totalWeightedStake = aggregatedStatsPtr.totalWeightedStake.safeAdd(weightedStakeInPool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Increase the number of active pools.
 | 
					            // Increase the number of pools to finalize.
 | 
				
			||||||
            numActivePoolsThisEpoch = numActivePoolsThisEpoch.safeAdd(1);
 | 
					            aggregatedStatsPtr.numPoolsToFinalize = aggregatedStatsPtr.numPoolsToFinalize.safeAdd(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Emit an event so keepers know what pools to pass into
 | 
					            // Emit an event so keepers know what pools earned rewards this epoch.
 | 
				
			||||||
            // `finalize()`.
 | 
					            emit StakingPoolEarnedRewardsInEpoch(currentEpoch_, poolId);
 | 
				
			||||||
            emit StakingPoolActivated(currentEpoch_, poolId);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Credit the fees to the pool.
 | 
					        // Credit the fees to the pool.
 | 
				
			||||||
        pool.feesCollected = pool.feesCollected.safeAdd(protocolFeePaid);
 | 
					        poolStatsPtr.feesCollected = feesCollectedByPool.safeAdd(protocolFeePaid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Increase the total fees collected this epoch.
 | 
					        // Increase the total fees collected this epoch.
 | 
				
			||||||
        totalFeesCollectedThisEpoch = totalFeesCollectedThisEpoch.safeAdd(protocolFeePaid);
 | 
					        aggregatedStatsPtr.totalFeesCollected = aggregatedStatsPtr.totalFeesCollected.safeAdd(protocolFeePaid);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Store the pool.
 | 
					 | 
				
			||||||
        activePoolsThisEpoch[poolId] = pool;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Get information on an active staking pool in this epoch.
 | 
					    /// @dev Get stats on a staking pool in this epoch.
 | 
				
			||||||
    /// @param poolId Pool Id to query.
 | 
					    /// @param poolId Pool Id to query.
 | 
				
			||||||
    /// @return pool ActivePool struct.
 | 
					    /// @return PoolStats struct for pool id.
 | 
				
			||||||
    function getActiveStakingPoolThisEpoch(bytes32 poolId)
 | 
					    function getStakingPoolStatsThisEpoch(bytes32 poolId)
 | 
				
			||||||
        external
 | 
					        external
 | 
				
			||||||
        view
 | 
					        view
 | 
				
			||||||
        returns (IStructs.ActivePool memory pool)
 | 
					        returns (IStructs.PoolStats memory)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        pool = _getActivePoolFromEpoch(currentEpoch, poolId);
 | 
					        return poolStatsByEpoch[poolId][currentEpoch];
 | 
				
			||||||
        return pool;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Computes the members and weighted stake for a pool at the current
 | 
					    /// @dev Computes the members and weighted stake for a pool at the current
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,25 +101,15 @@ contract MixinStorage is
 | 
				
			|||||||
    // Denominator for cobb douglas alpha factor.
 | 
					    // Denominator for cobb douglas alpha factor.
 | 
				
			||||||
    uint32 public cobbDouglasAlphaDenominator;
 | 
					    uint32 public cobbDouglasAlphaDenominator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Finalization states */
 | 
					    /* State for finalization */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev The total fees collected in the current epoch, built up iteratively
 | 
					    /// @dev Stats for each pool that generated fees with sufficient stake to earn rewards.
 | 
				
			||||||
    ///      in `payProtocolFee()`.
 | 
					    ///      See `_minimumPoolStake` in MixinParams.
 | 
				
			||||||
    uint256 public totalFeesCollectedThisEpoch;
 | 
					    mapping (bytes32 => mapping (uint256 => IStructs.PoolStats)) public poolStatsByEpoch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev The total weighted stake in the current epoch, built up iteratively
 | 
					    /// @dev Aggregated stats across all pools that generated fees with sufficient stake to earn rewards.
 | 
				
			||||||
    ///      in `payProtocolFee()`.
 | 
					    ///      See `_minimumPoolStake` in MixinParams.
 | 
				
			||||||
    uint256 public totalWeightedStakeThisEpoch;
 | 
					    mapping (uint256 => IStructs.AggregatedStats) public aggregatedStatsByEpoch;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// @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)) internal _activePoolsByEpoch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// @dev Number of pools activated in the current epoch.
 | 
					 | 
				
			||||||
    uint256 public numActivePoolsThisEpoch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// @dev State for unfinalized rewards.
 | 
					 | 
				
			||||||
    IStructs.UnfinalizedState public unfinalizedState;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev The WETH balance of this contract that is reserved for pool reward payouts.
 | 
					    /// @dev The WETH balance of this contract that is reserved for pool reward payouts.
 | 
				
			||||||
    uint256 public wethReservedForPoolRewards;
 | 
					    uint256 public wethReservedForPoolRewards;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,24 +43,23 @@ interface IStakingEvents {
 | 
				
			|||||||
        address exchangeAddress
 | 
					        address exchangeAddress
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Emitted by MixinExchangeFees when a pool pays protocol fees
 | 
					    /// @dev Emitted by MixinExchangeFees when a pool starts earning rewards in an epoch.
 | 
				
			||||||
    ///      for the first time in an epoch.
 | 
					    /// @param epoch The epoch in which the pool earned rewards.
 | 
				
			||||||
    /// @param epoch The epoch in which the pool was activated.
 | 
					 | 
				
			||||||
    /// @param poolId The ID of the pool.
 | 
					    /// @param poolId The ID of the pool.
 | 
				
			||||||
    event StakingPoolActivated(
 | 
					    event StakingPoolEarnedRewardsInEpoch(
 | 
				
			||||||
        uint256 indexed epoch,
 | 
					        uint256 indexed epoch,
 | 
				
			||||||
        bytes32 indexed poolId
 | 
					        bytes32 indexed poolId
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @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 epoch that ended.
 | 
				
			||||||
    /// @param numActivePools Number of active pools in the closing epoch.
 | 
					    /// @param numPoolsToFinalize Number of pools that earned rewards during `epoch` and must be finalized.
 | 
				
			||||||
    /// @param rewardsAvailable Rewards available to all active pools.
 | 
					    /// @param rewardsAvailable Rewards available to all pools that earned rewards during `epoch`.
 | 
				
			||||||
    /// @param totalWeightedStake Total weighted stake across all active pools.
 | 
					    /// @param totalWeightedStake Total weighted stake across all pools that earned rewards during `epoch`.
 | 
				
			||||||
    /// @param totalFeesCollected Total fees collected across all active pools.
 | 
					    /// @param totalFeesCollected Total fees collected across all pools that earned rewards during `epoch`.
 | 
				
			||||||
    event EpochEnded(
 | 
					    event EpochEnded(
 | 
				
			||||||
        uint256 indexed epoch,
 | 
					        uint256 indexed epoch,
 | 
				
			||||||
        uint256 numActivePools,
 | 
					        uint256 numPoolsToFinalize,
 | 
				
			||||||
        uint256 rewardsAvailable,
 | 
					        uint256 rewardsAvailable,
 | 
				
			||||||
        uint256 totalFeesCollected,
 | 
					        uint256 totalFeesCollected,
 | 
				
			||||||
        uint256 totalWeightedStake
 | 
					        uint256 totalWeightedStake
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,11 +65,6 @@ interface IStorage {
 | 
				
			|||||||
        view
 | 
					        view
 | 
				
			||||||
        returns (uint256);
 | 
					        returns (uint256);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function activePoolsThisEpoch()
 | 
					 | 
				
			||||||
        external
 | 
					 | 
				
			||||||
        view
 | 
					 | 
				
			||||||
        returns (bytes32[] memory);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    function validExchanges(address exchangeAddress)
 | 
					    function validExchanges(address exchangeAddress)
 | 
				
			||||||
        external
 | 
					        external
 | 
				
			||||||
        view
 | 
					        view
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,29 +29,27 @@ interface IStructs {
 | 
				
			|||||||
        uint96 lastSetTimestamp;
 | 
					        uint96 lastSetTimestamp;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Status for a pool that actively traded during the current epoch.
 | 
					    /// @dev Stats for a pool that earned rewards.
 | 
				
			||||||
    /// (see MixinExchangeFees).
 | 
					 | 
				
			||||||
    /// @param feesCollected Fees collected in ETH by this pool.
 | 
					    /// @param feesCollected Fees collected in ETH by this pool.
 | 
				
			||||||
    /// @param weightedStake Amount of weighted stake in the pool.
 | 
					    /// @param weightedStake Amount of weighted stake in the pool.
 | 
				
			||||||
    /// @param membersStake Amount of non-operator stake in the pool.
 | 
					    /// @param membersStake Amount of non-operator stake in the pool.
 | 
				
			||||||
    struct ActivePool {
 | 
					    struct PoolStats {
 | 
				
			||||||
        uint256 feesCollected;
 | 
					        uint256 feesCollected;
 | 
				
			||||||
        uint256 weightedStake;
 | 
					        uint256 weightedStake;
 | 
				
			||||||
        uint256 membersStake;
 | 
					        uint256 membersStake;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Holds state for unfinalized epoch rewards.
 | 
					    /// @dev Holds stats aggregated across a set of pools.
 | 
				
			||||||
    /// @param rewardsAvailable Rewards (ETH) available to the epoch
 | 
					    /// @param rewardsAvailable Rewards (ETH) available to the epoch
 | 
				
			||||||
    ///        being finalized (the previous epoch). This is simply the balance
 | 
					    ///        being finalized (the previous epoch). This is simply the balance
 | 
				
			||||||
    ///        of the contract at the end of the epoch.
 | 
					    ///        of the contract at the end of the epoch.
 | 
				
			||||||
    /// @param poolsRemaining The number of active pools in the last
 | 
					    /// @param numPoolsToFinalize The number of pools that have yet to be finalized through `finalizePools()`.
 | 
				
			||||||
    ///        epoch that have yet to be finalized through `finalizePools()`.
 | 
					 | 
				
			||||||
    /// @param totalFeesCollected The total fees collected for the epoch being finalized.
 | 
					    /// @param totalFeesCollected The total fees collected for the epoch being finalized.
 | 
				
			||||||
    /// @param totalWeightedStake The total fees collected for the epoch being finalized.
 | 
					    /// @param totalWeightedStake The total fees collected for the epoch being finalized.
 | 
				
			||||||
    /// @param totalRewardsFinalized Amount of rewards that have been paid during finalization.
 | 
					    /// @param totalRewardsFinalized Amount of rewards that have been paid during finalization.
 | 
				
			||||||
    struct UnfinalizedState {
 | 
					    struct AggregatedStats {
 | 
				
			||||||
        uint256 rewardsAvailable;
 | 
					        uint256 rewardsAvailable;
 | 
				
			||||||
        uint256 poolsRemaining;
 | 
					        uint256 numPoolsToFinalize;
 | 
				
			||||||
        uint256 totalFeesCollected;
 | 
					        uint256 totalFeesCollected;
 | 
				
			||||||
        uint256 totalWeightedStake;
 | 
					        uint256 totalWeightedStake;
 | 
				
			||||||
        uint256 totalRewardsFinalized;
 | 
					        uint256 totalRewardsFinalized;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,11 +33,9 @@ library LibCobbDouglas {
 | 
				
			|||||||
    ///         0 <= alphaNumerator / alphaDenominator <= 1
 | 
					    ///         0 <= alphaNumerator / alphaDenominator <= 1
 | 
				
			||||||
    /// @param totalRewards collected over an epoch.
 | 
					    /// @param totalRewards collected over an epoch.
 | 
				
			||||||
    /// @param fees Fees attributed to the the staking pool.
 | 
					    /// @param fees Fees attributed to the the staking pool.
 | 
				
			||||||
    /// @param totalFees Total fees collected across all active staking pools in
 | 
					    /// @param totalFees Total fees collected across all pools that earned rewards.
 | 
				
			||||||
    ///        the epoch.
 | 
					 | 
				
			||||||
    /// @param stake Stake attributed to the staking pool.
 | 
					    /// @param stake Stake attributed to the staking pool.
 | 
				
			||||||
    /// @param totalStake Total stake across all active staking pools in the
 | 
					    /// @param totalStake Total stake across all pools that earned rewards.
 | 
				
			||||||
    ///        epoch.
 | 
					 | 
				
			||||||
    /// @param alphaNumerator Numerator of `alpha` in the cobb-douglas function.
 | 
					    /// @param alphaNumerator Numerator of `alpha` in the cobb-douglas function.
 | 
				
			||||||
    /// @param alphaDenominator Denominator of `alpha` in the cobb-douglas
 | 
					    /// @param alphaDenominator Denominator of `alpha` in the cobb-douglas
 | 
				
			||||||
    ///        function.
 | 
					    ///        function.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,7 +72,7 @@ contract MixinStakeBalances is
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// @dev Returns the total stake for a given staker.
 | 
					    /// @dev Returns the total stake for a given staker.
 | 
				
			||||||
    /// @param staker of stake.
 | 
					    /// @param staker of stake.
 | 
				
			||||||
    /// @return Total active stake for staker.
 | 
					    /// @return Total ZRX staked by `staker`.
 | 
				
			||||||
    function getTotalStake(address staker)
 | 
					    function getTotalStake(address staker)
 | 
				
			||||||
        public
 | 
					        public
 | 
				
			||||||
        view
 | 
					        view
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,93 +35,83 @@ contract MixinFinalizer is
 | 
				
			|||||||
    /// @dev Begins a new epoch, preparing the prior one for finalization.
 | 
					    /// @dev Begins a new epoch, preparing the prior one for finalization.
 | 
				
			||||||
    ///      Throws if not enough time has passed between epochs or if the
 | 
					    ///      Throws if not enough time has passed between epochs or if the
 | 
				
			||||||
    ///      previous epoch was not fully finalized.
 | 
					    ///      previous epoch was not fully finalized.
 | 
				
			||||||
    ///      If there were no active pools in the closing epoch, the epoch
 | 
					    /// @return numPoolsToFinalize The number of unfinalized pools.
 | 
				
			||||||
    ///      will be instantly finalized here. Otherwise, `finalizePool()`
 | 
					 | 
				
			||||||
    ///      should be called on each active pool afterwards.
 | 
					 | 
				
			||||||
    /// @return poolsRemaining The number of unfinalized pools.
 | 
					 | 
				
			||||||
    function endEpoch()
 | 
					    function endEpoch()
 | 
				
			||||||
        external
 | 
					        external
 | 
				
			||||||
        returns (uint256 poolsRemaining)
 | 
					        returns (uint256)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        uint256 closingEpoch = currentEpoch;
 | 
					        uint256 currentEpoch_ = currentEpoch;
 | 
				
			||||||
        IStructs.UnfinalizedState memory state = unfinalizedState;
 | 
					        uint256 prevEpoch = currentEpoch_.safeSub(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Make sure the previous epoch has been fully finalized.
 | 
					        // Make sure the previous epoch has been fully finalized.
 | 
				
			||||||
        if (state.poolsRemaining != 0) {
 | 
					        uint256 numPoolsToFinalizeFromPrevEpoch = aggregatedStatsByEpoch[prevEpoch].numPoolsToFinalize;
 | 
				
			||||||
 | 
					        if (numPoolsToFinalizeFromPrevEpoch != 0) {
 | 
				
			||||||
            LibRichErrors.rrevert(
 | 
					            LibRichErrors.rrevert(
 | 
				
			||||||
                LibStakingRichErrors.PreviousEpochNotFinalizedError(
 | 
					                LibStakingRichErrors.PreviousEpochNotFinalizedError(
 | 
				
			||||||
                    closingEpoch.safeSub(1),
 | 
					                    prevEpoch,
 | 
				
			||||||
                    state.poolsRemaining
 | 
					                    numPoolsToFinalizeFromPrevEpoch
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Convert all ETH to WETH
 | 
					        // Convert all ETH to WETH; the WETH balance of this contract is the total rewards.
 | 
				
			||||||
        _wrapEth();
 | 
					        _wrapEth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set up unfinalized state.
 | 
					        // Load aggregated stats for the epoch we're ending.
 | 
				
			||||||
        state.rewardsAvailable = _getAvailableWethBalance();
 | 
					        aggregatedStatsByEpoch[currentEpoch_].rewardsAvailable = _getAvailableWethBalance();
 | 
				
			||||||
        state.poolsRemaining = poolsRemaining = numActivePoolsThisEpoch;
 | 
					        IStructs.AggregatedStats memory aggregatedStats = aggregatedStatsByEpoch[currentEpoch_];
 | 
				
			||||||
        state.totalFeesCollected = totalFeesCollectedThisEpoch;
 | 
					 | 
				
			||||||
        state.totalWeightedStake = totalWeightedStakeThisEpoch;
 | 
					 | 
				
			||||||
        state.totalRewardsFinalized = 0;
 | 
					 | 
				
			||||||
        unfinalizedState = state;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Emit an event.
 | 
					        // Emit an event.
 | 
				
			||||||
        emit EpochEnded(
 | 
					        emit EpochEnded(
 | 
				
			||||||
            closingEpoch,
 | 
					            currentEpoch_,
 | 
				
			||||||
            state.poolsRemaining,
 | 
					            aggregatedStats.numPoolsToFinalize,
 | 
				
			||||||
            state.rewardsAvailable,
 | 
					            aggregatedStats.rewardsAvailable,
 | 
				
			||||||
            state.totalFeesCollected,
 | 
					            aggregatedStats.totalFeesCollected,
 | 
				
			||||||
            state.totalWeightedStake
 | 
					            aggregatedStats.totalWeightedStake
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Reset current epoch state.
 | 
					 | 
				
			||||||
        totalFeesCollectedThisEpoch = 0;
 | 
					 | 
				
			||||||
        totalWeightedStakeThisEpoch = 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, the epoch is already finalized.
 | 
					        // If there are no pools to finalize then the epoch is finalized.
 | 
				
			||||||
        if (poolsRemaining == 0) {
 | 
					        if (aggregatedStats.numPoolsToFinalize == 0) {
 | 
				
			||||||
            emit EpochFinalized(closingEpoch, 0, state.rewardsAvailable);
 | 
					            emit EpochFinalized(currentEpoch_, 0, aggregatedStats.rewardsAvailable);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return aggregatedStats.numPoolsToFinalize;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Instantly finalizes a single pool that was active in the previous
 | 
					    /// @dev Instantly finalizes a single pool that earned rewards in the previous
 | 
				
			||||||
    ///      epoch, crediting it rewards for members and withdrawing operator's
 | 
					    ///      epoch, crediting it rewards for members and withdrawing operator's
 | 
				
			||||||
    ///      rewards as WETH. This can be called by internal functions that need
 | 
					    ///      rewards as WETH. This can be called by internal functions that need
 | 
				
			||||||
    ///      to finalize a pool immediately. Does nothing if the pool is already
 | 
					    ///      to finalize a pool immediately. Does nothing if the pool is already
 | 
				
			||||||
    ///      finalized or was not active in the previous epoch.
 | 
					    ///      finalized or did not earn rewards in the previous epoch.
 | 
				
			||||||
    /// @param poolId The pool ID to finalize.
 | 
					    /// @param poolId The pool ID to finalize.
 | 
				
			||||||
    function finalizePool(bytes32 poolId)
 | 
					    function finalizePool(bytes32 poolId)
 | 
				
			||||||
        external
 | 
					        external
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Load the finalization and pool state into memory.
 | 
					        // Compute relevant epochs
 | 
				
			||||||
        IStructs.UnfinalizedState memory state = unfinalizedState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Noop if all active pools have been finalized.
 | 
					 | 
				
			||||||
        if (state.poolsRemaining == 0) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint256 currentEpoch_ = currentEpoch;
 | 
					        uint256 currentEpoch_ = currentEpoch;
 | 
				
			||||||
        uint256 prevEpoch = currentEpoch_.safeSub(1);
 | 
					        uint256 prevEpoch = currentEpoch_.safeSub(1);
 | 
				
			||||||
        IStructs.ActivePool memory pool = _getActivePoolFromEpoch(prevEpoch, poolId);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Noop if the pool was not active or already finalized (has no fees).
 | 
					        // Load the aggregated stats into memory; noop if no pools to finalize.
 | 
				
			||||||
        if (pool.feesCollected == 0) {
 | 
					        IStructs.AggregatedStats memory aggregatedStats = aggregatedStatsByEpoch[prevEpoch];
 | 
				
			||||||
 | 
					        if (aggregatedStats.numPoolsToFinalize == 0) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Clear the pool state so we don't finalize it again, and to recoup
 | 
					        // Noop if the pool did not earn rewards or already finalized (has no fees).
 | 
				
			||||||
 | 
					        IStructs.PoolStats memory poolStats = poolStatsByEpoch[poolId][prevEpoch];
 | 
				
			||||||
 | 
					        if (poolStats.feesCollected == 0) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Clear the pool stats so we don't finalize it again, and to recoup
 | 
				
			||||||
        // some gas.
 | 
					        // some gas.
 | 
				
			||||||
        delete _getActivePoolsFromEpoch(prevEpoch)[poolId];
 | 
					        delete poolStatsByEpoch[poolId][prevEpoch];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Compute the rewards.
 | 
					        // Compute the rewards.
 | 
				
			||||||
        uint256 rewards = _getUnfinalizedPoolRewardsFromState(pool, state);
 | 
					        uint256 rewards = _getUnfinalizedPoolRewardsFromPoolStats(poolStats, aggregatedStats);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Pay the operator and update rewards for the pool.
 | 
					        // Pay the operator and update rewards for the pool.
 | 
				
			||||||
        // Note that we credit at the CURRENT epoch even though these rewards
 | 
					        // Note that we credit at the CURRENT epoch even though these rewards
 | 
				
			||||||
@@ -129,7 +119,7 @@ contract MixinFinalizer is
 | 
				
			|||||||
        (uint256 operatorReward, uint256 membersReward) = _syncPoolRewards(
 | 
					        (uint256 operatorReward, uint256 membersReward) = _syncPoolRewards(
 | 
				
			||||||
            poolId,
 | 
					            poolId,
 | 
				
			||||||
            rewards,
 | 
					            rewards,
 | 
				
			||||||
            pool.membersStake
 | 
					            poolStats.membersStake
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Emit an event.
 | 
					        // Emit an event.
 | 
				
			||||||
@@ -143,22 +133,22 @@ contract MixinFinalizer is
 | 
				
			|||||||
        uint256 totalReward = operatorReward.safeAdd(membersReward);
 | 
					        uint256 totalReward = operatorReward.safeAdd(membersReward);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Increase `totalRewardsFinalized`.
 | 
					        // Increase `totalRewardsFinalized`.
 | 
				
			||||||
        unfinalizedState.totalRewardsFinalized =
 | 
					        aggregatedStatsByEpoch[prevEpoch].totalRewardsFinalized =
 | 
				
			||||||
            state.totalRewardsFinalized =
 | 
					            aggregatedStats.totalRewardsFinalized =
 | 
				
			||||||
            state.totalRewardsFinalized.safeAdd(totalReward);
 | 
					            aggregatedStats.totalRewardsFinalized.safeAdd(totalReward);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Decrease the number of unfinalized pools left.
 | 
					        // Decrease the number of unfinalized pools left.
 | 
				
			||||||
        unfinalizedState.poolsRemaining =
 | 
					        aggregatedStatsByEpoch[prevEpoch].numPoolsToFinalize =
 | 
				
			||||||
            state.poolsRemaining =
 | 
					            aggregatedStats.numPoolsToFinalize =
 | 
				
			||||||
            state.poolsRemaining.safeSub(1);
 | 
					            aggregatedStats.numPoolsToFinalize.safeSub(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // If there are no more unfinalized pools remaining, the epoch is
 | 
					        // If there are no more unfinalized pools remaining, the epoch is
 | 
				
			||||||
        // finalized.
 | 
					        // finalized.
 | 
				
			||||||
        if (state.poolsRemaining == 0) {
 | 
					        if (aggregatedStats.numPoolsToFinalize == 0) {
 | 
				
			||||||
            emit EpochFinalized(
 | 
					            emit EpochFinalized(
 | 
				
			||||||
                prevEpoch,
 | 
					                prevEpoch,
 | 
				
			||||||
                state.totalRewardsFinalized,
 | 
					                aggregatedStats.totalRewardsFinalized,
 | 
				
			||||||
                state.rewardsAvailable.safeSub(state.totalRewardsFinalized)
 | 
					                aggregatedStats.rewardsAvailable.safeSub(aggregatedStats.totalRewardsFinalized)
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -177,44 +167,10 @@ contract MixinFinalizer is
 | 
				
			|||||||
            uint256 membersStake
 | 
					            uint256 membersStake
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        IStructs.ActivePool memory pool = _getActivePoolFromEpoch(
 | 
					        uint256 prevEpoch = currentEpoch.safeSub(1);
 | 
				
			||||||
            currentEpoch.safeSub(1),
 | 
					        IStructs.PoolStats memory poolStats = poolStatsByEpoch[poolId][prevEpoch];
 | 
				
			||||||
            poolId
 | 
					        reward = _getUnfinalizedPoolRewardsFromPoolStats(poolStats, aggregatedStatsByEpoch[prevEpoch]);
 | 
				
			||||||
        );
 | 
					        membersStake = poolStats.membersStake;
 | 
				
			||||||
        reward = _getUnfinalizedPoolRewardsFromState(pool, unfinalizedState);
 | 
					 | 
				
			||||||
        membersStake = pool.membersStake;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// @dev Get an active pool from an epoch by its ID.
 | 
					 | 
				
			||||||
    /// @param epoch The epoch the pool was/will be active in.
 | 
					 | 
				
			||||||
    /// @param poolId The ID of the pool.
 | 
					 | 
				
			||||||
    /// @return pool The pool with ID `poolId` that was active in `epoch`.
 | 
					 | 
				
			||||||
    function _getActivePoolFromEpoch(
 | 
					 | 
				
			||||||
        uint256 epoch,
 | 
					 | 
				
			||||||
        bytes32 poolId
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
        internal
 | 
					 | 
				
			||||||
        view
 | 
					 | 
				
			||||||
        returns (IStructs.ActivePool memory pool)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        pool = _getActivePoolsFromEpoch(epoch)[poolId];
 | 
					 | 
				
			||||||
        return pool;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// @dev Get a mapping of active pools from an epoch.
 | 
					 | 
				
			||||||
    ///      This uses the formula `epoch % 2` as the epoch index in order
 | 
					 | 
				
			||||||
    ///      to reuse state, because we only need to remember, at most, two
 | 
					 | 
				
			||||||
    ///      epochs at once.
 | 
					 | 
				
			||||||
    /// @return activePools The pools that were active in `epoch`.
 | 
					 | 
				
			||||||
    function _getActivePoolsFromEpoch(
 | 
					 | 
				
			||||||
        uint256 epoch
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
        internal
 | 
					 | 
				
			||||||
        view
 | 
					 | 
				
			||||||
        returns (mapping (bytes32 => IStructs.ActivePool) storage activePools)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        activePools = _activePoolsByEpoch[epoch % 2];
 | 
					 | 
				
			||||||
        return activePools;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Converts the entire ETH balance of this contract into WETH.
 | 
					    /// @dev Converts the entire ETH balance of this contract into WETH.
 | 
				
			||||||
@@ -247,10 +203,10 @@ contract MixinFinalizer is
 | 
				
			|||||||
        view
 | 
					        view
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        uint256 prevEpoch = currentEpoch.safeSub(1);
 | 
					        uint256 prevEpoch = currentEpoch.safeSub(1);
 | 
				
			||||||
        IStructs.ActivePool memory pool = _getActivePoolFromEpoch(prevEpoch, poolId);
 | 
					        IStructs.PoolStats memory poolStats = poolStatsByEpoch[poolId][prevEpoch];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // A pool that has any fees remaining has not been finalized
 | 
					        // A pool that has any fees remaining has not been finalized
 | 
				
			||||||
        if (pool.feesCollected != 0) {
 | 
					        if (poolStats.feesCollected != 0) {
 | 
				
			||||||
            LibRichErrors.rrevert(
 | 
					            LibRichErrors.rrevert(
 | 
				
			||||||
                LibStakingRichErrors.PoolNotFinalizedError(
 | 
					                LibStakingRichErrors.PoolNotFinalizedError(
 | 
				
			||||||
                    poolId,
 | 
					                    poolId,
 | 
				
			||||||
@@ -261,30 +217,29 @@ contract MixinFinalizer is
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Computes the reward owed to a pool during finalization.
 | 
					    /// @dev Computes the reward owed to a pool during finalization.
 | 
				
			||||||
    /// @param pool The active pool.
 | 
					    /// @param poolStats Stats for a specific pool.
 | 
				
			||||||
    /// @param state The current state of finalization.
 | 
					    /// @param aggregatedStats Stats aggregated across all pools.
 | 
				
			||||||
    /// @return rewards Unfinalized rewards for this pool.
 | 
					    /// @return rewards Unfinalized rewards for the input pool.
 | 
				
			||||||
    function _getUnfinalizedPoolRewardsFromState(
 | 
					    function _getUnfinalizedPoolRewardsFromPoolStats(
 | 
				
			||||||
        IStructs.ActivePool memory pool,
 | 
					        IStructs.PoolStats memory poolStats,
 | 
				
			||||||
        IStructs.UnfinalizedState memory state
 | 
					        IStructs.AggregatedStats memory aggregatedStats
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
        private
 | 
					        private
 | 
				
			||||||
        view
 | 
					        view
 | 
				
			||||||
        returns (uint256 rewards)
 | 
					        returns (uint256 rewards)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // There can't be any rewards if the pool was active or if it has
 | 
					        // There can't be any rewards if the pool did not collect any fees.
 | 
				
			||||||
        // no stake.
 | 
					        if (poolStats.feesCollected == 0) {
 | 
				
			||||||
        if (pool.feesCollected == 0) {
 | 
					 | 
				
			||||||
            return rewards;
 | 
					            return rewards;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Use the cobb-douglas function to compute the total reward.
 | 
					        // Use the cobb-douglas function to compute the total reward.
 | 
				
			||||||
        rewards = LibCobbDouglas.cobbDouglas(
 | 
					        rewards = LibCobbDouglas.cobbDouglas(
 | 
				
			||||||
            state.rewardsAvailable,
 | 
					            aggregatedStats.rewardsAvailable,
 | 
				
			||||||
            pool.feesCollected,
 | 
					            poolStats.feesCollected,
 | 
				
			||||||
            state.totalFeesCollected,
 | 
					            aggregatedStats.totalFeesCollected,
 | 
				
			||||||
            pool.weightedStake,
 | 
					            poolStats.weightedStake,
 | 
				
			||||||
            state.totalWeightedStake,
 | 
					            aggregatedStats.totalWeightedStake,
 | 
				
			||||||
            cobbDouglasAlphaNumerator,
 | 
					            cobbDouglasAlphaNumerator,
 | 
				
			||||||
            cobbDouglasAlphaDenominator
 | 
					            cobbDouglasAlphaDenominator
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -292,7 +247,7 @@ contract MixinFinalizer is
 | 
				
			|||||||
        // Clip the reward to always be under
 | 
					        // Clip the reward to always be under
 | 
				
			||||||
        // `rewardsAvailable - totalRewardsPaid`,
 | 
					        // `rewardsAvailable - totalRewardsPaid`,
 | 
				
			||||||
        // in case cobb-douglas overflows, which should be unlikely.
 | 
					        // in case cobb-douglas overflows, which should be unlikely.
 | 
				
			||||||
        uint256 rewardsRemaining = state.rewardsAvailable.safeSub(state.totalRewardsFinalized);
 | 
					        uint256 rewardsRemaining = aggregatedStats.rewardsAvailable.safeSub(aggregatedStats.totalRewardsFinalized);
 | 
				
			||||||
        if (rewardsRemaining < rewards) {
 | 
					        if (rewardsRemaining < rewards) {
 | 
				
			||||||
            rewards = rewardsRemaining;
 | 
					            rewards = rewardsRemaining;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,7 @@ contract TestDelegatorRewards is
 | 
				
			|||||||
        currentEpoch += 1;
 | 
					        currentEpoch += 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Create and delegate stake that is active in the current epoch.
 | 
					    /// @dev Create and delegate stake to the current epoch.
 | 
				
			||||||
    ///      Only used to test purportedly unreachable states.
 | 
					    ///      Only used to test purportedly unreachable states.
 | 
				
			||||||
    ///      Also withdraws pending rewards.
 | 
					    ///      Also withdraws pending rewards.
 | 
				
			||||||
    function delegateStakeNow(
 | 
					    function delegateStakeNow(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,20 +74,19 @@ contract TestFinalizer is
 | 
				
			|||||||
        external
 | 
					        external
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        require(feesCollected > 0, "FEES_MUST_BE_NONZERO");
 | 
					        require(feesCollected > 0, "FEES_MUST_BE_NONZERO");
 | 
				
			||||||
        mapping (bytes32 => IStructs.ActivePool) storage activePools = _getActivePoolsFromEpoch(
 | 
					        uint256 currentEpoch_ = currentEpoch;
 | 
				
			||||||
            currentEpoch
 | 
					        IStructs.PoolStats memory poolStats = poolStatsByEpoch[poolId][currentEpoch_];
 | 
				
			||||||
        );
 | 
					        require(poolStats.feesCollected == 0, "POOL_ALREADY_ADDED");
 | 
				
			||||||
        IStructs.ActivePool memory pool = activePools[poolId];
 | 
					 | 
				
			||||||
        require(pool.feesCollected == 0, "POOL_ALREADY_ADDED");
 | 
					 | 
				
			||||||
        _operatorSharesByPool[poolId] = operatorShare;
 | 
					        _operatorSharesByPool[poolId] = operatorShare;
 | 
				
			||||||
        activePools[poolId] = IStructs.ActivePool({
 | 
					        poolStatsByEpoch[poolId][currentEpoch_] = IStructs.PoolStats({
 | 
				
			||||||
            feesCollected: feesCollected,
 | 
					            feesCollected: feesCollected,
 | 
				
			||||||
            membersStake: membersStake,
 | 
					            membersStake: membersStake,
 | 
				
			||||||
            weightedStake: weightedStake
 | 
					            weightedStake: weightedStake
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        totalFeesCollectedThisEpoch += feesCollected;
 | 
					
 | 
				
			||||||
        totalWeightedStakeThisEpoch += weightedStake;
 | 
					        aggregatedStatsByEpoch[currentEpoch_].totalFeesCollected += feesCollected;
 | 
				
			||||||
        numActivePoolsThisEpoch += 1;
 | 
					        aggregatedStatsByEpoch[currentEpoch_].totalWeightedStake += weightedStake;
 | 
				
			||||||
 | 
					        aggregatedStatsByEpoch[currentEpoch_].numPoolsToFinalize += 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Drain the balance of this contract.
 | 
					    /// @dev Drain the balance of this contract.
 | 
				
			||||||
@@ -131,13 +130,21 @@ contract TestFinalizer is
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Expose `_getActivePoolFromEpoch`.
 | 
					    /// @dev Expose pool stats for the input epoch.
 | 
				
			||||||
    function getActivePoolFromEpoch(uint256 epoch, bytes32 poolId)
 | 
					    function getPoolStatsFromEpoch(uint256 epoch, bytes32 poolId)
 | 
				
			||||||
        external
 | 
					        external
 | 
				
			||||||
        view
 | 
					        view
 | 
				
			||||||
        returns (IStructs.ActivePool memory pool)
 | 
					        returns (IStructs.PoolStats memory)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        pool = _getActivePoolFromEpoch(epoch, poolId);
 | 
					        return poolStatsByEpoch[poolId][epoch];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function getAggregatedStatsForPreviousEpoch()
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        view
 | 
				
			||||||
 | 
					        returns (IStructs.AggregatedStats memory)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return aggregatedStatsByEpoch[currentEpoch - 1];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Overridden to log and transfer to receivers.
 | 
					    /// @dev Overridden to log and transfer to receivers.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,6 +86,14 @@ contract TestProtocolFees is
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function getAggregatedStatsForCurrentEpoch()
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        view
 | 
				
			||||||
 | 
					        returns (IStructs.AggregatedStats memory)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return aggregatedStatsByEpoch[currentEpoch];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// @dev Overridden to use test pools.
 | 
					    /// @dev Overridden to use test pools.
 | 
				
			||||||
    function getStakingPoolIdOfMaker(address makerAddress)
 | 
					    function getStakingPoolIdOfMaker(address makerAddress)
 | 
				
			||||||
        public
 | 
					        public
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -275,45 +275,21 @@ contract TestStorageLayoutAndConstants is
 | 
				
			|||||||
            offset := 0x0
 | 
					            offset := 0x0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assertSlotAndOffset(
 | 
					            assertSlotAndOffset(
 | 
				
			||||||
                totalFeesCollectedThisEpoch_slot,
 | 
					                poolStatsByEpoch_slot,
 | 
				
			||||||
                totalFeesCollectedThisEpoch_offset,
 | 
					                poolStatsByEpoch_offset,
 | 
				
			||||||
                slot,
 | 
					                slot,
 | 
				
			||||||
                offset
 | 
					                offset
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            slot := add(slot, 0x1)
 | 
					            slot := add(slot, 0x1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assertSlotAndOffset(
 | 
					            assertSlotAndOffset(
 | 
				
			||||||
                totalWeightedStakeThisEpoch_slot,
 | 
					                aggregatedStatsByEpoch_slot,
 | 
				
			||||||
                totalWeightedStakeThisEpoch_offset,
 | 
					                aggregatedStatsByEpoch_offset,
 | 
				
			||||||
                slot,
 | 
					                slot,
 | 
				
			||||||
                offset
 | 
					                offset
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            slot := add(slot, 0x1)
 | 
					            slot := add(slot, 0x1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assertSlotAndOffset(
 | 
					 | 
				
			||||||
                _activePoolsByEpoch_slot,
 | 
					 | 
				
			||||||
                _activePoolsByEpoch_offset,
 | 
					 | 
				
			||||||
                slot,
 | 
					 | 
				
			||||||
                offset
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            slot := add(slot, 0x1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            assertSlotAndOffset(
 | 
					 | 
				
			||||||
                numActivePoolsThisEpoch_slot,
 | 
					 | 
				
			||||||
                numActivePoolsThisEpoch_offset,
 | 
					 | 
				
			||||||
                slot,
 | 
					 | 
				
			||||||
                offset
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            slot := add(slot, 0x1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            assertSlotAndOffset(
 | 
					 | 
				
			||||||
                unfinalizedState_slot,
 | 
					 | 
				
			||||||
                unfinalizedState_offset,
 | 
					 | 
				
			||||||
                slot,
 | 
					 | 
				
			||||||
                offset
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            slot := add(slot, 0x5)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            assertSlotAndOffset(
 | 
					            assertSlotAndOffset(
 | 
				
			||||||
                wethReservedForPoolRewards_slot,
 | 
					                wethReservedForPoolRewards_slot,
 | 
				
			||||||
                wethReservedForPoolRewards_offset,
 | 
					                wethReservedForPoolRewards_offset,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,7 +238,7 @@ export class FinalizerActor extends BaseActor {
 | 
				
			|||||||
    private async _getRewardByPoolIdAsync(poolIds: string[]): Promise<RewardByPoolId> {
 | 
					    private async _getRewardByPoolIdAsync(poolIds: string[]): Promise<RewardByPoolId> {
 | 
				
			||||||
        const activePools = await Promise.all(
 | 
					        const activePools = await Promise.all(
 | 
				
			||||||
            poolIds.map(async poolId =>
 | 
					            poolIds.map(async poolId =>
 | 
				
			||||||
                this._stakingApiWrapper.stakingContract.getActiveStakingPoolThisEpoch.callAsync(poolId),
 | 
					                this._stakingApiWrapper.stakingContract.getStakingPoolStatsThisEpoch.callAsync(poolId),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        const totalRewards = await this._stakingApiWrapper.utils.getAvailableRewardsBalanceAsync();
 | 
					        const totalRewards = await this._stakingApiWrapper.utils.getAvailableRewardsBalanceAsync();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,21 +87,14 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    interface UnfinalizedState {
 | 
					    interface UnfinalizedState {
 | 
				
			||||||
        rewardsAvailable: Numberish;
 | 
					        rewardsAvailable: Numberish;
 | 
				
			||||||
        poolsRemaining: number;
 | 
					        numPoolsToFinalize: Numberish;
 | 
				
			||||||
        totalFeesCollected: Numberish;
 | 
					        totalFeesCollected: Numberish;
 | 
				
			||||||
        totalWeightedStake: Numberish;
 | 
					        totalWeightedStake: Numberish;
 | 
				
			||||||
        totalRewardsFinalized: Numberish;
 | 
					        totalRewardsFinalized: Numberish;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function getUnfinalizedStateAsync(): Promise<UnfinalizedState> {
 | 
					    async function getUnfinalizedStateAsync(): Promise<UnfinalizedState> {
 | 
				
			||||||
        const r = await testContract.unfinalizedState.callAsync();
 | 
					        return testContract.getAggregatedStatsForPreviousEpoch.callAsync();
 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            rewardsAvailable: r[0],
 | 
					 | 
				
			||||||
            poolsRemaining: r[1].toNumber(),
 | 
					 | 
				
			||||||
            totalFeesCollected: r[2],
 | 
					 | 
				
			||||||
            totalWeightedStake: r[3],
 | 
					 | 
				
			||||||
            totalRewardsFinalized: r[4],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function finalizePoolsAsync(poolIds: string[]): Promise<LogEntry[]> {
 | 
					    async function finalizePoolsAsync(poolIds: string[]): Promise<LogEntry[]> {
 | 
				
			||||||
@@ -142,16 +135,16 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    async function assertFinalizationLogsAndBalancesAsync(
 | 
					    async function assertFinalizationLogsAndBalancesAsync(
 | 
				
			||||||
        rewardsAvailable: Numberish,
 | 
					        rewardsAvailable: Numberish,
 | 
				
			||||||
        activePools: ActivePoolOpts[],
 | 
					        poolsToFinalize: ActivePoolOpts[],
 | 
				
			||||||
        finalizationLogs: LogEntry[],
 | 
					        finalizationLogs: LogEntry[],
 | 
				
			||||||
    ): Promise<void> {
 | 
					    ): Promise<void> {
 | 
				
			||||||
        const currentEpoch = await getCurrentEpochAsync();
 | 
					        const currentEpoch = await getCurrentEpochAsync();
 | 
				
			||||||
        // Compute the expected rewards for each pool.
 | 
					        // Compute the expected rewards for each pool.
 | 
				
			||||||
        const poolsWithStake = activePools.filter(p => !new BigNumber(p.weightedStake).isZero());
 | 
					        const poolsWithStake = poolsToFinalize.filter(p => !new BigNumber(p.weightedStake).isZero());
 | 
				
			||||||
        const poolRewards = await calculatePoolRewardsAsync(rewardsAvailable, poolsWithStake);
 | 
					        const poolRewards = await calculatePoolRewardsAsync(rewardsAvailable, poolsWithStake);
 | 
				
			||||||
        const totalRewards = BigNumber.sum(...poolRewards);
 | 
					        const totalRewards = BigNumber.sum(...poolRewards);
 | 
				
			||||||
        const rewardsRemaining = new BigNumber(rewardsAvailable).minus(totalRewards);
 | 
					        const rewardsRemaining = new BigNumber(rewardsAvailable).minus(totalRewards);
 | 
				
			||||||
        const [totalOperatorRewards, totalMembersRewards] = getTotalSplitRewards(activePools, poolRewards);
 | 
					        const [totalOperatorRewards, totalMembersRewards] = getTotalSplitRewards(poolsToFinalize, poolRewards);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Assert the `RewardsPaid` logs.
 | 
					        // Assert the `RewardsPaid` logs.
 | 
				
			||||||
        const rewardsPaidEvents = getRewardsPaidEvents(finalizationLogs);
 | 
					        const rewardsPaidEvents = getRewardsPaidEvents(finalizationLogs);
 | 
				
			||||||
@@ -203,13 +196,13 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    async function calculatePoolRewardsAsync(
 | 
					    async function calculatePoolRewardsAsync(
 | 
				
			||||||
        rewardsAvailable: Numberish,
 | 
					        rewardsAvailable: Numberish,
 | 
				
			||||||
        activePools: ActivePoolOpts[],
 | 
					        poolsToFinalize: ActivePoolOpts[],
 | 
				
			||||||
    ): Promise<BigNumber[]> {
 | 
					    ): Promise<BigNumber[]> {
 | 
				
			||||||
        const totalFees = BigNumber.sum(...activePools.map(p => p.feesCollected));
 | 
					        const totalFees = BigNumber.sum(...poolsToFinalize.map(p => p.feesCollected));
 | 
				
			||||||
        const totalStake = BigNumber.sum(...activePools.map(p => p.weightedStake));
 | 
					        const totalStake = BigNumber.sum(...poolsToFinalize.map(p => p.weightedStake));
 | 
				
			||||||
        const poolRewards = _.times(activePools.length, () => constants.ZERO_AMOUNT);
 | 
					        const poolRewards = _.times(poolsToFinalize.length, () => constants.ZERO_AMOUNT);
 | 
				
			||||||
        for (const i of _.times(activePools.length)) {
 | 
					        for (const i of _.times(poolsToFinalize.length)) {
 | 
				
			||||||
            const pool = activePools[i];
 | 
					            const pool = poolsToFinalize[i];
 | 
				
			||||||
            const feesCollected = new BigNumber(pool.feesCollected);
 | 
					            const feesCollected = new BigNumber(pool.feesCollected);
 | 
				
			||||||
            if (feesCollected.isZero()) {
 | 
					            if (feesCollected.isZero()) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
@@ -288,7 +281,7 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('immediately finalizes if there are no active pools', async () => {
 | 
					        it('immediately finalizes if there are no pools to finalize', async () => {
 | 
				
			||||||
            const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            assertEpochFinalizedEvent(receipt.logs, {
 | 
					            assertEpochFinalizedEvent(receipt.logs, {
 | 
				
			||||||
                epoch: stakingConstants.INITIAL_EPOCH,
 | 
					                epoch: stakingConstants.INITIAL_EPOCH,
 | 
				
			||||||
@@ -297,7 +290,7 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('does not immediately finalize if there is an active pool', async () => {
 | 
					        it('does not immediately finalize if there is a pool to finalize', async () => {
 | 
				
			||||||
            await addActivePoolAsync();
 | 
					            await addActivePoolAsync();
 | 
				
			||||||
            const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            const events = filterLogsToArguments<IStakingEventsEpochFinalizedEventArgs>(
 | 
					            const events = filterLogsToArguments<IStakingEventsEpochFinalizedEventArgs>(
 | 
				
			||||||
@@ -307,32 +300,32 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            expect(events).to.deep.eq([]);
 | 
					            expect(events).to.deep.eq([]);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it("clears the next epoch's finalization state", async () => {
 | 
					 | 
				
			||||||
            // Add a pool so there is state to clear.
 | 
					 | 
				
			||||||
            await addActivePoolAsync();
 | 
					 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					 | 
				
			||||||
            const epoch = await testContract.currentEpoch.callAsync();
 | 
					 | 
				
			||||||
            expect(epoch).to.bignumber.eq(stakingConstants.INITIAL_EPOCH.plus(1));
 | 
					 | 
				
			||||||
            const numActivePools = await testContract.numActivePoolsThisEpoch.callAsync();
 | 
					 | 
				
			||||||
            const totalFees = await testContract.totalFeesCollectedThisEpoch.callAsync();
 | 
					 | 
				
			||||||
            const totalStake = await testContract.totalWeightedStakeThisEpoch.callAsync();
 | 
					 | 
				
			||||||
            expect(numActivePools).to.bignumber.eq(0);
 | 
					 | 
				
			||||||
            expect(totalFees).to.bignumber.eq(0);
 | 
					 | 
				
			||||||
            expect(totalStake).to.bignumber.eq(0);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        it('prepares unfinalized state', async () => {
 | 
					        it('prepares unfinalized state', async () => {
 | 
				
			||||||
            // Add a pool so there is state to clear.
 | 
					            // Add a pool so there is state to clear.
 | 
				
			||||||
            const pool = await addActivePoolAsync();
 | 
					            const pool = await addActivePoolAsync();
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            return assertUnfinalizedStateAsync({
 | 
					            return assertUnfinalizedStateAsync({
 | 
				
			||||||
                poolsRemaining: 1,
 | 
					                numPoolsToFinalize: 1,
 | 
				
			||||||
                rewardsAvailable: INITIAL_BALANCE,
 | 
					                rewardsAvailable: INITIAL_BALANCE,
 | 
				
			||||||
                totalFeesCollected: pool.feesCollected,
 | 
					                totalFeesCollected: pool.feesCollected,
 | 
				
			||||||
                totalWeightedStake: pool.weightedStake,
 | 
					                totalWeightedStake: pool.weightedStake,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it("correctly stores the epoch's aggregated stats after ending the epoch", async () => {
 | 
				
			||||||
 | 
					            const pool = await addActivePoolAsync();
 | 
				
			||||||
 | 
					            const epoch = await testContract.currentEpoch.callAsync();
 | 
				
			||||||
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
 | 
					            const aggregatedStats = await testContract.aggregatedStatsByEpoch.callAsync(epoch);
 | 
				
			||||||
 | 
					            expect(aggregatedStats).to.be.deep.equal([
 | 
				
			||||||
 | 
					                INITIAL_BALANCE,
 | 
				
			||||||
 | 
					                new BigNumber(1), // pools to finalize
 | 
				
			||||||
 | 
					                pool.feesCollected,
 | 
				
			||||||
 | 
					                pool.weightedStake,
 | 
				
			||||||
 | 
					                new BigNumber(0), // rewards finalized
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('reverts if the prior epoch is unfinalized', async () => {
 | 
					        it('reverts if the prior epoch is unfinalized', async () => {
 | 
				
			||||||
            await addActivePoolAsync();
 | 
					            await addActivePoolAsync();
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
@@ -346,7 +339,7 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('_finalizePool()', () => {
 | 
					    describe('_finalizePool()', () => {
 | 
				
			||||||
        it('does nothing if there were no active pools', async () => {
 | 
					        it('does nothing if there were no pools to finalize', async () => {
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            const poolId = hexRandom();
 | 
					            const poolId = hexRandom();
 | 
				
			||||||
            const logs = await finalizePoolsAsync([poolId]);
 | 
					            const logs = await finalizePoolsAsync([poolId]);
 | 
				
			||||||
@@ -382,7 +375,7 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            const pool = _.sample(pools) as ActivePoolOpts;
 | 
					            const pool = _.sample(pools) as ActivePoolOpts;
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            await finalizePoolsAsync([pool.poolId]);
 | 
					            await finalizePoolsAsync([pool.poolId]);
 | 
				
			||||||
            const poolState = await testContract.getActivePoolFromEpoch.callAsync(
 | 
					            const poolState = await testContract.getPoolStatsFromEpoch.callAsync(
 | 
				
			||||||
                stakingConstants.INITIAL_EPOCH,
 | 
					                stakingConstants.INITIAL_EPOCH,
 | 
				
			||||||
                pool.poolId,
 | 
					                pool.poolId,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
@@ -436,7 +429,7 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            return expect(getCurrentEpochAsync()).to.become(stakingConstants.INITIAL_EPOCH.plus(2));
 | 
					            return expect(getCurrentEpochAsync()).to.become(stakingConstants.INITIAL_EPOCH.plus(2));
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('does not reward a pool that was only active 2 epochs ago', async () => {
 | 
					        it('does not reward a pool that only earned rewards 2 epochs ago', async () => {
 | 
				
			||||||
            const pool1 = await addActivePoolAsync();
 | 
					            const pool1 = await addActivePoolAsync();
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            await finalizePoolsAsync([pool1.poolId]);
 | 
					            await finalizePoolsAsync([pool1.poolId]);
 | 
				
			||||||
@@ -448,7 +441,7 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            expect(rewardsPaidEvents).to.deep.eq([]);
 | 
					            expect(rewardsPaidEvents).to.deep.eq([]);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('does not reward a pool that was only active 3 epochs ago', async () => {
 | 
					        it('does not reward a pool that only earned rewards 3 epochs ago', async () => {
 | 
				
			||||||
            const pool1 = await addActivePoolAsync();
 | 
					            const pool1 = await addActivePoolAsync();
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            await finalizePoolsAsync([pool1.poolId]);
 | 
					            await finalizePoolsAsync([pool1.poolId]);
 | 
				
			||||||
@@ -503,18 +496,18 @@ blockchainTests.resets('Finalizer unit tests', env => {
 | 
				
			|||||||
            return assertUnfinalizedPoolRewardsAsync(poolId, ZERO_REWARDS);
 | 
					            return assertUnfinalizedPoolRewardsAsync(poolId, ZERO_REWARDS);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('returns empty if pool was not active', async () => {
 | 
					        it('returns empty if pool did not earn rewards', async () => {
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            const poolId = hexRandom();
 | 
					            const poolId = hexRandom();
 | 
				
			||||||
            return assertUnfinalizedPoolRewardsAsync(poolId, ZERO_REWARDS);
 | 
					            return assertUnfinalizedPoolRewardsAsync(poolId, ZERO_REWARDS);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('returns empty if pool is active only in the current epoch', async () => {
 | 
					        it('returns empty if pool is earned rewards only in the current epoch', async () => {
 | 
				
			||||||
            const pool = await addActivePoolAsync();
 | 
					            const pool = await addActivePoolAsync();
 | 
				
			||||||
            return assertUnfinalizedPoolRewardsAsync(pool.poolId, ZERO_REWARDS);
 | 
					            return assertUnfinalizedPoolRewardsAsync(pool.poolId, ZERO_REWARDS);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it('returns empty if pool was only active in the 2 epochs ago', async () => {
 | 
					        it('returns empty if pool only earned rewards in the 2 epochs ago', async () => {
 | 
				
			||||||
            const pool = await addActivePoolAsync();
 | 
					            const pool = await addActivePoolAsync();
 | 
				
			||||||
            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
					            await testContract.endEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
            await finalizePoolsAsync([pool.poolId]);
 | 
					            await finalizePoolsAsync([pool.poolId]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ import * as _ from 'lodash';
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    artifacts,
 | 
					    artifacts,
 | 
				
			||||||
    IStakingEventsEvents,
 | 
					    IStakingEventsEvents,
 | 
				
			||||||
    IStakingEventsStakingPoolActivatedEventArgs,
 | 
					    IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs,
 | 
				
			||||||
    TestProtocolFeesContract,
 | 
					    TestProtocolFeesContract,
 | 
				
			||||||
    TestProtocolFeesERC20ProxyTransferFromEventArgs,
 | 
					    TestProtocolFeesERC20ProxyTransferFromEventArgs,
 | 
				
			||||||
    TestProtocolFeesEvents,
 | 
					    TestProtocolFeesEvents,
 | 
				
			||||||
@@ -152,7 +152,7 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async function getProtocolFeesAsync(poolId: string): Promise<BigNumber> {
 | 
					        async function getProtocolFeesAsync(poolId: string): Promise<BigNumber> {
 | 
				
			||||||
            return (await testContract.getActiveStakingPoolThisEpoch.callAsync(poolId)).feesCollected;
 | 
					            return (await testContract.getStakingPoolStatsThisEpoch.callAsync(poolId)).feesCollected;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        describe('ETH fees', () => {
 | 
					        describe('ETH fees', () => {
 | 
				
			||||||
@@ -369,21 +369,22 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            interface FinalizationState {
 | 
					            interface FinalizationState {
 | 
				
			||||||
                numActivePools: BigNumber;
 | 
					                numPoolsToFinalize: BigNumber;
 | 
				
			||||||
                totalFeesCollected: BigNumber;
 | 
					                totalFeesCollected: BigNumber;
 | 
				
			||||||
                totalWeightedStake: BigNumber;
 | 
					                totalWeightedStake: BigNumber;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            async function getFinalizationStateAsync(): Promise<FinalizationState> {
 | 
					            async function getFinalizationStateAsync(): Promise<FinalizationState> {
 | 
				
			||||||
 | 
					                const aggregatedStats = await testContract.getAggregatedStatsForCurrentEpoch.callAsync();
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                    numActivePools: await testContract.numActivePoolsThisEpoch.callAsync(),
 | 
					                    numPoolsToFinalize: aggregatedStats.numPoolsToFinalize,
 | 
				
			||||||
                    totalFeesCollected: await testContract.totalFeesCollectedThisEpoch.callAsync(),
 | 
					                    totalFeesCollected: aggregatedStats.totalFeesCollected,
 | 
				
			||||||
                    totalWeightedStake: await testContract.totalWeightedStakeThisEpoch.callAsync(),
 | 
					                    totalWeightedStake: aggregatedStats.totalWeightedStake,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            interface PayToMakerResult {
 | 
					            interface PayToMakerResult {
 | 
				
			||||||
                poolActivatedEvents: IStakingEventsStakingPoolActivatedEventArgs[];
 | 
					                poolEarnedRewardsEvents: IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs[];
 | 
				
			||||||
                fee: BigNumber;
 | 
					                fee: BigNumber;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -395,13 +396,13 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
                    new BigNumber(_fee),
 | 
					                    new BigNumber(_fee),
 | 
				
			||||||
                    { from: exchangeAddress, value: _fee },
 | 
					                    { from: exchangeAddress, value: _fee },
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                const events = filterLogsToArguments<IStakingEventsStakingPoolActivatedEventArgs>(
 | 
					                const events = filterLogsToArguments<IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs>(
 | 
				
			||||||
                    receipt.logs,
 | 
					                    receipt.logs,
 | 
				
			||||||
                    IStakingEventsEvents.StakingPoolActivated,
 | 
					                    IStakingEventsEvents.StakingPoolEarnedRewardsInEpoch,
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                    fee: new BigNumber(_fee),
 | 
					                    fee: new BigNumber(_fee),
 | 
				
			||||||
                    poolActivatedEvents: events,
 | 
					                    poolEarnedRewardsEvents: events,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -412,37 +413,37 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
                    .plus(operatorStake);
 | 
					                    .plus(operatorStake);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            it('no active pools to start', async () => {
 | 
					            it('no pools to finalize to start', async () => {
 | 
				
			||||||
                const state = await getFinalizationStateAsync();
 | 
					                const state = await getFinalizationStateAsync();
 | 
				
			||||||
                expect(state.numActivePools).to.bignumber.eq(0);
 | 
					                expect(state.numPoolsToFinalize).to.bignumber.eq(0);
 | 
				
			||||||
                expect(state.totalFeesCollected).to.bignumber.eq(0);
 | 
					                expect(state.totalFeesCollected).to.bignumber.eq(0);
 | 
				
			||||||
                expect(state.totalWeightedStake).to.bignumber.eq(0);
 | 
					                expect(state.totalWeightedStake).to.bignumber.eq(0);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            it('pool is not registered to start', async () => {
 | 
					            it('pool is not registered to start', async () => {
 | 
				
			||||||
                const { poolId } = await createTestPoolAsync();
 | 
					                const { poolId } = await createTestPoolAsync();
 | 
				
			||||||
                const pool = await testContract.getActiveStakingPoolThisEpoch.callAsync(poolId);
 | 
					                const pool = await testContract.getStakingPoolStatsThisEpoch.callAsync(poolId);
 | 
				
			||||||
                expect(pool.feesCollected).to.bignumber.eq(0);
 | 
					                expect(pool.feesCollected).to.bignumber.eq(0);
 | 
				
			||||||
                expect(pool.membersStake).to.bignumber.eq(0);
 | 
					                expect(pool.membersStake).to.bignumber.eq(0);
 | 
				
			||||||
                expect(pool.weightedStake).to.bignumber.eq(0);
 | 
					                expect(pool.weightedStake).to.bignumber.eq(0);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            it('activates a active pool the first time it earns a fee', async () => {
 | 
					            it('correctly emits event for pool the first time it earns a fee', async () => {
 | 
				
			||||||
                const pool = await createTestPoolAsync();
 | 
					                const pool = await createTestPoolAsync();
 | 
				
			||||||
                const {
 | 
					                const {
 | 
				
			||||||
                    poolId,
 | 
					                    poolId,
 | 
				
			||||||
                    makers: [poolMaker],
 | 
					                    makers: [poolMaker],
 | 
				
			||||||
                } = pool;
 | 
					                } = pool;
 | 
				
			||||||
                const { fee, poolActivatedEvents } = await payToMakerAsync(poolMaker);
 | 
					                const { fee, poolEarnedRewardsEvents } = await payToMakerAsync(poolMaker);
 | 
				
			||||||
                expect(poolActivatedEvents.length).to.eq(1);
 | 
					                expect(poolEarnedRewardsEvents.length).to.eq(1);
 | 
				
			||||||
                expect(poolActivatedEvents[0].poolId).to.eq(poolId);
 | 
					                expect(poolEarnedRewardsEvents[0].poolId).to.eq(poolId);
 | 
				
			||||||
                const actualPool = await testContract.getActiveStakingPoolThisEpoch.callAsync(poolId);
 | 
					                const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch.callAsync(poolId);
 | 
				
			||||||
                const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
					                const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
				
			||||||
                expect(actualPool.feesCollected).to.bignumber.eq(fee);
 | 
					                expect(actualPoolStats.feesCollected).to.bignumber.eq(fee);
 | 
				
			||||||
                expect(actualPool.membersStake).to.bignumber.eq(pool.membersStake);
 | 
					                expect(actualPoolStats.membersStake).to.bignumber.eq(pool.membersStake);
 | 
				
			||||||
                expect(actualPool.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
					                expect(actualPoolStats.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
				
			||||||
                const state = await getFinalizationStateAsync();
 | 
					                const state = await getFinalizationStateAsync();
 | 
				
			||||||
                expect(state.numActivePools).to.bignumber.eq(1);
 | 
					                expect(state.numPoolsToFinalize).to.bignumber.eq(1);
 | 
				
			||||||
                expect(state.totalFeesCollected).to.bignumber.eq(fee);
 | 
					                expect(state.totalFeesCollected).to.bignumber.eq(fee);
 | 
				
			||||||
                expect(state.totalWeightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
					                expect(state.totalWeightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@@ -454,16 +455,16 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
                    makers: [poolMaker],
 | 
					                    makers: [poolMaker],
 | 
				
			||||||
                } = pool;
 | 
					                } = pool;
 | 
				
			||||||
                const { fee: fee1 } = await payToMakerAsync(poolMaker);
 | 
					                const { fee: fee1 } = await payToMakerAsync(poolMaker);
 | 
				
			||||||
                const { fee: fee2, poolActivatedEvents } = await payToMakerAsync(poolMaker);
 | 
					                const { fee: fee2, poolEarnedRewardsEvents } = await payToMakerAsync(poolMaker);
 | 
				
			||||||
                expect(poolActivatedEvents).to.deep.eq([]);
 | 
					                expect(poolEarnedRewardsEvents).to.deep.eq([]);
 | 
				
			||||||
                const actualPool = await testContract.getActiveStakingPoolThisEpoch.callAsync(poolId);
 | 
					                const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch.callAsync(poolId);
 | 
				
			||||||
                const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
					                const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
				
			||||||
                const fees = BigNumber.sum(fee1, fee2);
 | 
					                const fees = BigNumber.sum(fee1, fee2);
 | 
				
			||||||
                expect(actualPool.feesCollected).to.bignumber.eq(fees);
 | 
					                expect(actualPoolStats.feesCollected).to.bignumber.eq(fees);
 | 
				
			||||||
                expect(actualPool.membersStake).to.bignumber.eq(pool.membersStake);
 | 
					                expect(actualPoolStats.membersStake).to.bignumber.eq(pool.membersStake);
 | 
				
			||||||
                expect(actualPool.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
					                expect(actualPoolStats.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
				
			||||||
                const state = await getFinalizationStateAsync();
 | 
					                const state = await getFinalizationStateAsync();
 | 
				
			||||||
                expect(state.numActivePools).to.bignumber.eq(1);
 | 
					                expect(state.numPoolsToFinalize).to.bignumber.eq(1);
 | 
				
			||||||
                expect(state.totalFeesCollected).to.bignumber.eq(fees);
 | 
					                expect(state.totalFeesCollected).to.bignumber.eq(fees);
 | 
				
			||||||
                expect(state.totalWeightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
					                expect(state.totalWeightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@@ -477,19 +478,19 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
                        poolId,
 | 
					                        poolId,
 | 
				
			||||||
                        makers: [poolMaker],
 | 
					                        makers: [poolMaker],
 | 
				
			||||||
                    } = pool;
 | 
					                    } = pool;
 | 
				
			||||||
                    const { fee, poolActivatedEvents } = await payToMakerAsync(poolMaker);
 | 
					                    const { fee, poolEarnedRewardsEvents } = await payToMakerAsync(poolMaker);
 | 
				
			||||||
                    expect(poolActivatedEvents.length).to.eq(1);
 | 
					                    expect(poolEarnedRewardsEvents.length).to.eq(1);
 | 
				
			||||||
                    expect(poolActivatedEvents[0].poolId).to.eq(poolId);
 | 
					                    expect(poolEarnedRewardsEvents[0].poolId).to.eq(poolId);
 | 
				
			||||||
                    const actualPool = await testContract.getActiveStakingPoolThisEpoch.callAsync(poolId);
 | 
					                    const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch.callAsync(poolId);
 | 
				
			||||||
                    const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
					                    const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
				
			||||||
                    expect(actualPool.feesCollected).to.bignumber.eq(fee);
 | 
					                    expect(actualPoolStats.feesCollected).to.bignumber.eq(fee);
 | 
				
			||||||
                    expect(actualPool.membersStake).to.bignumber.eq(pool.membersStake);
 | 
					                    expect(actualPoolStats.membersStake).to.bignumber.eq(pool.membersStake);
 | 
				
			||||||
                    expect(actualPool.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
					                    expect(actualPoolStats.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
				
			||||||
                    totalFees = totalFees.plus(fee);
 | 
					                    totalFees = totalFees.plus(fee);
 | 
				
			||||||
                    totalWeightedStake = totalWeightedStake.plus(expectedWeightedStake);
 | 
					                    totalWeightedStake = totalWeightedStake.plus(expectedWeightedStake);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const state = await getFinalizationStateAsync();
 | 
					                const state = await getFinalizationStateAsync();
 | 
				
			||||||
                expect(state.numActivePools).to.bignumber.eq(pools.length);
 | 
					                expect(state.numPoolsToFinalize).to.bignumber.eq(pools.length);
 | 
				
			||||||
                expect(state.totalFeesCollected).to.bignumber.eq(totalFees);
 | 
					                expect(state.totalFeesCollected).to.bignumber.eq(totalFees);
 | 
				
			||||||
                expect(state.totalWeightedStake).to.bignumber.eq(totalWeightedStake);
 | 
					                expect(state.totalWeightedStake).to.bignumber.eq(totalWeightedStake);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@@ -502,10 +503,10 @@ blockchainTests('Protocol Fees unit tests', env => {
 | 
				
			|||||||
                } = pool;
 | 
					                } = pool;
 | 
				
			||||||
                await payToMakerAsync(poolMaker);
 | 
					                await payToMakerAsync(poolMaker);
 | 
				
			||||||
                await testContract.advanceEpoch.awaitTransactionSuccessAsync();
 | 
					                await testContract.advanceEpoch.awaitTransactionSuccessAsync();
 | 
				
			||||||
                const actualPool = await testContract.getActiveStakingPoolThisEpoch.callAsync(poolId);
 | 
					                const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch.callAsync(poolId);
 | 
				
			||||||
                expect(actualPool.feesCollected).to.bignumber.eq(0);
 | 
					                expect(actualPoolStats.feesCollected).to.bignumber.eq(0);
 | 
				
			||||||
                expect(actualPool.membersStake).to.bignumber.eq(0);
 | 
					                expect(actualPoolStats.membersStake).to.bignumber.eq(0);
 | 
				
			||||||
                expect(actualPool.weightedStake).to.bignumber.eq(0);
 | 
					                expect(actualPoolStats.weightedStake).to.bignumber.eq(0);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            describe('Multiple makers', () => {
 | 
					            describe('Multiple makers', () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ import * as _ from 'lodash';
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    artifacts,
 | 
					    artifacts,
 | 
				
			||||||
    IStakingEventsEpochEndedEventArgs,
 | 
					    IStakingEventsEpochEndedEventArgs,
 | 
				
			||||||
    IStakingEventsStakingPoolActivatedEventArgs,
 | 
					    IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs,
 | 
				
			||||||
    ReadOnlyProxyContract,
 | 
					    ReadOnlyProxyContract,
 | 
				
			||||||
    StakingProxyContract,
 | 
					    StakingProxyContract,
 | 
				
			||||||
    TestCobbDouglasContract,
 | 
					    TestCobbDouglasContract,
 | 
				
			||||||
@@ -76,13 +76,13 @@ export class StakingApiWrapper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        findActivePoolIdsAsync: async (epoch?: number): Promise<string[]> => {
 | 
					        findActivePoolIdsAsync: async (epoch?: number): Promise<string[]> => {
 | 
				
			||||||
            const _epoch = epoch !== undefined ? epoch : await this.stakingContract.currentEpoch.callAsync();
 | 
					            const _epoch = epoch !== undefined ? epoch : await this.stakingContract.currentEpoch.callAsync();
 | 
				
			||||||
            const events = filterLogsToArguments<IStakingEventsStakingPoolActivatedEventArgs>(
 | 
					            const events = filterLogsToArguments<IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs>(
 | 
				
			||||||
                await this.stakingContract.getLogsAsync(
 | 
					                await this.stakingContract.getLogsAsync(
 | 
				
			||||||
                    TestStakingEvents.StakingPoolActivated,
 | 
					                    TestStakingEvents.StakingPoolEarnedRewardsInEpoch,
 | 
				
			||||||
                    { fromBlock: BlockParamLiteral.Earliest, toBlock: BlockParamLiteral.Latest },
 | 
					                    { fromBlock: BlockParamLiteral.Earliest, toBlock: BlockParamLiteral.Latest },
 | 
				
			||||||
                    { epoch: new BigNumber(_epoch) },
 | 
					                    { epoch: new BigNumber(_epoch) },
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                TestStakingEvents.StakingPoolActivated,
 | 
					                TestStakingEvents.StakingPoolEarnedRewardsInEpoch,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            return events.map(e => e.poolId);
 | 
					            return events.map(e => e.poolId);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user