@0x/contracts-staking
: It compiles!
This commit is contained in:
committed by
Lawrence Forman
parent
7fb5ed0b42
commit
ac7f6aef9e
@@ -29,10 +29,24 @@ import "./fees/MixinExchangeFees.sol";
|
||||
|
||||
contract Staking is
|
||||
IStaking,
|
||||
IStakingEvents,
|
||||
MixinAbstract,
|
||||
MixinConstants,
|
||||
MixinDeploymentConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinExchangeManager,
|
||||
MixinParams,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards,
|
||||
MixinStakingPool,
|
||||
MixinStake,
|
||||
MixinExchangeFees,
|
||||
MixinExchangeFees
|
||||
{
|
||||
// this contract can receive ETH
|
||||
// solhint-disable no-empty-blocks
|
||||
|
@@ -27,6 +27,8 @@ import "./interfaces/IStakingProxy.sol";
|
||||
|
||||
contract StakingProxy is
|
||||
IStakingProxy,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage
|
||||
{
|
||||
using LibProxy for address;
|
||||
|
@@ -44,10 +44,19 @@ import "./MixinExchangeManager.sol";
|
||||
/// disincentive for market makers to monopolize a single pool that they
|
||||
/// all delegate to.
|
||||
contract MixinExchangeFees is
|
||||
MixinDeploymentConstants,
|
||||
MixinExchangeManager,
|
||||
IStakingEvents,
|
||||
MixinAbstract,
|
||||
MixinConstants,
|
||||
MixinDeploymentConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinExchangeManager,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards,
|
||||
MixinStakingPool
|
||||
{
|
||||
@@ -99,7 +108,7 @@ contract MixinExchangeFees is
|
||||
}
|
||||
|
||||
// Look up the pool for this epoch.
|
||||
uint256 currentEpoch = getCurrentEpoch();
|
||||
uint256 currentEpoch = currentEpoch;
|
||||
mapping (bytes32 => IStructs.ActivePool) storage activePoolsThisEpoch =
|
||||
_getActivePoolsFromEpoch(currentEpoch);
|
||||
IStructs.ActivePool memory pool = activePoolsThisEpoch[poolId];
|
||||
@@ -169,7 +178,7 @@ contract MixinExchangeFees is
|
||||
// because we only need to remember state in the current epoch and the
|
||||
// epoch prior.
|
||||
IStructs.ActivePool memory pool =
|
||||
_getActivePoolFromEpoch(getCurrentEpoch(), poolId);
|
||||
_getActivePoolFromEpoch(currentEpoch, poolId);
|
||||
feesCollected = pool.feesCollected;
|
||||
}
|
||||
|
||||
@@ -188,7 +197,7 @@ contract MixinExchangeFees is
|
||||
returns (uint256 membersStake, uint256 weightedStake)
|
||||
{
|
||||
uint256 operatorStake = getStakeDelegatedToPoolByOwner(
|
||||
getPoolOperator(poolId),
|
||||
poolById[poolId].operator,
|
||||
poolId
|
||||
).currentEpochBalance;
|
||||
membersStake = totalStake.safeSub(operatorStake);
|
||||
|
@@ -30,6 +30,8 @@ import "../immutable/MixinStorage.sol";
|
||||
/// then it should be removed.
|
||||
contract MixinExchangeManager is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage
|
||||
{
|
||||
/// @dev Asserts that the call is coming from a valid exchange.
|
||||
|
@@ -74,12 +74,6 @@ contract MixinStorage is
|
||||
// mapping from Owner to Amount of Withdrawable Stake
|
||||
mapping (address => uint256) internal _withdrawableStakeByOwner;
|
||||
|
||||
// Mapping from Owner to Pool Id to epoch of the last rewards collected.
|
||||
// This is the last reward epoch for a pool that a delegator collected
|
||||
// rewards from. This is different from the epoch when the rewards were
|
||||
// collected This will always be `<= currentEpoch`.
|
||||
mapping (address => mapping (bytes32 => uint256)) internal lastCollectedRewardsEpochToPoolByOwner;
|
||||
|
||||
// tracking Pool Id
|
||||
bytes32 public nextPoolId = INITIAL_POOL_ID;
|
||||
|
||||
@@ -141,35 +135,35 @@ contract MixinStorage is
|
||||
|
||||
/// @dev The total fees collected in the current epoch, built up iteratively
|
||||
/// in `payProtocolFee()`.
|
||||
uint256 internal totalFeesCollectedThisEpoch;
|
||||
uint256 public totalFeesCollectedThisEpoch;
|
||||
|
||||
/// @dev The total weighted stake in the current epoch, built up iteratively
|
||||
/// in `payProtocolFee()`.
|
||||
uint256 internal totalWeightedStakeThisEpoch;
|
||||
uint256 public 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))
|
||||
internal
|
||||
activePoolsByEpoch;
|
||||
_activePoolsByEpoch;
|
||||
|
||||
/// @dev Number of pools activated in the current epoch.
|
||||
uint256 internal numActivePoolsThisEpoch;
|
||||
uint256 public 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 internal unfinalizedRewardsAvailable;
|
||||
uint256 public unfinalizedRewardsAvailable;
|
||||
|
||||
/// @dev The number of active pools in the last epoch that have yet to be
|
||||
/// finalized through `finalizePools()`.
|
||||
uint256 internal unfinalizedPoolsRemaining;
|
||||
uint256 public unfinalizedPoolsRemaining;
|
||||
|
||||
/// @dev The total fees collected for the epoch being finalized.
|
||||
uint256 internal unfinalizedTotalFeesCollected;
|
||||
uint256 public unfinalizedTotalFeesCollected;
|
||||
|
||||
/// @dev The total fees collected for the epoch being finalized.
|
||||
uint256 internal unfinalizedTotalWeightedStake;
|
||||
uint256 public unfinalizedTotalWeightedStake;
|
||||
|
||||
/// @dev How many rewards were paid at the end of finalization.
|
||||
uint256 totalRewardsPaidLastEpoch;
|
||||
|
@@ -42,13 +42,14 @@ interface IEthVault {
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
/// @dev Deposit an `amount` of ETH from `owner` into the vault.
|
||||
/// Note that only the Staking contract can call this.
|
||||
/// Note that this can only be called when *not* in Catostrophic Failure mode.
|
||||
/// @param owner of ETH Tokens.
|
||||
function depositFor(address owner)
|
||||
external
|
||||
payable;
|
||||
/// @dev Record a deposit of an amount of ETH for `owner` into the vault.
|
||||
/// The staking contract should pay this contract the ETH owed in the
|
||||
/// same transaction.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param owner Owner of the ETH.
|
||||
/// @param amount Amount of deposit.
|
||||
function recordDepositFor(address owner, uint256 amount)
|
||||
external;
|
||||
|
||||
/// @dev Withdraw an `amount` of ETH to `msg.sender` from the vault.
|
||||
/// Note that only the Staking contract can call this.
|
||||
|
@@ -48,8 +48,8 @@ interface IStakingEvents {
|
||||
/// @param epoch The epoch in which the pool was activated.
|
||||
/// @param poolId The ID of the pool.
|
||||
event StakingPoolActivated(
|
||||
uint256 epoch,
|
||||
bytes32 poolId
|
||||
uint256 indexed epoch,
|
||||
bytes32 indexed poolId
|
||||
);
|
||||
|
||||
/// @dev Emitted by MixinFinalizer when an epoch has ended.
|
||||
@@ -59,7 +59,7 @@ interface IStakingEvents {
|
||||
/// @param totalWeightedStake Total weighted stake across all active pools.
|
||||
/// @param totalFeesCollected Total fees collected across all active pools.
|
||||
event EpochEnded(
|
||||
uint256 epoch,
|
||||
uint256 indexed epoch,
|
||||
uint256 numActivePools,
|
||||
uint256 rewardsAvailable,
|
||||
uint256 totalFeesCollected,
|
||||
@@ -71,7 +71,7 @@ interface IStakingEvents {
|
||||
/// @param rewardsPaid Total amount of rewards paid out.
|
||||
/// @param rewardsRemaining Rewards left over.
|
||||
event EpochFinalized(
|
||||
uint256 epoch,
|
||||
uint256 indexed epoch,
|
||||
uint256 rewardsPaid,
|
||||
uint256 rewardsRemaining
|
||||
);
|
||||
@@ -82,8 +82,8 @@ interface IStakingEvents {
|
||||
/// @param operatorReward Amount of reward paid to pool operator.
|
||||
/// @param membersReward Amount of reward paid to pool members.
|
||||
event RewardsPaid(
|
||||
uint256 epoch,
|
||||
bytes32 poolId,
|
||||
uint256 indexed epoch,
|
||||
bytes32 indexed poolId,
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward
|
||||
);
|
||||
|
@@ -33,34 +33,35 @@ interface IStakingPoolRewardVault {
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
/// @dev Emitted when a reward is transferred to the ETH vault.
|
||||
/// @param amount The amount in ETH withdrawn.
|
||||
/// @param member of the pool.
|
||||
/// @param poolId The pool the reward was deposited for.
|
||||
event PoolRewardTransferredToEthVault(
|
||||
/// @dev Emitted when rewards are transferred out fo the vault.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param to Address to send funds to.
|
||||
/// @param amount Amount of ETH to transfer.
|
||||
event PoolRewardTransferred(
|
||||
bytes32 indexed poolId,
|
||||
address indexed member,
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
/// @dev Deposit an amount of ETH (`msg.value`) for `poolId` into the vault.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param poolId that owns the ETH.
|
||||
function depositFor(bytes32 poolId)
|
||||
external
|
||||
payable;
|
||||
|
||||
/// @dev Withdraw some amount in ETH of a pool member.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @dev Record a deposit of an amount of ETH for `poolId` into the vault.
|
||||
/// The staking contract should pay this contract the ETH owed in the
|
||||
/// same transaction.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param poolId Pool that holds the ETH.
|
||||
/// @param amount Amount of deposit.
|
||||
function recordDepositFor(bytes32 poolId, uint256 amount)
|
||||
external;
|
||||
|
||||
/// @dev Withdraw some amount in ETH from a pool.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param member of pool to transfer funds to.
|
||||
/// @param amount Amount in ETH to transfer.
|
||||
/// @param ethVaultAddress address of Eth Vault to send rewards to.
|
||||
function transferToEthVault(
|
||||
/// @param to Address to send funds to.
|
||||
/// @param amount Amount of ETH to transfer.
|
||||
function transfer(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
uint256 amount,
|
||||
address ethVaultAddress
|
||||
address payable to,
|
||||
uint256 amount
|
||||
)
|
||||
external;
|
||||
|
||||
|
@@ -32,16 +32,6 @@ interface IStructs {
|
||||
uint256 membersStake;
|
||||
}
|
||||
|
||||
/// @dev Rewards credited to a pool during finalization.
|
||||
/// @param operatorReward The amount of reward credited to the pool operator.
|
||||
/// @param membersReward The amount of reward credited to the pool members.
|
||||
/// @param membersStake The amount of members/delegated stake in the pool.
|
||||
struct PoolRewards {
|
||||
uint256 operatorReward;
|
||||
uint256 membersReward;
|
||||
uint256 membersStake;
|
||||
}
|
||||
|
||||
/// @dev Encapsulates a balance for the current and next epochs.
|
||||
/// Note that these balances may be stale if the current epoch
|
||||
/// is greater than `currentEpoch`.
|
||||
|
@@ -27,16 +27,24 @@ import "../libs/LibStakingRichErrors.sol";
|
||||
|
||||
/// @dev This mixin contains logic for managing ZRX tokens and Stake.
|
||||
contract MixinStake is
|
||||
IStakingEvents,
|
||||
MixinAbstract,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards,
|
||||
MixinStakingPool
|
||||
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Stake ZRX tokens. Tokens are deposited into the ZRX Vault. Unstake to retrieve the ZRX.
|
||||
/// Stake is in the 'Active' status.
|
||||
/// @dev Stake ZRX tokens. Tokens are deposited into the ZRX Vault.
|
||||
/// Unstake to retrieve the ZRX. Stake is in the 'Active' status.
|
||||
/// @param amount of ZRX to stake.
|
||||
function stake(uint256 amount)
|
||||
external
|
||||
@@ -59,8 +67,9 @@ contract MixinStake is
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Unstake. Tokens are withdrawn from the ZRX Vault and returned to the owner.
|
||||
/// Stake must be in the 'inactive' status for at least one full epoch to unstake.
|
||||
/// @dev Unstake. Tokens are withdrawn from the ZRX Vault and returned to
|
||||
/// the owner. Stake must be in the 'inactive' status for at least
|
||||
/// one full epoch to unstake.
|
||||
/// @param amount of ZRX to unstake.
|
||||
function unstake(uint256 amount)
|
||||
external
|
||||
@@ -85,7 +94,8 @@ contract MixinStake is
|
||||
_decrementCurrentAndNextBalance(globalStakeByStatus[uint8(IStructs.StakeStatus.INACTIVE)], amount);
|
||||
|
||||
// update withdrawable field
|
||||
_withdrawableStakeByOwner[owner] = currentWithdrawableStake.safeSub(amount);
|
||||
_withdrawableStakeByOwner[owner] =
|
||||
currentWithdrawableStake.safeSub(amount);
|
||||
|
||||
// withdraw equivalent amount of ZRX from vault
|
||||
zrxVault.withdrawFrom(owner, amount);
|
||||
@@ -110,16 +120,20 @@ contract MixinStake is
|
||||
external
|
||||
{
|
||||
// sanity check - do nothing if moving stake between the same status
|
||||
if (from.status != IStructs.StakeStatus.DELEGATED && from.status == to.status) {
|
||||
if (from.status != IStructs.StakeStatus.DELEGATED
|
||||
&& from.status == to.status)
|
||||
{
|
||||
return;
|
||||
} else if (from.status == IStructs.StakeStatus.DELEGATED && from.poolId == to.poolId) {
|
||||
} else if (from.status == IStructs.StakeStatus.DELEGATED
|
||||
&& from.poolId == to.poolId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address payable owner = msg.sender;
|
||||
|
||||
// handle delegation; this must be done before moving stake as the current
|
||||
// (out-of-sync) status is used during delegation.
|
||||
// handle delegation; this must be done before moving stake as the
|
||||
// current (out-of-sync) status is used during delegation.
|
||||
if (from.status == IStructs.StakeStatus.DELEGATED) {
|
||||
_undelegateStake(
|
||||
from.poolId,
|
||||
@@ -136,14 +150,18 @@ contract MixinStake is
|
||||
);
|
||||
}
|
||||
|
||||
// cache the current withdrawal amount, which may change if we're moving out of the inactive status.
|
||||
uint256 withdrawableStake = (from.status == IStructs.StakeStatus.INACTIVE)
|
||||
// cache the current withdrawal amount, which may change if we're
|
||||
// moving out of the inactive status.
|
||||
uint256 withdrawableStake =
|
||||
(from.status == IStructs.StakeStatus.INACTIVE)
|
||||
? getWithdrawableStake(owner)
|
||||
: 0;
|
||||
|
||||
// execute move
|
||||
IStructs.StoredBalance storage fromPtr = _getBalancePtrFromStatus(owner, from.status);
|
||||
IStructs.StoredBalance storage toPtr = _getBalancePtrFromStatus(owner, to.status);
|
||||
IStructs.StoredBalance storage fromPtr =
|
||||
_getBalancePtrFromStatus(owner, from.status);
|
||||
IStructs.StoredBalance storage toPtr =
|
||||
_getBalancePtrFromStatus(owner, to.status);
|
||||
_moveStake(fromPtr, toPtr, amount);
|
||||
|
||||
// update global total of stake in the statuses being moved between
|
||||
@@ -155,7 +173,8 @@ contract MixinStake is
|
||||
|
||||
// update withdrawable field, if necessary
|
||||
if (from.status == IStructs.StakeStatus.INACTIVE) {
|
||||
_withdrawableStakeByOwner[owner] = _computeWithdrawableStake(owner, withdrawableStake);
|
||||
_withdrawableStakeByOwner[owner] =
|
||||
_computeWithdrawableStake(owner, withdrawableStake);
|
||||
}
|
||||
|
||||
// notify
|
||||
@@ -171,8 +190,8 @@ contract MixinStake is
|
||||
|
||||
/// @dev Delegates a owners stake to a staking pool.
|
||||
/// @param poolId Id of pool to delegate to.
|
||||
/// @param owner who wants to delegate.
|
||||
/// @param amount of stake to delegate.
|
||||
/// @param owner Owner who wants to delegate.
|
||||
/// @param amount Amount of stake to delegate.
|
||||
function _delegateStake(
|
||||
bytes32 poolId,
|
||||
address payable owner,
|
||||
@@ -180,27 +199,38 @@ contract MixinStake is
|
||||
)
|
||||
private
|
||||
{
|
||||
// sanity check the pool we're delegating to exists
|
||||
// Sanity check the pool we're delegating to exists.
|
||||
_assertStakingPoolExists(poolId);
|
||||
|
||||
// cache amount delegated to pool by owner
|
||||
IStructs.StoredBalance memory initDelegatedStakeToPoolByOwner = _loadUnsyncedBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
// Cache amount delegated to pool by owner.
|
||||
IStructs.StoredBalance memory initDelegatedStakeToPoolByOwner =
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
|
||||
// increment how much stake the owner has delegated to the input pool
|
||||
_incrementNextBalance(_delegatedStakeToPoolByOwner[owner][poolId], amount);
|
||||
// Increment how much stake the owner has delegated to the input pool.
|
||||
_incrementNextBalance(
|
||||
_delegatedStakeToPoolByOwner[owner][poolId],
|
||||
amount
|
||||
);
|
||||
|
||||
// increment how much stake has been delegated to pool
|
||||
// Increment how much stake has been delegated to pool.
|
||||
_incrementNextBalance(_delegatedStakeByPoolId[poolId], amount);
|
||||
|
||||
// synchronizes reward state in the pool that the staker is delegating to
|
||||
IStructs.StoredBalance memory finalDelegatedStakeToPoolByOwner = _loadAndSyncBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
_syncRewardsForDelegator(poolId, owner, initDelegatedStakeToPoolByOwner, finalDelegatedStakeToPoolByOwner);
|
||||
// Synchronizes reward state in the pool that the staker is delegating
|
||||
// to.
|
||||
IStructs.StoredBalance memory finalDelegatedStakeToPoolByOwner =
|
||||
_loadAndSyncBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
_syncRewardsForDelegator(
|
||||
poolId,
|
||||
owner,
|
||||
initDelegatedStakeToPoolByOwner,
|
||||
finalDelegatedStakeToPoolByOwner
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Un-Delegates a owners stake from a staking pool.
|
||||
/// @param poolId Id of pool to un-delegate from.
|
||||
/// @param owner who wants to un-delegate.
|
||||
/// @param amount of stake to un-delegate.
|
||||
/// @param owner Owner who wants to un-delegate.
|
||||
/// @param amount Amount of stake to un-delegate.
|
||||
function _undelegateStake(
|
||||
bytes32 poolId,
|
||||
address payable owner,
|
||||
@@ -212,24 +242,38 @@ contract MixinStake is
|
||||
_assertStakingPoolExists(poolId);
|
||||
|
||||
// cache amount delegated to pool by owner
|
||||
IStructs.StoredBalance memory initDelegatedStakeToPoolByOwner = _loadUnsyncedBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
IStructs.StoredBalance memory initDelegatedStakeToPoolByOwner =
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
|
||||
// decrement how much stake the owner has delegated to the input pool
|
||||
_decrementNextBalance(_delegatedStakeToPoolByOwner[owner][poolId], amount);
|
||||
_decrementNextBalance(
|
||||
_delegatedStakeToPoolByOwner[owner][poolId],
|
||||
amount
|
||||
);
|
||||
|
||||
// decrement how much stake has been delegated to pool
|
||||
_decrementNextBalance(_delegatedStakeByPoolId[poolId], amount);
|
||||
|
||||
// synchronizes reward state in the pool that the staker is undelegating from
|
||||
IStructs.StoredBalance memory finalDelegatedStakeToPoolByOwner = _loadAndSyncBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
_syncRewardsForDelegator(poolId, owner, initDelegatedStakeToPoolByOwner, finalDelegatedStakeToPoolByOwner);
|
||||
// synchronizes reward state in the pool that the staker is undelegating
|
||||
// from
|
||||
IStructs.StoredBalance memory finalDelegatedStakeToPoolByOwner =
|
||||
_loadAndSyncBalance(_delegatedStakeToPoolByOwner[owner][poolId]);
|
||||
_syncRewardsForDelegator(
|
||||
poolId,
|
||||
owner,
|
||||
initDelegatedStakeToPoolByOwner,
|
||||
finalDelegatedStakeToPoolByOwner
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Returns a storage pointer to a user's stake in a given status.
|
||||
/// @param owner of stake to query.
|
||||
/// @param status of user's stake to lookup.
|
||||
/// @return a storage pointer to the corresponding stake stake
|
||||
function _getBalancePtrFromStatus(address owner, IStructs.StakeStatus status)
|
||||
/// @param owner Owner of stake to query.
|
||||
/// @param status Status of user's stake to lookup.
|
||||
/// @return storage A storage pointer to the corresponding stake stake
|
||||
function _getBalancePtrFromStatus(
|
||||
address owner,
|
||||
IStructs.StakeStatus status
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (IStructs.StoredBalance storage)
|
||||
|
@@ -27,6 +27,11 @@ import "./MixinStakeStorage.sol";
|
||||
/// @dev This mixin contains logic for querying stake balances.
|
||||
/// **** Read MixinStake before continuing ****
|
||||
contract MixinStakeBalances is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
@@ -26,6 +26,10 @@ import "../sys/MixinScheduler.sol";
|
||||
|
||||
/// @dev This mixin contains logic for managing stake storage.
|
||||
contract MixinStakeStorage is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinScheduler
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
@@ -25,6 +25,12 @@ import "../stake/MixinStakeBalances.sol";
|
||||
|
||||
|
||||
contract MixinCumulativeRewards is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakeBalances
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
@@ -34,7 +40,7 @@ contract MixinCumulativeRewards is
|
||||
function _initializeCumulativeRewards(bytes32 poolId)
|
||||
internal
|
||||
{
|
||||
// sets the default cumulative reward
|
||||
// Sets the default cumulative reward
|
||||
_forceSetCumulativeReward(
|
||||
poolId,
|
||||
currentEpoch,
|
||||
@@ -51,31 +57,35 @@ contract MixinCumulativeRewards is
|
||||
pure
|
||||
returns (bool)
|
||||
{
|
||||
// we use the denominator as a proxy for whether the cumulative
|
||||
// We use the denominator as a proxy for whether the cumulative
|
||||
// reward is set, as setting the cumulative reward always sets this
|
||||
// field to at least 1.
|
||||
return cumulativeReward.denominator != 0;
|
||||
}
|
||||
|
||||
/// Returns true iff the cumulative reward for `poolId` at `epoch` can be unset.
|
||||
/// @dev Returns true iff the cumulative reward for `poolId` at `epoch` can
|
||||
/// be unset.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param epoch of the cumulative reward.
|
||||
/// @param epoch Epoch of the cumulative reward.
|
||||
function _canUnsetCumulativeReward(bytes32 poolId, uint256 epoch)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return (
|
||||
_isCumulativeRewardSet(_cumulativeRewardsByPool[poolId][epoch]) && // is there a value to unset
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch] == 0 && // no references to this CR
|
||||
_cumulativeRewardsByPoolLastStored[poolId] > epoch // this is *not* the most recent CR
|
||||
// Is there a value to unset
|
||||
_isCumulativeRewardSet(_cumulativeRewardsByPool[poolId][epoch]) &&
|
||||
// No references to this CR
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch] == 0 &&
|
||||
// This is *not* the most recent CR
|
||||
_cumulativeRewardsByPoolLastStored[poolId] > epoch
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Tries to set a cumulative reward for `poolId` at `epoch`.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param epoch of cumulative reward.
|
||||
/// @param value of cumulative reward.
|
||||
/// @param epoch Epoch of cumulative reward.
|
||||
/// @param value Value of cumulative reward.
|
||||
function _trySetCumulativeReward(
|
||||
bytes32 poolId,
|
||||
uint256 epoch,
|
||||
@@ -84,7 +94,7 @@ contract MixinCumulativeRewards is
|
||||
internal
|
||||
{
|
||||
if (_isCumulativeRewardSet(_cumulativeRewardsByPool[poolId][epoch])) {
|
||||
// do nothing; we don't want to override the current value
|
||||
// Do nothing; we don't want to override the current value
|
||||
return;
|
||||
}
|
||||
_forceSetCumulativeReward(poolId, epoch, value);
|
||||
@@ -93,8 +103,8 @@ contract MixinCumulativeRewards is
|
||||
/// @dev Sets a cumulative reward for `poolId` at `epoch`.
|
||||
/// This can be used to overwrite an existing value.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param epoch of cumulative reward.
|
||||
/// @param value of cumulative reward.
|
||||
/// @param epoch Epoch of cumulative reward.
|
||||
/// @param value Value of cumulative reward.
|
||||
function _forceSetCumulativeReward(
|
||||
bytes32 poolId,
|
||||
uint256 epoch,
|
||||
@@ -108,7 +118,7 @@ contract MixinCumulativeRewards is
|
||||
|
||||
/// @dev Tries to unset the cumulative reward for `poolId` at `epoch`.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param epoch of cumulative reward to unset.
|
||||
/// @param epoch Epoch of cumulative reward to unset.
|
||||
function _tryUnsetCumulativeReward(bytes32 poolId, uint256 epoch)
|
||||
internal
|
||||
{
|
||||
@@ -120,11 +130,11 @@ contract MixinCumulativeRewards is
|
||||
|
||||
/// @dev Unsets the cumulative reward for `poolId` at `epoch`.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param epoch of cumulative reward to unset.
|
||||
/// @param epoch Epoch of cumulative reward to unset.
|
||||
function _forceUnsetCumulativeReward(bytes32 poolId, uint256 epoch)
|
||||
internal
|
||||
{
|
||||
_cumulativeRewardsByPool[poolId][epoch] = IStructs.Fraction({numerator: 0, denominator: 0});
|
||||
delete _cumulativeRewardsByPool[poolId][epoch];
|
||||
}
|
||||
|
||||
/// @dev Returns info on most recent cumulative reward.
|
||||
@@ -133,31 +143,38 @@ contract MixinCumulativeRewards is
|
||||
view
|
||||
returns (IStructs.CumulativeRewardInfo memory)
|
||||
{
|
||||
// fetch the last epoch at which we stored a cumulative reward for this pool
|
||||
uint256 cumulativeRewardsLastStored = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
// Fetch the last epoch at which we stored a cumulative reward for
|
||||
// this pool
|
||||
uint256 cumulativeRewardsLastStored =
|
||||
_cumulativeRewardsByPoolLastStored[poolId];
|
||||
|
||||
// query and return cumulative reward info for this pool
|
||||
// Query and return cumulative reward info for this pool
|
||||
return IStructs.CumulativeRewardInfo({
|
||||
cumulativeReward: _cumulativeRewardsByPool[poolId][cumulativeRewardsLastStored],
|
||||
cumulativeReward:
|
||||
_cumulativeRewardsByPool[poolId][cumulativeRewardsLastStored],
|
||||
cumulativeRewardEpoch: cumulativeRewardsLastStored
|
||||
});
|
||||
}
|
||||
|
||||
/// @dev Tries to set the epoch of the most recent cumulative reward.
|
||||
/// The value will only be set if the input epoch is greater than the current
|
||||
/// most recent value.
|
||||
/// The value will only be set if the input epoch is greater than the
|
||||
/// current most recent value.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param epoch of the most recent cumulative reward.
|
||||
function _trySetMostRecentCumulativeRewardEpoch(bytes32 poolId, uint256 epoch)
|
||||
/// @param epoch Epoch of the most recent cumulative reward.
|
||||
function _trySetMostRecentCumulativeRewardEpoch(
|
||||
bytes32 poolId,
|
||||
uint256 epoch
|
||||
)
|
||||
internal
|
||||
{
|
||||
// check if we should do any work
|
||||
uint256 currentMostRecentEpoch = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
// Check if we should do any work
|
||||
uint256 currentMostRecentEpoch =
|
||||
_cumulativeRewardsByPoolLastStored[poolId];
|
||||
if (epoch == currentMostRecentEpoch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update state to reflect the most recent cumulative reward
|
||||
// Update state to reflect the most recent cumulative reward
|
||||
_forceSetMostRecentCumulativeRewardEpoch(
|
||||
poolId,
|
||||
currentMostRecentEpoch,
|
||||
@@ -167,8 +184,10 @@ contract MixinCumulativeRewards is
|
||||
|
||||
/// @dev Forcefully sets the epoch of the most recent cumulative reward.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param currentMostRecentEpoch of the most recent cumulative reward.
|
||||
/// @param newMostRecentEpoch of the new most recent cumulative reward.
|
||||
/// @param currentMostRecentEpoch Epoch of the most recent cumulative
|
||||
/// reward.
|
||||
/// @param newMostRecentEpoch Epoch of the new most recent cumulative
|
||||
/// reward.
|
||||
function _forceSetMostRecentCumulativeRewardEpoch(
|
||||
bytes32 poolId,
|
||||
uint256 currentMostRecentEpoch,
|
||||
@@ -176,19 +195,21 @@ contract MixinCumulativeRewards is
|
||||
)
|
||||
internal
|
||||
{
|
||||
// sanity check that we're not trying to go back in time
|
||||
// Sanity check that we're not trying to go back in time
|
||||
assert(newMostRecentEpoch >= currentMostRecentEpoch);
|
||||
_cumulativeRewardsByPoolLastStored[poolId] = newMostRecentEpoch;
|
||||
|
||||
// unset the previous most recent reward, if it is no longer needed
|
||||
// Unset the previous most recent reward, if it is no longer needed
|
||||
_tryUnsetCumulativeReward(poolId, currentMostRecentEpoch);
|
||||
}
|
||||
|
||||
/// @dev Adds a dependency on a cumulative reward for a given epoch.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param epoch to remove dependency from.
|
||||
/// @param mostRecentCumulativeRewardInfo Info for the most recent cumulative reward (value and epoch)
|
||||
/// @param isDependent True iff there is a dependency on the cumulative reward for `poolId` at `epoch`
|
||||
/// @param epoch Epoch to remove dependency from.
|
||||
/// @param mostRecentCumulativeRewardInfo Info for the most recent
|
||||
/// cumulative reward (value and epoch)
|
||||
/// @param isDependent True iff there is a dependency on the cumulative
|
||||
/// reward for `poolId` at `epoch`
|
||||
function _addOrRemoveDependencyOnCumulativeReward(
|
||||
bytes32 poolId,
|
||||
uint256 epoch,
|
||||
@@ -211,21 +232,100 @@ contract MixinCumulativeRewards is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Computes a member's reward over a given epoch interval.
|
||||
/// @param poolId Uniqud Id of pool.
|
||||
/// @param memberStakeOverInterval Stake delegated to pool by member over
|
||||
/// the interval.
|
||||
/// @param beginEpoch Beginning of interval.
|
||||
/// @param endEpoch End of interval.
|
||||
/// @return rewards Reward accumulated over interval [beginEpoch, endEpoch]
|
||||
function _computeMemberRewardOverInterval(
|
||||
bytes32 poolId,
|
||||
uint256 memberStakeOverInterval,
|
||||
uint256 beginEpoch,
|
||||
uint256 endEpoch
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256 reward)
|
||||
{
|
||||
if (memberStakeOverInterval == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sanity check interval
|
||||
if (beginEpoch > endEpoch) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalError(
|
||||
LibStakingRichErrors
|
||||
.CumulativeRewardIntervalErrorCode
|
||||
.BeginEpochMustBeLessThanEndEpoch,
|
||||
poolId,
|
||||
beginEpoch,
|
||||
endEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Sanity check begin reward
|
||||
IStructs.Fraction memory beginReward =
|
||||
_cumulativeRewardsByPool[poolId][beginEpoch];
|
||||
if (!_isCumulativeRewardSet(beginReward)) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalError(
|
||||
LibStakingRichErrors
|
||||
.CumulativeRewardIntervalErrorCode
|
||||
.BeginEpochDoesNotHaveReward,
|
||||
poolId,
|
||||
beginEpoch,
|
||||
endEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Sanity check end reward
|
||||
IStructs.Fraction memory endReward =
|
||||
_cumulativeRewardsByPool[poolId][endEpoch];
|
||||
if (!_isCumulativeRewardSet(endReward)) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalError(
|
||||
LibStakingRichErrors
|
||||
.CumulativeRewardIntervalErrorCode
|
||||
.EndEpochDoesNotHaveReward,
|
||||
poolId,
|
||||
beginEpoch,
|
||||
endEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Compute reward
|
||||
reward = LibFractions.scaleFractionalDifference(
|
||||
endReward.numerator,
|
||||
endReward.denominator,
|
||||
beginReward.numerator,
|
||||
beginReward.denominator,
|
||||
memberStakeOverInterval
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Adds a dependency on a cumulative reward for a given epoch.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param epoch to remove dependency from.
|
||||
/// @param mostRecentCumulativeRewardInfo Info on the most recent cumulative reward.
|
||||
/// @param epoch Epoch to remove dependency from.
|
||||
/// @param mostRecentCumulativeRewardInfo Info on the most recent cumulative
|
||||
/// reward.
|
||||
function _addDependencyOnCumulativeReward(
|
||||
bytes32 poolId,
|
||||
uint256 epoch,
|
||||
IStructs.CumulativeRewardInfo memory mostRecentCumulativeRewardInfo
|
||||
)
|
||||
internal
|
||||
private
|
||||
{
|
||||
// add dependency by increasing the reference counter
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch] = _cumulativeRewardsByPoolReferenceCounter[poolId][epoch].safeAdd(1);
|
||||
// Add dependency by increasing the reference counter
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch] =
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch].safeAdd(1);
|
||||
|
||||
// set CR to most recent reward (if it is not already set)
|
||||
// Set CR to most recent reward (if it is not already set)
|
||||
_trySetCumulativeReward(
|
||||
poolId,
|
||||
epoch,
|
||||
@@ -235,88 +335,20 @@ contract MixinCumulativeRewards is
|
||||
|
||||
/// @dev Removes a dependency on a cumulative reward for a given epoch.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param epoch to remove dependency from.
|
||||
/// @param epoch Epoch to remove dependency from.
|
||||
function _removeDependencyOnCumulativeReward(
|
||||
bytes32 poolId,
|
||||
uint256 epoch
|
||||
)
|
||||
internal
|
||||
private
|
||||
{
|
||||
// remove dependency by decreasing reference counter
|
||||
uint256 newReferenceCounter = _cumulativeRewardsByPoolReferenceCounter[poolId][epoch].safeSub(1);
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch] = newReferenceCounter;
|
||||
// Remove dependency by decreasing reference counter
|
||||
uint256 newReferenceCounter =
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch].safeSub(1);
|
||||
_cumulativeRewardsByPoolReferenceCounter[poolId][epoch] =
|
||||
newReferenceCounter;
|
||||
|
||||
// clear cumulative reward from state, if it is no longer needed
|
||||
// Clear cumulative reward from state, if it is no longer needed
|
||||
_tryUnsetCumulativeReward(poolId, epoch);
|
||||
}
|
||||
|
||||
/// @dev Computes a member's reward over a given epoch interval.
|
||||
/// @param poolId Uniqud Id of pool.
|
||||
/// @param memberStakeOverInterval Stake delegated to pool by member over the interval.
|
||||
/// @param beginEpoch beginning of interval.
|
||||
/// @param endEpoch end of interval.
|
||||
/// @return rewards accumulated over interval [beginEpoch, endEpoch]
|
||||
function _computeMemberRewardOverInterval(
|
||||
bytes32 poolId,
|
||||
uint256 memberStakeOverInterval,
|
||||
uint256 beginEpoch,
|
||||
uint256 endEpoch
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
// sanity check inputs
|
||||
if (memberStakeOverInterval == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sanity check interval
|
||||
if (beginEpoch >= endEpoch) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalError(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalErrorCode.BeginEpochMustBeLessThanEndEpoch,
|
||||
poolId,
|
||||
beginEpoch,
|
||||
endEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// sanity check begin reward
|
||||
IStructs.Fraction memory beginReward = _cumulativeRewardsByPool[poolId][beginEpoch];
|
||||
if (!_isCumulativeRewardSet(beginReward)) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalError(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalErrorCode.BeginEpochDoesNotHaveReward,
|
||||
poolId,
|
||||
beginEpoch,
|
||||
endEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// sanity check end reward
|
||||
IStructs.Fraction memory endReward = _cumulativeRewardsByPool[poolId][endEpoch];
|
||||
if (!_isCumulativeRewardSet(endReward)) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalError(
|
||||
LibStakingRichErrors.CumulativeRewardIntervalErrorCode.EndEpochDoesNotHaveReward,
|
||||
poolId,
|
||||
beginEpoch,
|
||||
endEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// compute reward
|
||||
uint256 reward = LibFractions.scaleFractionalDifference(
|
||||
endReward.numerator,
|
||||
endReward.denominator,
|
||||
beginReward.numerator,
|
||||
beginReward.denominator,
|
||||
memberStakeOverInterval
|
||||
);
|
||||
return reward;
|
||||
}
|
||||
}
|
||||
|
@@ -28,8 +28,17 @@ import "./MixinStakingPoolMakers.sol";
|
||||
|
||||
|
||||
contract MixinStakingPool is
|
||||
IStakingEvents,
|
||||
MixinAbstract,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
@@ -113,17 +122,6 @@ contract MixinStakingPool is
|
||||
return poolById[poolId];
|
||||
}
|
||||
|
||||
/// @dev Look up the operator of a pool.
|
||||
/// @param poolId The ID of the pool.
|
||||
/// @return operatorAddress The pool operator.
|
||||
function getPoolOperator(bytes32 poolId)
|
||||
public
|
||||
view
|
||||
returns (address operatorAddress)
|
||||
{
|
||||
return rewardVault.operatorOf(poolId);
|
||||
}
|
||||
|
||||
/// @dev Computes the unique id that comes after the input pool id.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @return Next pool id after input pool.
|
||||
|
@@ -31,6 +31,8 @@ import "./MixinStakingPoolModifiers.sol";
|
||||
/// @dev This mixin contains logic for staking pools.
|
||||
contract MixinStakingPoolMakers is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers
|
||||
{
|
||||
|
@@ -23,6 +23,8 @@ import "../immutable/MixinStorage.sol";
|
||||
|
||||
|
||||
contract MixinStakingPoolModifiers is
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage
|
||||
{
|
||||
|
||||
|
@@ -27,31 +27,53 @@ import "../sys/MixinAbstract.sol";
|
||||
|
||||
|
||||
contract MixinStakingPoolRewards is
|
||||
MixinCumulativeRewards,
|
||||
MixinAbstract
|
||||
IStakingEvents,
|
||||
MixinAbstract,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Syncs rewards for a delegator. This includes transferring rewards from
|
||||
/// the Reward Vault to the Eth Vault, and adding/removing dependencies on cumulative rewards.
|
||||
/// This is used by a delegator when they want to sync their rewards without delegating/undelegating.
|
||||
/// It's effectively the same as delegating zero stake.
|
||||
function computeRewardBalanceOfOperator(bytes32 poolId, address operator)
|
||||
public
|
||||
view
|
||||
returns (uint256 reward)
|
||||
{
|
||||
// TODO.
|
||||
// unfinalizedStake +
|
||||
}
|
||||
|
||||
/// @dev Syncs rewards for a delegator. This includes transferring rewards
|
||||
/// from the Reward Vault to the Eth Vault, and adding/removing
|
||||
/// dependencies on cumulative rewards.
|
||||
/// This is used by a delegator when they want to sync their rewards
|
||||
/// without delegating/undelegating. It's effectively the same as
|
||||
/// delegating zero stake.
|
||||
/// @param poolId Unique id of pool.
|
||||
function syncDelegatorRewards(bytes32 poolId)
|
||||
external
|
||||
{
|
||||
address member = msg.sender;
|
||||
|
||||
IStructs.StoredBalance memory finalDelegatedStakeToPoolByOwner = _loadAndSyncBalance(_delegatedStakeToPoolByOwner[member][poolId]);
|
||||
IStructs.StoredBalance memory finalDelegatedStakeToPoolByOwner =
|
||||
_loadAndSyncBalance(_delegatedStakeToPoolByOwner[member][poolId]);
|
||||
_syncRewardsForDelegator(
|
||||
poolId,
|
||||
member,
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[member][poolId]), // initial balance
|
||||
// Initial balance
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[member][poolId]),
|
||||
finalDelegatedStakeToPoolByOwner
|
||||
);
|
||||
|
||||
// update stored balance with synchronized version; this prevents redundant withdrawals.
|
||||
_delegatedStakeToPoolByOwner[member][poolId] = finalDelegatedStakeToPoolByOwner;
|
||||
// Update stored balance with synchronized version; this prevents
|
||||
// redundant withdrawals.
|
||||
_delegatedStakeToPoolByOwner[member][poolId] =
|
||||
finalDelegatedStakeToPoolByOwner;
|
||||
}
|
||||
|
||||
/// @dev Computes the reward balance in ETH of a specific member of a pool.
|
||||
@@ -59,51 +81,38 @@ contract MixinStakingPoolRewards is
|
||||
/// @param member The member of the pool.
|
||||
/// @return totalReward Balance in ETH.
|
||||
function computeRewardBalanceOfDelegator(bytes32 poolId, address member)
|
||||
public
|
||||
external
|
||||
view
|
||||
returns (uint256 reward)
|
||||
{
|
||||
IStructs.PoolRewards memory unfinalizedPoolRewards =
|
||||
IStructs.Pool memory pool = poolById[poolId];
|
||||
// Get any unfinalized rewards.
|
||||
(uint256 unfinalizedTotalRewards, uint256 unfinalizedMembersStake) =
|
||||
_getUnfinalizedPoolRewards(poolId);
|
||||
// Get the members' portion.
|
||||
(, uint256 unfinalizedMembersReward) = _splitStakingPoolRewards(
|
||||
pool.operatorShare,
|
||||
unfinalizedTotalRewards,
|
||||
unfinalizedMembersStake
|
||||
);
|
||||
reward = _computeRewardBalanceOfDelegator(
|
||||
poolId,
|
||||
member,
|
||||
unfinalizedPoolRewards.membersReward,
|
||||
unfinalizedPoolRewards.membersStake
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Computes the reward balance in ETH of a specific member of a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param member The member of the pool.
|
||||
/// @param unfinalizedMembersReward Unfinalized memeber reward for
|
||||
/// this pool in the current epoch.
|
||||
/// @param unfinalizedDelegatedStake Unfinalized total delegated stake for
|
||||
/// this pool in the current epoch.
|
||||
/// @return totalReward Balance in ETH.
|
||||
function _computeRewardBalanceOfDelegator(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
uint256 unfinalizedMembersReward,
|
||||
uint256 unfinalizedDelegatedStake
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256 reward)
|
||||
{
|
||||
return _computeRewardBalanceOfDelegator(
|
||||
poolId,
|
||||
_loadUnsyncedBalance(_delegatedStakeToPoolByOwner[member][poolId]),
|
||||
currentEpoch
|
||||
currentEpoch,
|
||||
unfinalizedMembersReward,
|
||||
unfinalizedMembersStake
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Syncs rewards for a delegator. This includes transferring rewards from
|
||||
/// the Reward Vault to the Eth Vault, and adding/removing dependencies on cumulative rewards.
|
||||
/// @dev Syncs rewards for a delegator. This includes transferring rewards
|
||||
/// from the Reward Vault to the Eth Vault, and adding/removing
|
||||
/// dependencies on cumulative rewards.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param member of the pool.
|
||||
/// @param initialDelegatedStakeToPoolByOwner The member's delegated balance at the beginning of this transaction.
|
||||
/// @param finalDelegatedStakeToPoolByOwner The member's delegated balance at the end of this transaction.
|
||||
/// @param initialDelegatedStakeToPoolByOwner The member's delegated
|
||||
/// balance at the beginning of this transaction.
|
||||
/// @param finalDelegatedStakeToPoolByOwner The member's delegated balance
|
||||
/// at the end of this transaction.
|
||||
function _syncRewardsForDelegator(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
@@ -112,8 +121,9 @@ contract MixinStakingPoolRewards is
|
||||
)
|
||||
internal
|
||||
{
|
||||
// transfer any rewards from the transient pool vault to the eth vault;
|
||||
// this must be done before we can modify the owner's portion of the delegator pool.
|
||||
// Rransfer any rewards from the transient pool vault to the eth vault;
|
||||
// this must be done before we can modify the owner's portion of the
|
||||
// delegator pool.
|
||||
_transferDelegatorRewardsToEthVault(
|
||||
poolId,
|
||||
member,
|
||||
@@ -121,14 +131,16 @@ contract MixinStakingPoolRewards is
|
||||
currentEpoch
|
||||
);
|
||||
|
||||
// add dependencies on cumulative rewards for this epoch and the previous epoch, if necessary.
|
||||
// Add dependencies on cumulative rewards for this epoch and the next
|
||||
// epoch, if necessary.
|
||||
_setCumulativeRewardDependenciesForDelegator(
|
||||
poolId,
|
||||
finalDelegatedStakeToPoolByOwner,
|
||||
true
|
||||
);
|
||||
|
||||
// remove dependencies on previous cumulative rewards, if they are no longer needed.
|
||||
// Remove dependencies on previous cumulative rewards, if they are no
|
||||
// longer needed.
|
||||
_setCumulativeRewardDependenciesForDelegator(
|
||||
poolId,
|
||||
initialDelegatedStakeToPoolByOwner,
|
||||
@@ -136,193 +148,241 @@ contract MixinStakingPoolRewards is
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Handles a pool's reward. This will deposit the operator's reward into the Eth Vault and
|
||||
/// the members' reward into the Staking Pool Vault. It also records the cumulative reward, which
|
||||
/// is used to compute each delegator's portion of the members' reward.
|
||||
/// @dev Handles a pool's reward at the current epoch.
|
||||
/// This will compute the reward split and record the cumulative
|
||||
/// reward, which is used to compute each delegator's portion of the
|
||||
/// members' reward. It will NOT make any transfers to the eth or
|
||||
/// reward vaults. That should be done with a separate call to
|
||||
/// `_depositStakingPoolRewards()``.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param reward received by the pool.
|
||||
/// @param amountOfDelegatedStake the amount of delegated stake that will split the reward.
|
||||
/// @param epoch at which this was earned.
|
||||
function _handleStakingPoolReward(
|
||||
/// @param membersStake the amount of non-operator delegated stake that
|
||||
/// will split the reward.
|
||||
/// @return operatorReward
|
||||
function _recordStakingPoolRewards(
|
||||
bytes32 poolId,
|
||||
uint256 reward,
|
||||
uint256 amountOfDelegatedStake
|
||||
uint256 membersStake
|
||||
)
|
||||
internal
|
||||
returns (uint256 operatorReward, uint256 membersReward)
|
||||
{
|
||||
IStructs.Pool memory pool = poolById[poolId];
|
||||
|
||||
// compute the operator's portion of the reward and transfer it to the ETH vault (we round in favor of the operator).
|
||||
uint256 operatorPortion = amountOfDelegatedStake == 0
|
||||
? reward
|
||||
: LibMath.getPartialAmountCeil(
|
||||
uint256(pool.operatorShare),
|
||||
PPM_DENOMINATOR,
|
||||
reward
|
||||
);
|
||||
// Split the reward between operator and members
|
||||
(operatorReward, membersReward) =
|
||||
_splitStakingPoolRewards(pool.operatorShare, reward, membersStake);
|
||||
|
||||
ethVault.depositFor.value(operatorPortion)(pool.operator);
|
||||
// Record the operator's reward in the eth vault.
|
||||
ethVault.recordDepositFor(pool.operator, operatorReward);
|
||||
|
||||
// compute the reward portion for the pool members and transfer it to the Reward Vault.
|
||||
uint256 membersPortion = reward.safeSub(operatorPortion);
|
||||
if (membersPortion == 0) {
|
||||
return;
|
||||
if (membersReward == 0) {
|
||||
return (operatorReward, membersReward);
|
||||
}
|
||||
// Record the members reward in the reward vault.
|
||||
rewardVault.recordDepositFor(poolId, membersReward);
|
||||
|
||||
rewardVault.depositFor.value(membersPortion)(poolId);
|
||||
|
||||
// cache a storage pointer to the cumulative rewards for `poolId` indexed by epoch.
|
||||
mapping (uint256 => IStructs.Fraction) storage _cumulativeRewardsByPoolPtr = _cumulativeRewardsByPool[poolId];
|
||||
// Cache a storage pointer to the cumulative rewards for `poolId`
|
||||
// indexed by epoch.
|
||||
mapping (uint256 => IStructs.Fraction)
|
||||
storage
|
||||
_cumulativeRewardsByPoolPtr = _cumulativeRewardsByPool[poolId];
|
||||
|
||||
// Fetch the last epoch at which we stored an entry for this pool;
|
||||
// this is the most up-to-date cumulative rewards for this pool.
|
||||
uint256 cumulativeRewardsLastStored = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
IStructs.Fraction memory mostRecentCumulativeRewards = _cumulativeRewardsByPoolPtr[cumulativeRewardsLastStored];
|
||||
uint256 cumulativeRewardsLastStored =
|
||||
_cumulativeRewardsByPoolLastStored[poolId];
|
||||
IStructs.Fraction memory mostRecentCumulativeRewards =
|
||||
_cumulativeRewardsByPoolPtr[cumulativeRewardsLastStored];
|
||||
|
||||
// Compute new cumulative reward
|
||||
(uint256 numerator, uint256 denominator) = LibFractions.addFractions(
|
||||
mostRecentCumulativeRewards.numerator,
|
||||
mostRecentCumulativeRewards.denominator,
|
||||
membersPortion,
|
||||
amountOfDelegatedStake
|
||||
);
|
||||
|
||||
// Normalize fraction components by dividing by the minimum denominator.
|
||||
uint256 minDenominator =
|
||||
mostRecentCumulativeRewards.denominator <= amountOfDelegatedStake ?
|
||||
mostRecentCumulativeRewards.denominator :
|
||||
amountOfDelegatedStake;
|
||||
minDenominator = minDenominator == 0 ? 1 : minDenominator;
|
||||
(uint256 numeratorNormalized, uint256 denominatorNormalized) = (
|
||||
numerator.safeDiv(minDenominator),
|
||||
denominator.safeDiv(minDenominator)
|
||||
membersReward,
|
||||
membersStake
|
||||
);
|
||||
|
||||
// store cumulative rewards and set most recent
|
||||
_forceSetCumulativeReward(
|
||||
poolId,
|
||||
epoch,
|
||||
IStructs.Fraction({
|
||||
numerator: numeratorNormalized,
|
||||
denominator: denominatorNormalized
|
||||
})
|
||||
currentEpoch,
|
||||
IStructs.Fraction(numerator, denominator)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Transfers a delegators accumulated rewards from the transient pool Reward Pool vault
|
||||
/// to the Eth Vault. This is required before the member's stake in the pool can be
|
||||
/// modified.
|
||||
/// @dev Deposit rewards into the eth vault and reward vault for pool
|
||||
/// operators and members rewards, respectively. This should be called
|
||||
/// in tandem with `_recordStakingPoolRewards()`. We separate them
|
||||
/// so we can bath deposits, because ETH transfers are expensive.
|
||||
/// @param operatorReward Operator rewards.
|
||||
/// @param membersReward Operator rewards.
|
||||
function _depositStakingPoolRewards(
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward
|
||||
)
|
||||
internal
|
||||
{
|
||||
address(uint160(address(ethVault))).transfer(operatorReward);
|
||||
address(uint160(address(rewardVault))).transfer(membersReward);
|
||||
}
|
||||
|
||||
/// @dev Transfers a delegators accumulated rewards from the transient pool
|
||||
/// Reward Pool vault to the Eth Vault. This is required before the
|
||||
/// member's stake in the pool can be modified.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param member The member of the pool.
|
||||
/// @param unsyncedStake Unsynced stake of the delegator to the pool.
|
||||
function _transferDelegatorRewardsToEthVault(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
IStructs.StoredBalance memory unsyncedDelegatedStakeToPoolByOwner,
|
||||
IStructs.StoredBalance memory unsyncedStake,
|
||||
uint256 currentEpoch
|
||||
)
|
||||
private
|
||||
{
|
||||
// compute balance owed to delegator
|
||||
// Ensure the pool is finalized.
|
||||
_finalizePool(poolId);
|
||||
|
||||
// Compute balance owed to delegator
|
||||
uint256 balance = _computeRewardBalanceOfDelegator(
|
||||
poolId,
|
||||
unsyncedDelegatedStakeToPoolByOwner,
|
||||
currentEpoch
|
||||
unsyncedStake,
|
||||
currentEpoch,
|
||||
// No unfinalized values because we ensured the pool is already
|
||||
// finalized.
|
||||
0,
|
||||
0
|
||||
);
|
||||
if (balance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// transfer from transient Reward Pool vault to ETH Vault
|
||||
rewardVault.transferToEthVault(
|
||||
// Transfer from transient Reward Pool vault to ETH Vault
|
||||
ethVault.recordDepositFor(member, balance);
|
||||
rewardVault.transfer(
|
||||
poolId,
|
||||
member,
|
||||
balance,
|
||||
address(ethVault)
|
||||
address(uint160(address(ethVault))),
|
||||
balance
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Split a pool reward between the operator and members based on
|
||||
/// the `operatorShare` and `membersStake`.
|
||||
/// @param operatorShare The fraction of rewards owed to the operator,
|
||||
/// in PPM.
|
||||
/// @param totalReward The pool reward.
|
||||
/// @param membersStake The amount of member (non-operator) stake delegated
|
||||
/// to the pool in the epoch the rewards were earned.
|
||||
function _splitStakingPoolRewards(
|
||||
uint32 operatorShare,
|
||||
uint256 totalReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 operatorReward, uint256 membersReward)
|
||||
{
|
||||
if (membersStake == 0) {
|
||||
operatorReward = totalReward;
|
||||
} else {
|
||||
operatorReward = LibMath.getPartialAmountCeil(
|
||||
uint256(operatorShare),
|
||||
PPM_DENOMINATOR,
|
||||
totalReward
|
||||
);
|
||||
membersReward = totalReward - operatorReward;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Computes the reward balance in ETH of a specific member of a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param unsyncedDelegatedStakeToPoolByOwner Unsynced delegated stake to pool by owner
|
||||
/// @param unsyncedStake Unsynced delegated stake to pool by owner
|
||||
/// @param currentEpoch The epoch in which this call is executing
|
||||
/// @param unfinalizedMembersReward Unfinalized total members reward
|
||||
/// (if any).
|
||||
/// @param unfinalizedMembersStake Unfinalized total members stake (if any).
|
||||
/// @return totalReward Balance in ETH.
|
||||
function _computeRewardBalanceOfDelegator(
|
||||
bytes32 poolId,
|
||||
IStructs.StoredBalance memory unsyncedDelegatedStakeToPoolByOwner,
|
||||
uint256 currentEpoch
|
||||
IStructs.StoredBalance memory unsyncedStake,
|
||||
uint256 currentEpoch,
|
||||
uint256 unfinalizedMembersReward,
|
||||
uint256 unfinalizedMembersStake
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 totalReward)
|
||||
returns (uint256 reward)
|
||||
{
|
||||
uint256 currentEpoch = getCurrentEpoch();
|
||||
// There can be no rewards in epoch 0 because there is no delegated
|
||||
// stake.
|
||||
if (currentEpoch == 0) {
|
||||
return reward = 0;
|
||||
}
|
||||
|
||||
IStructs.StoredBalance memory stake =
|
||||
_loadUnsyncedBalance(delegatedStakeToPoolByOwner[member][poolId]);
|
||||
// There can be no rewards if the last epoch when stake was synced is
|
||||
// equal to the current epoch, because all prior rewards, including
|
||||
// rewards finalized this epoch have been claimed.
|
||||
if (stake.currentEpoch == currentEpoch) {
|
||||
if (unsyncedStake.currentEpoch == currentEpoch) {
|
||||
return reward = 0;
|
||||
}
|
||||
|
||||
// If there are unfinalized rewards this epoch, compute the member's
|
||||
// share.
|
||||
if (unfinalizedMembersReward != 0 && unfinalizedDelegatedStake != 0) {
|
||||
if (unfinalizedMembersReward != 0 && unfinalizedMembersStake != 0) {
|
||||
// Unfinalized rewards are always earned from stake in
|
||||
// the prior epoch so we want the stake at `currentEpoch-1`.
|
||||
uint256 _stake = stake.currentEpoch >= currentEpoch - 1 ?
|
||||
stake.currentEpochBalance :
|
||||
stake.nextEpochBalance;
|
||||
uint256 _stake = unsyncedStake.currentEpoch >= currentEpoch - 1 ?
|
||||
unsyncedStake.currentEpochBalance :
|
||||
unsyncedStake.nextEpochBalance;
|
||||
if (_stake != 0) {
|
||||
reward = _stake
|
||||
.safeMul(unfinalizedMembersReward)
|
||||
.safeDiv(unfinalizedDelegatedStake);
|
||||
.safeDiv(unfinalizedMembersStake);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the last epoch where a reward was credited to this pool.
|
||||
uint256 lastRewardEpoch = lastPoolRewardEpoch[poolId];
|
||||
// Get the last epoch where a reward was credited to this pool, which
|
||||
// also happens to be when we last created a cumulative reward entry.
|
||||
uint256 lastRewardEpoch = _cumulativeRewardsByPoolLastStored[poolId];
|
||||
|
||||
// If the stake has been touched since the last reward epoch,
|
||||
// it has already been claimed.
|
||||
if (stake.currentEpoch >= lastRewardEpoch) {
|
||||
if (unsyncedStake.currentEpoch >= lastRewardEpoch) {
|
||||
return reward;
|
||||
}
|
||||
// From here we know: `stake.currentEpoch < currentEpoch > 0`.
|
||||
// From here we know: `unsyncedStake.currentEpoch < currentEpoch > 0`.
|
||||
|
||||
if (stake.currentEpoch < lastRewardEpoch) {
|
||||
if (unsyncedStake.currentEpoch >= lastRewardEpoch) {
|
||||
return reward;
|
||||
}
|
||||
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
unsyncedStake.currentEpochBalance,
|
||||
unsyncedStake.currentEpoch,
|
||||
unsyncedStake.currentEpoch + 1
|
||||
)
|
||||
);
|
||||
if (unsyncedStake.currentEpoch + 1 < lastRewardEpoch) {
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
stake,
|
||||
stake.currentEpoch,
|
||||
stake.currentEpoch + 1
|
||||
unsyncedStake.nextEpochBalance,
|
||||
unsyncedStake.currentEpoch + 1,
|
||||
lastRewardEpoch
|
||||
)
|
||||
);
|
||||
if (stake.currentEpoch + 1 < lastRewardEpoch) {
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
stake,
|
||||
stake.currentEpoch + 1,
|
||||
lastRewardEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Adds or removes cumulative reward dependencies for a delegator.
|
||||
/// A delegator always depends on the cumulative reward for the current epoch.
|
||||
/// They will also depend on the previous epoch's reward, if they are already staked with the input pool.
|
||||
/// A delegator always depends on the cumulative reward for the current
|
||||
/// and next epoch, if they would still have stake in the next epoch.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param _delegatedStakeToPoolByOwner Amount of stake the member has delegated to the pool.
|
||||
/// @param _delegatedStakeToPoolByOwner Amount of stake the member has
|
||||
/// delegated to the pool.
|
||||
/// @param isDependent is true iff adding a dependency. False, otherwise.
|
||||
function _setCumulativeRewardDependenciesForDelegator(
|
||||
bytes32 poolId,
|
||||
@@ -331,26 +391,34 @@ contract MixinStakingPoolRewards is
|
||||
)
|
||||
private
|
||||
{
|
||||
// if this delegator is not yet initialized then there's no dependency to unset.
|
||||
// If this delegator is not yet initialized then there's no dependency
|
||||
// to unset.
|
||||
if (!isDependent && !_delegatedStakeToPoolByOwner.isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the most recent cumulative reward, which will serve as a reference point when updating dependencies
|
||||
IStructs.CumulativeRewardInfo memory mostRecentCumulativeRewardInfo = _getMostRecentCumulativeRewardInfo(poolId);
|
||||
// Get the most recent cumulative reward, which will serve as a
|
||||
// reference point when updating dependencies
|
||||
IStructs.CumulativeRewardInfo memory mostRecentCumulativeRewardInfo =
|
||||
_getMostRecentCumulativeRewardInfo(poolId);
|
||||
|
||||
// record dependency on `lastEpoch`
|
||||
if (_delegatedStakeToPoolByOwner.currentEpoch > 0 && _delegatedStakeToPoolByOwner.currentEpochBalance != 0) {
|
||||
// Record dependency on the next epoch
|
||||
uint256 nextEpoch = currentEpoch.safeAdd(1);
|
||||
if (_delegatedStakeToPoolByOwner.currentEpoch > 0
|
||||
&& _delegatedStakeToPoolByOwner.nextEpochBalance != 0)
|
||||
{
|
||||
_addOrRemoveDependencyOnCumulativeReward(
|
||||
poolId,
|
||||
uint256(_delegatedStakeToPoolByOwner.currentEpoch).safeSub(1),
|
||||
nextEpoch,
|
||||
mostRecentCumulativeRewardInfo,
|
||||
isDependent
|
||||
);
|
||||
}
|
||||
|
||||
// record dependency on current epoch.
|
||||
if (_delegatedStakeToPoolByOwner.currentEpochBalance != 0 || _delegatedStakeToPoolByOwner.nextEpochBalance != 0) {
|
||||
// Record dependency on current epoch.
|
||||
if (_delegatedStakeToPoolByOwner.currentEpochBalance != 0
|
||||
|| _delegatedStakeToPoolByOwner.nextEpochBalance != 0)
|
||||
{
|
||||
_addOrRemoveDependencyOnCumulativeReward(
|
||||
poolId,
|
||||
_delegatedStakeToPoolByOwner.currentEpoch,
|
||||
@@ -358,9 +426,5 @@ contract MixinStakingPoolRewards is
|
||||
isDependent
|
||||
);
|
||||
}
|
||||
uint256 nextEpoch = epoch.safeAdd(1);
|
||||
if (!_isCumulativeRewardSet(cumulativeRewardsByPoolPtr[nextEpoch])) {
|
||||
cumulativeRewardsByPoolPtr[nextEpoch] = mostRecentCumulativeRewards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -29,11 +29,16 @@ contract MixinAbstract {
|
||||
/// @dev Computes the reward owed to a pool during finalization.
|
||||
/// Does nothing if the pool is already finalized.
|
||||
/// @param poolId The pool's ID.
|
||||
/// @return rewards Amount of rewards for this pool.
|
||||
/// @return operatorReward The reward owed to the pool operator.
|
||||
/// @return membersStake The total stake for all non-operator members in
|
||||
/// this pool.
|
||||
function _getUnfinalizedPoolRewards(bytes32 poolId)
|
||||
internal
|
||||
view
|
||||
returns (IStructs.PoolRewards memory rewards);
|
||||
returns (
|
||||
uint256 reward,
|
||||
uint256 membersStake
|
||||
);
|
||||
|
||||
/// @dev Get an active pool from an epoch by its ID.
|
||||
/// @param epoch The epoch the pool was/will be active in.
|
||||
@@ -61,13 +66,20 @@ contract MixinAbstract {
|
||||
|
||||
/// @dev Instantly finalizes a single pool that was active in the previous
|
||||
/// epoch, crediting it rewards and sending those rewards to the reward
|
||||
/// vault. This can be called by internal functions that need to
|
||||
/// finalize a pool immediately. Does nothing if the pool is already
|
||||
/// and eth vault. This can be called by internal functions that need
|
||||
/// to finalize a pool immediately. Does nothing if the pool is already
|
||||
/// finalized. Does nothing if the pool was not active or was already
|
||||
/// finalized.
|
||||
/// @param poolId The pool ID to finalize.
|
||||
/// @return rewards Rewards.
|
||||
/// @return rewards The rewards credited to the pool.
|
||||
/// @return operatorReward The reward credited to the pool operator.
|
||||
/// @return membersReward The reward credited to the pool members.
|
||||
/// @return membersStake The total stake for all non-operator members in
|
||||
/// this pool.
|
||||
function _finalizePool(bytes32 poolId)
|
||||
internal
|
||||
returns (IStructs.PoolRewards memory rewards);
|
||||
returns (
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
);
|
||||
}
|
||||
|
@@ -31,7 +31,6 @@ import "../interfaces/IStakingEvents.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "../stake/MixinStakeBalances.sol";
|
||||
import "../staking_pools/MixinStakingPool.sol";
|
||||
import "../staking_pools/MixinStakingPoolRewardVault.sol";
|
||||
import "./MixinScheduler.sol";
|
||||
|
||||
|
||||
@@ -47,11 +46,10 @@ contract MixinFinalizer is
|
||||
MixinDeploymentConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinZrxVault,
|
||||
MixinScheduler,
|
||||
MixinStakingPoolRewardVault,
|
||||
MixinStakeStorage,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
@@ -67,7 +65,7 @@ contract MixinFinalizer is
|
||||
external
|
||||
returns (uint256 _unfinalizedPoolsRemaining)
|
||||
{
|
||||
uint256 closingEpoch = getCurrentEpoch();
|
||||
uint256 closingEpoch = currentEpoch;
|
||||
|
||||
// Make sure the previous epoch has been fully finalized.
|
||||
if (unfinalizedPoolsRemaining != 0) {
|
||||
@@ -114,9 +112,9 @@ contract MixinFinalizer is
|
||||
}
|
||||
|
||||
/// @dev Finalizes pools that were active in the previous epoch, paying out
|
||||
/// rewards to the reward vault. Keepers should call this function
|
||||
/// repeatedly until all active pools that were emitted in in a
|
||||
/// `StakingPoolActivated` in the prior epoch have been finalized.
|
||||
/// rewards to the reward and eth vault. Keepers should call this
|
||||
/// function repeatedly until all active pools that were emitted in in
|
||||
/// a `StakingPoolActivated` in the prior epoch have been finalized.
|
||||
/// Pools that have already been finalized will be silently ignored.
|
||||
/// We deliberately try not to revert here in case multiple parties
|
||||
/// are finalizing pools.
|
||||
@@ -126,7 +124,7 @@ contract MixinFinalizer is
|
||||
external
|
||||
returns (uint256 _unfinalizedPoolsRemaining)
|
||||
{
|
||||
uint256 epoch = getCurrentEpoch();
|
||||
uint256 epoch = currentEpoch;
|
||||
// There are no pools to finalize at epoch 0.
|
||||
if (epoch == 0) {
|
||||
return _unfinalizedPoolsRemaining = 0;
|
||||
@@ -144,8 +142,11 @@ contract MixinFinalizer is
|
||||
_getActivePoolsFromEpoch(epoch - 1);
|
||||
uint256 numPoolIds = poolIds.length;
|
||||
uint256 rewardsPaid = 0;
|
||||
uint256 totalOperatorRewardsPaid = 0;
|
||||
uint256 totalMembersRewardsPaid = 0;
|
||||
|
||||
for (uint256 i = 0; i != numPoolIds && poolsRemaining != 0; ++i) {
|
||||
for (uint256 i = 0; i != numPoolIds && poolsRemaining != 0; ++i)
|
||||
{
|
||||
bytes32 poolId = poolIds[i];
|
||||
IStructs.ActivePool memory pool = activePools[poolId];
|
||||
|
||||
@@ -154,12 +155,17 @@ contract MixinFinalizer is
|
||||
continue;
|
||||
}
|
||||
|
||||
IStructs.PoolRewards memory poolRewards =
|
||||
_creditRewardsToPool(epoch, poolId, pool);
|
||||
(uint256 operatorReward, uint256 membersReward) =
|
||||
_creditRewardsToPool(epoch, poolId, pool, rewardsPaid);
|
||||
|
||||
rewardsPaid = rewardsPaid.safeAdd(
|
||||
poolRewards.operatorReward + poolRewards.membersReward
|
||||
);
|
||||
totalOperatorRewardsPaid =
|
||||
totalOperatorRewardsPaid.safeAdd(operatorReward);
|
||||
totalMembersRewardsPaid =
|
||||
totalMembersRewardsPaid.safeAdd(membersReward);
|
||||
|
||||
rewardsPaid = rewardsPaid
|
||||
.safeAdd(operatorReward)
|
||||
.safeAdd(membersReward);
|
||||
|
||||
// Decrease the number of unfinalized pools left.
|
||||
poolsRemaining = poolsRemaining.safeSub(1);
|
||||
@@ -182,47 +188,52 @@ contract MixinFinalizer is
|
||||
);
|
||||
}
|
||||
|
||||
// Deposit all the rewards at once into the RewardVault.
|
||||
// Deposit all the rewards at once.
|
||||
if (rewardsPaid != 0) {
|
||||
_depositIntoStakingPoolRewardVault(rewardsPaid);
|
||||
_depositStakingPoolRewards(totalOperatorRewardsPaid, totalMembersRewardsPaid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @dev Instantly finalizes a single pool that was active in the previous
|
||||
/// epoch, crediting it rewards and sending those rewards to the reward
|
||||
/// vault. This can be called by internal functions that need to
|
||||
/// finalize a pool immediately. Does nothing if the pool is already
|
||||
/// and eth vault. This can be called by internal functions that need
|
||||
/// to finalize a pool immediately. Does nothing if the pool is already
|
||||
/// finalized. Does nothing if the pool was not active or was already
|
||||
/// finalized.
|
||||
/// @param poolId The pool ID to finalize.
|
||||
/// @return rewards Rewards.
|
||||
/// @return rewards The rewards credited to the pool.
|
||||
/// @return operatorReward The reward credited to the pool operator.
|
||||
/// @return membersReward The reward credited to the pool members.
|
||||
/// @return membersStake The total stake for all non-operator members in
|
||||
/// this pool.
|
||||
function _finalizePool(bytes32 poolId)
|
||||
internal
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
{
|
||||
uint256 epoch = getCurrentEpoch();
|
||||
uint256 epoch = currentEpoch;
|
||||
// There are no pools to finalize at epoch 0.
|
||||
if (epoch == 0) {
|
||||
return rewards;
|
||||
return (operatorReward, membersReward, membersStake);
|
||||
}
|
||||
|
||||
IStructs.ActivePool memory pool =
|
||||
_getActivePoolFromEpoch(epoch - 1, poolId);
|
||||
// Do nothing if the pool was not active (has no fees).
|
||||
if (pool.feesCollected == 0) {
|
||||
return rewards;
|
||||
return (operatorReward, membersReward, membersStake);
|
||||
}
|
||||
|
||||
rewards = _creditRewardsToPool(epoch, poolId, pool);
|
||||
uint256 totalReward =
|
||||
rewards.membersReward.safeAdd(rewards.operatorReward);
|
||||
(operatorReward, membersReward) =
|
||||
_creditRewardsToPool(epoch, poolId, pool, 0);
|
||||
uint256 totalReward = operatorReward.safeAdd(membersReward);
|
||||
|
||||
if (totalReward > 0) {
|
||||
totalRewardsPaidLastEpoch =
|
||||
totalRewardsPaidLastEpoch.safeAdd(totalReward);
|
||||
_depositIntoStakingPoolRewardVault(totalReward);
|
||||
_depositStakingPoolRewards(operatorReward, membersReward);
|
||||
}
|
||||
|
||||
// Decrease the number of unfinalized pools left.
|
||||
@@ -238,26 +249,32 @@ contract MixinFinalizer is
|
||||
unfinalizedRewardsAvailable.safeSub(totalRewardsPaidLastEpoch)
|
||||
);
|
||||
}
|
||||
membersStake = pool.membersStake;
|
||||
}
|
||||
|
||||
/// @dev Computes the reward owed to a pool during finalization.
|
||||
/// Does nothing if the pool is already finalized.
|
||||
/// @param poolId The pool's ID.
|
||||
/// @return rewards Amount of rewards for this pool.
|
||||
/// @return operatorReward The reward owed to the pool operator.
|
||||
/// @return membersStake The total stake for all non-operator members in
|
||||
/// this pool.
|
||||
function _getUnfinalizedPoolRewards(bytes32 poolId)
|
||||
internal
|
||||
view
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (
|
||||
uint256 reward,
|
||||
uint256 membersStake
|
||||
)
|
||||
{
|
||||
uint256 epoch = getCurrentEpoch();
|
||||
uint256 epoch = currentEpoch;
|
||||
// There are no pools to finalize at epoch 0.
|
||||
if (epoch == 0) {
|
||||
return rewards;
|
||||
return (reward, membersStake);
|
||||
}
|
||||
rewards = _getUnfinalizedPoolRewards(
|
||||
poolId,
|
||||
_getActivePoolFromEpoch(epoch - 1, poolId)
|
||||
);
|
||||
IStructs.ActivePool memory pool =
|
||||
_getActivePoolFromEpoch(epoch - 1, poolId);
|
||||
reward = _getUnfinalizedPoolRewards(pool, 0);
|
||||
membersStake = pool.membersStake;
|
||||
}
|
||||
|
||||
/// @dev Get an active pool from an epoch by its ID.
|
||||
@@ -287,11 +304,13 @@ contract MixinFinalizer is
|
||||
view
|
||||
returns (mapping (bytes32 => IStructs.ActivePool) storage activePools)
|
||||
{
|
||||
activePools = activePoolsByEpoch[epoch % 2];
|
||||
activePools = _activePoolsByEpoch[epoch % 2];
|
||||
}
|
||||
|
||||
/// @dev Converts the entire WETH balance of the contract into ETH.
|
||||
function _unwrapWETH() internal {
|
||||
function _unwrapWETH()
|
||||
internal
|
||||
{
|
||||
uint256 wethBalance = IEtherToken(WETH_ADDRESS)
|
||||
.balanceOf(address(this));
|
||||
if (wethBalance != 0) {
|
||||
@@ -299,69 +318,28 @@ contract MixinFinalizer is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Splits an amount between the pool operator and members of the
|
||||
/// pool based on the pool operator's share.
|
||||
/// @param poolId The ID of the pool.
|
||||
/// @param amount Amount to to split.
|
||||
/// @return operatorPortion Portion of `amount` attributed to the operator.
|
||||
/// @return membersPortion Portion of `amount` attributed to the pool.
|
||||
function _splitRewardAmountBetweenOperatorAndMembers(
|
||||
bytes32 poolId,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256 operatorReward, uint256 membersReward)
|
||||
{
|
||||
(operatorReward, membersReward) =
|
||||
rewardVault.splitAmountBetweenOperatorAndMembers(poolId, amount);
|
||||
}
|
||||
|
||||
/// @dev Record a deposit for a pool in the RewardVault.
|
||||
/// @param poolId ID of the pool.
|
||||
/// @param amount Amount in ETH to record.
|
||||
/// @param operatorOnly Only attribute amount to operator.
|
||||
/// @return operatorPortion Portion of `amount` attributed to the operator.
|
||||
/// @return membersPortion Portion of `amount` attributed to the pool.
|
||||
function _recordDepositInRewardVaultFor(
|
||||
bytes32 poolId,
|
||||
uint256 amount,
|
||||
bool operatorOnly
|
||||
)
|
||||
internal
|
||||
returns (
|
||||
uint256 operatorPortion,
|
||||
uint256 membersPortion
|
||||
)
|
||||
{
|
||||
(operatorPortion, membersPortion) = rewardVault.recordDepositFor(
|
||||
poolId,
|
||||
amount,
|
||||
operatorOnly
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Computes the reward owed to a pool during finalization.
|
||||
/// @param poolId The pool's ID.
|
||||
/// @param pool The active pool.
|
||||
/// @return rewards Amount of rewards for this pool.
|
||||
/// @param unpaidRewards Rewards that have been credited but not finalized.
|
||||
/// @return rewards Unfinalized rewards for this pool.
|
||||
function _getUnfinalizedPoolRewards(
|
||||
bytes32 poolId,
|
||||
IStructs.ActivePool memory pool
|
||||
IStructs.ActivePool memory pool,
|
||||
uint256 unpaidRewards
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (uint256 rewards)
|
||||
{
|
||||
// There can't be any rewards if the pool was active or if it has
|
||||
// no stake.
|
||||
if (pool.feesCollected == 0) {
|
||||
return rewards;
|
||||
return rewards = 0;
|
||||
}
|
||||
|
||||
uint256 unfinalizedRewardsAvailable_ = unfinalizedRewardsAvailable;
|
||||
// Use the cobb-douglas function to compute the total reward.
|
||||
uint256 totalReward = LibCobbDouglas._cobbDouglas(
|
||||
unfinalizedRewardsAvailable,
|
||||
rewards = LibCobbDouglas._cobbDouglas(
|
||||
unfinalizedRewardsAvailable_,
|
||||
pool.feesCollected,
|
||||
unfinalizedTotalFeesCollected,
|
||||
pool.weightedStake,
|
||||
@@ -370,17 +348,15 @@ contract MixinFinalizer is
|
||||
cobbDouglasAlphaDenominator
|
||||
);
|
||||
|
||||
// Split the reward between the operator and delegators.
|
||||
if (pool.membersStake == 0) {
|
||||
rewards.operatorReward = totalReward;
|
||||
} else {
|
||||
(rewards.operatorReward, rewards.membersReward) =
|
||||
_splitRewardAmountBetweenOperatorAndMembers(
|
||||
poolId,
|
||||
totalReward
|
||||
);
|
||||
// Clip the reward to always be under
|
||||
// `unfinalizedRewardsAvailable - totalRewardsPaid - unpaidRewards`,
|
||||
// in case cobb-douglas overflows, which should be unlikely.
|
||||
uint256 rewardsRemaining = unfinalizedRewardsAvailable_
|
||||
.safeSub(totalRewardsPaidLastEpoch)
|
||||
.safeSub(unpaidRewards);
|
||||
if (rewardsRemaining < rewards) {
|
||||
rewards = rewardsRemaining;
|
||||
}
|
||||
rewards.membersStake = pool.membersStake;
|
||||
}
|
||||
|
||||
/// @dev Credits finalization rewards to a pool that was active in the
|
||||
@@ -388,48 +364,41 @@ contract MixinFinalizer is
|
||||
/// @param epoch The current epoch.
|
||||
/// @param poolId The pool ID to finalize.
|
||||
/// @param pool The active pool to finalize.
|
||||
/// @param unpaidRewards Rewards that have been credited but not finalized.
|
||||
/// @return rewards Rewards.
|
||||
/// @return rewards The rewards credited to the pool.
|
||||
/// @return operatorReward The reward credited to the pool operator.
|
||||
/// @return membersReward The reward credited to the pool members.
|
||||
function _creditRewardsToPool(
|
||||
uint256 epoch,
|
||||
bytes32 poolId,
|
||||
IStructs.ActivePool memory pool
|
||||
IStructs.ActivePool memory pool,
|
||||
uint256 unpaidRewards
|
||||
)
|
||||
private
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (uint256 operatorReward, uint256 membersReward)
|
||||
{
|
||||
// Clear the pool state so we don't finalize it again, and to recoup
|
||||
// some gas.
|
||||
delete _getActivePoolsFromEpoch(epoch - 1)[poolId];
|
||||
|
||||
// Compute the rewards.
|
||||
rewards = _getUnfinalizedPoolRewards(poolId, pool);
|
||||
uint256 totalReward =
|
||||
rewards.membersReward.safeAdd(rewards.operatorReward);
|
||||
uint256 rewards = _getUnfinalizedPoolRewards(pool, unpaidRewards);
|
||||
|
||||
// Credit the pool the rewards in the RewardVault.
|
||||
_recordDepositInRewardVaultFor(
|
||||
// Credit the pool.
|
||||
// Note that we credit at the CURRENT epoch even though these rewards
|
||||
// were earned in the previous epoch.
|
||||
(operatorReward, membersReward) = _recordStakingPoolRewards(
|
||||
poolId,
|
||||
totalReward,
|
||||
// If no delegated stake, all rewards go to the operator.
|
||||
pool.membersStake == 0
|
||||
rewards,
|
||||
pool.membersStake
|
||||
);
|
||||
|
||||
// Sync delegator rewards.
|
||||
if (rewards.membersReward != 0) {
|
||||
_recordRewardForDelegators(
|
||||
poolId,
|
||||
rewards.membersReward,
|
||||
pool.membersStake
|
||||
);
|
||||
}
|
||||
|
||||
// Emit an event.
|
||||
emit RewardsPaid(
|
||||
epoch,
|
||||
poolId,
|
||||
rewards.operatorReward,
|
||||
rewards.membersReward
|
||||
operatorReward,
|
||||
membersReward
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -30,6 +30,8 @@ import "../libs/LibStakingRichErrors.sol";
|
||||
|
||||
contract MixinParams is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage
|
||||
{
|
||||
/// @dev Set all configurable parameters at once.
|
||||
|
@@ -33,6 +33,8 @@ import "../interfaces/IStakingEvents.sol";
|
||||
/// and consistent scheduling metric than time. TimeLocks, for example, are measured in epochs.
|
||||
contract MixinScheduler is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
@@ -26,6 +26,8 @@ import "./MixinVaultCore.sol";
|
||||
/// @dev This vault manages ETH.
|
||||
contract EthVault is
|
||||
IEthVault,
|
||||
IVaultCore,
|
||||
Ownable,
|
||||
MixinVaultCore
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
@@ -33,19 +35,21 @@ contract EthVault is
|
||||
// mapping from Owner to ETH balance
|
||||
mapping (address => uint256) internal _balances;
|
||||
|
||||
/// @dev Deposit an `amount` of ETH from `owner` into the vault.
|
||||
/// Note that only the Staking contract can call this.
|
||||
/// Note that this can only be called when *not* in Catostrophic Failure mode.
|
||||
/// @param owner of ETH Tokens.
|
||||
function depositFor(address owner)
|
||||
external
|
||||
payable
|
||||
{
|
||||
// update balance
|
||||
uint256 amount = msg.value;
|
||||
_balances[owner] = _balances[owner].safeAdd(msg.value);
|
||||
// solhint-disable no-empty-blocks
|
||||
/// @dev Payable fallback for bulk-deposits.
|
||||
function () payable external {}
|
||||
|
||||
// notify
|
||||
/// @dev Record a deposit of an amount of ETH for `owner` into the vault.
|
||||
/// The staking contract should pay this contract the ETH owed in the
|
||||
/// same transaction.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param owner Owner of the ETH.
|
||||
/// @param amount Amount of deposit.
|
||||
function recordDepositFor(address owner, uint256 amount)
|
||||
external
|
||||
onlyStakingProxy
|
||||
{
|
||||
_balances[owner] = _balances[owner].safeAdd(amount);
|
||||
emit EthDepositedIntoVault(msg.sender, owner, amount);
|
||||
}
|
||||
|
||||
|
@@ -25,14 +25,15 @@ import "../libs/LibStakingRichErrors.sol";
|
||||
import "../libs/LibSafeDowncast.sol";
|
||||
import "./MixinVaultCore.sol";
|
||||
import "../interfaces/IStakingPoolRewardVault.sol";
|
||||
import "../interfaces/IEthVault.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
|
||||
|
||||
/// @dev This vault manages staking pool rewards.
|
||||
contract StakingPoolRewardVault is
|
||||
IStakingPoolRewardVault,
|
||||
IVaultCore,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinVaultCore
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
@@ -40,38 +41,42 @@ contract StakingPoolRewardVault is
|
||||
// mapping from poolId to Pool metadata
|
||||
mapping (bytes32 => uint256) internal _balanceByPoolId;
|
||||
|
||||
/// @dev Deposit an amount of ETH (`msg.value`) for `poolId` into the vault.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param poolId that holds the ETH.
|
||||
function depositFor(bytes32 poolId)
|
||||
// solhint-disable no-empty-blocks
|
||||
/// @dev Payable fallback for bulk-deposits.
|
||||
function () payable external {}
|
||||
|
||||
/// @dev Record a deposit of an amount of ETH for `poolId` into the vault.
|
||||
/// The staking contract should pay this contract the ETH owed in the
|
||||
/// same transaction.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param poolId Pool that holds the ETH.
|
||||
/// @param amount Amount of deposit.
|
||||
function recordDepositFor(bytes32 poolId, uint256 amount)
|
||||
external
|
||||
payable
|
||||
onlyStakingProxy
|
||||
{
|
||||
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeAdd(msg.value);
|
||||
emit EthDepositedIntoVault(msg.sender, poolId, msg.value);
|
||||
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeAdd(amount);
|
||||
emit EthDepositedIntoVault(msg.sender, poolId, amount);
|
||||
}
|
||||
|
||||
/// @dev Withdraw some amount in ETH of a pool member.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @dev Withdraw some amount in ETH from a pool.
|
||||
/// Note that this is only callable by the staking contract.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param member of pool to transfer funds to.
|
||||
/// @param amount Amount in ETH to transfer.
|
||||
/// @param ethVaultAddress address of Eth Vault to send rewards to.
|
||||
function transferToEthVault(
|
||||
/// @param to Address to send funds to.
|
||||
/// @param amount Amount of ETH to transfer.
|
||||
function transfer(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
uint256 amount,
|
||||
address ethVaultAddress
|
||||
address payable to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
onlyStakingProxy
|
||||
{
|
||||
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeSub(amount);
|
||||
IEthVault(ethVaultAddress).depositFor.value(amount)(member);
|
||||
emit PoolRewardTransferredToEthVault(
|
||||
to.transfer(amount);
|
||||
emit PoolRewardTransferred(
|
||||
poolId,
|
||||
member,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
@@ -34,7 +34,9 @@ import "./MixinVaultCore.sol";
|
||||
/// failure mode, it cannot be returned to normal mode; this prevents
|
||||
/// corruption of related state in the staking contract.
|
||||
contract ZrxVault is
|
||||
IVaultCore,
|
||||
IZrxVault,
|
||||
Ownable,
|
||||
MixinVaultCore
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
@@ -20,35 +20,52 @@ pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/interfaces/IStructs.sol";
|
||||
import "../src/interfaces/IStakingPoolRewardVault.sol";
|
||||
import "../src/interfaces/IEthVault.sol";
|
||||
import "./TestStaking.sol";
|
||||
|
||||
|
||||
contract TestDelegatorRewards is
|
||||
TestStaking
|
||||
{
|
||||
event Deposit(
|
||||
event RecordDepositToEthVault(
|
||||
address owner,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event RecordDepositToRewardVault(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
uint256 balance
|
||||
uint256 membersReward
|
||||
);
|
||||
|
||||
event FinalizePool(
|
||||
bytes32 poolId,
|
||||
uint256 reward,
|
||||
uint256 stake
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
);
|
||||
|
||||
struct UnfinalizedMembersReward {
|
||||
uint256 reward;
|
||||
uint256 stake;
|
||||
uint256 operatorReward;
|
||||
uint256 membersReward;
|
||||
uint256 membersStake;
|
||||
}
|
||||
|
||||
constructor() public {
|
||||
init();
|
||||
init(
|
||||
address(1),
|
||||
address(1),
|
||||
address(1),
|
||||
address(1)
|
||||
);
|
||||
// Set this contract up as the eth and reward vault to intercept
|
||||
// deposits.
|
||||
ethVault = IEthVault(address(this));
|
||||
rewardVault = IStakingPoolRewardVault(address(this));
|
||||
}
|
||||
|
||||
mapping (uint256 => mapping (bytes32 => UnfinalizedMembersReward)) private
|
||||
unfinalizedMembersRewardByPoolByEpoch;
|
||||
unfinalizedPoolRewardsByEpoch;
|
||||
|
||||
/// @dev Expose _finalizePool
|
||||
function internalFinalizePool(bytes32 poolId) external {
|
||||
@@ -58,15 +75,17 @@ contract TestDelegatorRewards is
|
||||
/// @dev Set unfinalized members reward for a pool in the current epoch.
|
||||
function setUnfinalizedMembersRewards(
|
||||
bytes32 poolId,
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
external
|
||||
{
|
||||
unfinalizedMembersRewardByPoolByEpoch[currentEpoch][poolId] =
|
||||
unfinalizedPoolRewardsByEpoch[currentEpoch][poolId] =
|
||||
UnfinalizedMembersReward({
|
||||
reward: membersReward,
|
||||
stake: membersStake
|
||||
operatorReward: operatorReward,
|
||||
membersReward: membersReward,
|
||||
membersStake: membersStake
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,13 +104,20 @@ contract TestDelegatorRewards is
|
||||
)
|
||||
external
|
||||
{
|
||||
_transferDelegatorsAccumulatedRewardsToEthVault(poolId, delegator);
|
||||
_syncCumulativeRewardsNeededByDelegator(poolId, currentEpoch);
|
||||
IStructs.StoredBalance memory initialStake =
|
||||
_delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
IStructs.StoredBalance storage _stake =
|
||||
delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
_delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
_stake.isInitialized = true;
|
||||
_stake.currentEpochBalance += uint96(stake);
|
||||
_stake.nextEpochBalance += uint96(stake);
|
||||
_stake.currentEpoch = uint64(currentEpoch);
|
||||
_stake.currentEpoch = uint32(currentEpoch);
|
||||
_syncRewardsForDelegator(
|
||||
poolId,
|
||||
delegator,
|
||||
initialStake,
|
||||
_stake
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Create and delegate stake that will occur in the next epoch
|
||||
@@ -104,15 +130,22 @@ contract TestDelegatorRewards is
|
||||
)
|
||||
external
|
||||
{
|
||||
_transferDelegatorsAccumulatedRewardsToEthVault(poolId, delegator);
|
||||
_syncCumulativeRewardsNeededByDelegator(poolId, currentEpoch);
|
||||
IStructs.StoredBalance memory initialStake =
|
||||
_delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
IStructs.StoredBalance storage _stake =
|
||||
delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
_delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
if (_stake.currentEpoch < currentEpoch) {
|
||||
_stake.currentEpochBalance = _stake.nextEpochBalance;
|
||||
}
|
||||
_stake.isInitialized = true;
|
||||
_stake.nextEpochBalance += uint96(stake);
|
||||
_stake.currentEpoch = uint64(currentEpoch);
|
||||
_stake.currentEpoch = uint32(currentEpoch);
|
||||
_syncRewardsForDelegator(
|
||||
poolId,
|
||||
delegator,
|
||||
initialStake,
|
||||
_stake
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Clear stake that will occur in the next epoch
|
||||
@@ -125,67 +158,115 @@ contract TestDelegatorRewards is
|
||||
)
|
||||
external
|
||||
{
|
||||
_transferDelegatorsAccumulatedRewardsToEthVault(poolId, delegator);
|
||||
_syncCumulativeRewardsNeededByDelegator(poolId, currentEpoch);
|
||||
IStructs.StoredBalance memory initialStake =
|
||||
_delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
IStructs.StoredBalance storage _stake =
|
||||
delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
_delegatedStakeToPoolByOwner[delegator][poolId];
|
||||
if (_stake.currentEpoch < currentEpoch) {
|
||||
_stake.currentEpochBalance = _stake.nextEpochBalance;
|
||||
}
|
||||
_stake.isInitialized = true;
|
||||
_stake.nextEpochBalance -= uint96(stake);
|
||||
_stake.currentEpoch = uint64(currentEpoch);
|
||||
}
|
||||
|
||||
/// @dev Expose `_recordDepositInRewardVaultFor`.
|
||||
function recordRewardForDelegators(
|
||||
bytes32 poolId,
|
||||
uint256 reward,
|
||||
uint256 amountOfDelegatedStake
|
||||
)
|
||||
external
|
||||
{
|
||||
_recordRewardForDelegators(poolId, reward, amountOfDelegatedStake);
|
||||
}
|
||||
|
||||
/// @dev Overridden to just emit events.
|
||||
function _transferMemberBalanceToEthVault(
|
||||
bytes32 poolId,
|
||||
address member,
|
||||
uint256 balance
|
||||
)
|
||||
internal
|
||||
{
|
||||
emit Deposit(
|
||||
_stake.currentEpoch = uint32(currentEpoch);
|
||||
_syncRewardsForDelegator(
|
||||
poolId,
|
||||
member,
|
||||
balance
|
||||
delegator,
|
||||
initialStake,
|
||||
_stake
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Overridden to realize unfinalizedMembersRewardByPoolByEpoch in
|
||||
/// the current epoch and eit a event,
|
||||
function _finalizePool(bytes32 poolId)
|
||||
internal
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
/// @dev `IEthVault.recordDepositFor()`,` overridden to just emit events.
|
||||
function recordDepositFor(
|
||||
address owner,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
UnfinalizedMembersReward memory reward =
|
||||
unfinalizedMembersRewardByPoolByEpoch[currentEpoch][poolId];
|
||||
delete unfinalizedMembersRewardByPoolByEpoch[currentEpoch][poolId];
|
||||
rewards.membersReward = reward.reward;
|
||||
rewards.membersStake = reward.stake;
|
||||
_recordRewardForDelegators(poolId, reward.reward, reward.stake);
|
||||
emit FinalizePool(poolId, reward.reward, reward.stake);
|
||||
emit RecordDepositToEthVault(
|
||||
owner,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Overridden to use unfinalizedMembersRewardByPoolByEpoch.
|
||||
/// @dev `IStakingPoolRewardVault.recordDepositFor()`,`
|
||||
/// overridden to just emit events.
|
||||
function recordDepositFor(
|
||||
bytes32 poolId,
|
||||
uint256 membersReward
|
||||
)
|
||||
external
|
||||
{
|
||||
emit RecordDepositToRewardVault(
|
||||
poolId,
|
||||
membersReward
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Expose `_recordStakingPoolRewards`.
|
||||
function recordStakingPoolRewards(
|
||||
bytes32 poolId,
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 rewards,
|
||||
uint256 amountOfDelegatedStake
|
||||
)
|
||||
public
|
||||
{
|
||||
_setOperatorShare(poolId, operatorReward, membersReward);
|
||||
_recordStakingPoolRewards(poolId, rewards, amountOfDelegatedStake);
|
||||
}
|
||||
|
||||
/// @dev Overridden to realize `unfinalizedPoolRewardsByEpoch` in
|
||||
/// the current epoch and emit a event,
|
||||
function _finalizePool(bytes32 poolId)
|
||||
internal
|
||||
returns (
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
{
|
||||
UnfinalizedMembersReward memory reward =
|
||||
unfinalizedPoolRewardsByEpoch[currentEpoch][poolId];
|
||||
delete unfinalizedPoolRewardsByEpoch[currentEpoch][poolId];
|
||||
|
||||
_setOperatorShare(poolId, operatorReward, membersReward);
|
||||
|
||||
uint256 totalRewards = reward.operatorReward + reward.membersReward;
|
||||
membersStake = reward.membersStake;
|
||||
(operatorReward, membersReward) =
|
||||
_recordStakingPoolRewards(poolId, totalRewards, membersStake);
|
||||
emit FinalizePool(poolId, operatorReward, membersReward, membersStake);
|
||||
}
|
||||
|
||||
/// @dev Overridden to use unfinalizedPoolRewardsByEpoch.
|
||||
function _getUnfinalizedPoolRewards(bytes32 poolId)
|
||||
internal
|
||||
view
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (
|
||||
uint256 totalReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
{
|
||||
UnfinalizedMembersReward storage reward =
|
||||
unfinalizedMembersRewardByPoolByEpoch[currentEpoch][poolId];
|
||||
rewards.membersReward = reward.reward;
|
||||
rewards.membersStake = reward.stake;
|
||||
unfinalizedPoolRewardsByEpoch[currentEpoch][poolId];
|
||||
totalReward = reward.operatorReward + reward.membersReward;
|
||||
membersStake = reward.membersStake;
|
||||
}
|
||||
|
||||
/// @dev Set the operator share of a pool based on reward ratios.
|
||||
function _setOperatorShare(
|
||||
bytes32 poolId,
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward
|
||||
)
|
||||
private
|
||||
{
|
||||
uint32 operatorShare = uint32(
|
||||
operatorReward * PPM_DENOMINATOR / (operatorReward + membersReward)
|
||||
);
|
||||
poolById[poolId].operatorShare = operatorShare;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -27,30 +27,37 @@ import "./TestStaking.sol";
|
||||
contract TestFinalizer is
|
||||
TestStaking
|
||||
{
|
||||
event RecordRewardForDelegatorsCall(
|
||||
event RecordStakingPoolRewards(
|
||||
bytes32 poolId,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
);
|
||||
|
||||
event RecordDepositInRewardVaultForCall(
|
||||
bytes32 poolId,
|
||||
uint256 totalReward,
|
||||
bool operatorOnly
|
||||
event DepositStakingPoolRewards(
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward
|
||||
);
|
||||
|
||||
event DepositIntoStakingPoolRewardVaultCall(
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
address payable private _rewardReceiver;
|
||||
address payable private _operatorRewardsReceiver;
|
||||
address payable private _membersRewardsReceiver;
|
||||
mapping (bytes32 => uint32) private _operatorSharesByPool;
|
||||
|
||||
/// @param rewardReceiver The address to transfer rewards into when
|
||||
/// @param operatorRewardsReceiver The address to transfer rewards into when
|
||||
/// a pool is finalized.
|
||||
constructor(address payable rewardReceiver) public {
|
||||
_rewardReceiver = rewardReceiver;
|
||||
init();
|
||||
constructor(
|
||||
address payable operatorRewardsReceiver,
|
||||
address payable membersRewardsReceiver
|
||||
)
|
||||
public
|
||||
{
|
||||
init(
|
||||
address(1),
|
||||
address(1),
|
||||
address(1),
|
||||
address(1)
|
||||
);
|
||||
_operatorRewardsReceiver = operatorRewardsReceiver;
|
||||
_membersRewardsReceiver = membersRewardsReceiver;
|
||||
}
|
||||
|
||||
/// @dev Activate a pool in the current epoch.
|
||||
@@ -82,9 +89,15 @@ contract TestFinalizer is
|
||||
/// @dev Expose `_finalizePool()`
|
||||
function internalFinalizePool(bytes32 poolId)
|
||||
external
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
{
|
||||
rewards = _finalizePool(poolId);
|
||||
(operatorReward,
|
||||
membersReward,
|
||||
membersStake) = _finalizePool(poolId);
|
||||
}
|
||||
|
||||
/// @dev Get finalization-related state variables.
|
||||
@@ -143,9 +156,12 @@ contract TestFinalizer is
|
||||
function internalGetUnfinalizedPoolRewards(bytes32 poolId)
|
||||
external
|
||||
view
|
||||
returns (IStructs.PoolRewards memory rewards)
|
||||
returns (
|
||||
uint256 totalReward,
|
||||
uint256 membersStake
|
||||
)
|
||||
{
|
||||
rewards = _getUnfinalizedPoolRewards(poolId);
|
||||
(totalReward, membersStake) = _getUnfinalizedPoolRewards(poolId);
|
||||
}
|
||||
|
||||
/// @dev Expose `_getActivePoolFromEpoch`.
|
||||
@@ -157,68 +173,33 @@ contract TestFinalizer is
|
||||
pool = _getActivePoolFromEpoch(epoch, poolId);
|
||||
}
|
||||
|
||||
/// @dev Overridden to just store inputs.
|
||||
function _recordRewardForDelegators(
|
||||
/// @dev Overridden to log and do some basic math.
|
||||
function _recordStakingPoolRewards(
|
||||
bytes32 poolId,
|
||||
uint256 membersReward,
|
||||
uint256 reward,
|
||||
uint256 membersStake
|
||||
)
|
||||
internal
|
||||
returns (uint256 operatorReward, uint256 membersReward)
|
||||
{
|
||||
emit RecordRewardForDelegatorsCall(
|
||||
(operatorReward, membersReward) = _splitReward(poolId, reward);
|
||||
emit RecordStakingPoolRewards(
|
||||
poolId,
|
||||
membersReward,
|
||||
reward,
|
||||
membersStake
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Overridden to store inputs and do some really basic math.
|
||||
function _depositIntoStakingPoolRewardVault(uint256 amount) internal {
|
||||
emit DepositIntoStakingPoolRewardVaultCall(amount);
|
||||
_rewardReceiver.transfer(amount);
|
||||
}
|
||||
|
||||
/// @dev Overridden to store inputs and do some really basic math.
|
||||
function _recordDepositInRewardVaultFor(
|
||||
bytes32 poolId,
|
||||
uint256 totalReward,
|
||||
bool operatorOnly
|
||||
/// @dev Overridden to log and transfer to receivers.
|
||||
function _depositStakingPoolRewards(
|
||||
uint256 operatorReward,
|
||||
uint256 membersReward
|
||||
)
|
||||
internal
|
||||
returns (
|
||||
uint256 operatorPortion,
|
||||
uint256 membersPortion
|
||||
)
|
||||
{
|
||||
emit RecordDepositInRewardVaultForCall(
|
||||
poolId,
|
||||
totalReward,
|
||||
operatorOnly
|
||||
);
|
||||
|
||||
if (operatorOnly) {
|
||||
operatorPortion = totalReward;
|
||||
} else {
|
||||
(operatorPortion, membersPortion) =
|
||||
_splitRewardAmountBetweenOperatorAndMembers(
|
||||
poolId,
|
||||
totalReward
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Overridden to do some really basic math.
|
||||
function _splitRewardAmountBetweenOperatorAndMembers(
|
||||
bytes32 poolId,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256 operatorPortion, uint256 membersPortion)
|
||||
{
|
||||
uint32 operatorShare = _operatorSharesByPool[poolId];
|
||||
operatorPortion = operatorShare * amount / PPM_DENOMINATOR;
|
||||
membersPortion = amount - operatorPortion;
|
||||
emit DepositStakingPoolRewards(operatorReward, membersReward);
|
||||
address(_operatorRewardsReceiver).transfer(operatorReward);
|
||||
address(_membersRewardsReceiver).transfer(operatorReward);
|
||||
}
|
||||
|
||||
/// @dev Overriden to just increase the epoch counter.
|
||||
@@ -229,4 +210,26 @@ contract TestFinalizer is
|
||||
// solhint-disable no-empty-blocks
|
||||
/// @dev Overridden to do nothing.
|
||||
function _unwrapWETH() internal {}
|
||||
|
||||
/// @dev Split a pool's total reward between the operator and members.
|
||||
function _splitReward(
|
||||
bytes32 poolId,
|
||||
uint256 amount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 operatorReward, uint256 membersReward)
|
||||
{
|
||||
IStructs.ActivePool memory pool = _getActivePoolFromEpoch(
|
||||
currentEpoch - 1,
|
||||
poolId
|
||||
);
|
||||
uint32 operatorShare = _operatorSharesByPool[poolId];
|
||||
(operatorReward, membersReward) = _splitStakingPoolRewards(
|
||||
operatorShare,
|
||||
amount,
|
||||
pool.membersStake
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -110,14 +110,4 @@ contract TestProtocolFees is
|
||||
nextEpochBalance: pool.operatorStake
|
||||
});
|
||||
}
|
||||
|
||||
/// @dev Overridden to use test pools.
|
||||
function getPoolOperator(bytes32)
|
||||
public
|
||||
view
|
||||
returns (address operatorAddress)
|
||||
{
|
||||
// Just return nil, we won't use it.
|
||||
return address(0);
|
||||
}
|
||||
}
|
||||
|
@@ -129,7 +129,7 @@ contract TestStorageLayout is
|
||||
if sub(totalWeightedStakeThisEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(activePoolsByEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
if sub(_activePoolsByEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
slot := add(slot, 1)
|
||||
|
||||
if sub(numActivePoolsThisEpoch_slot, slot) { revertIncorrectStorageSlot() }
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from "ethereum-types";
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as EthVault from '../generated-artifacts/EthVault.json';
|
||||
import * as IEthVault from '../generated-artifacts/IEthVault.json';
|
||||
@@ -47,6 +47,8 @@ import * as StakingProxy from '../generated-artifacts/StakingProxy.json';
|
||||
import * as TestAssertStorageParams from '../generated-artifacts/TestAssertStorageParams.json';
|
||||
import * as TestCobbDouglas from '../generated-artifacts/TestCobbDouglas.json';
|
||||
import * as TestCumulativeRewardTracking from '../generated-artifacts/TestCumulativeRewardTracking.json';
|
||||
import * as TestDelegatorRewards from '../generated-artifacts/TestDelegatorRewards.json';
|
||||
import * as TestFinalizer from '../generated-artifacts/TestFinalizer.json';
|
||||
import * as TestInitTarget from '../generated-artifacts/TestInitTarget.json';
|
||||
import * as TestLibFixedMath from '../generated-artifacts/TestLibFixedMath.json';
|
||||
import * as TestLibProxy from '../generated-artifacts/TestLibProxy.json';
|
||||
@@ -103,6 +105,8 @@ export const artifacts = {
|
||||
TestAssertStorageParams: TestAssertStorageParams as ContractArtifact,
|
||||
TestCobbDouglas: TestCobbDouglas as ContractArtifact,
|
||||
TestCumulativeRewardTracking: TestCumulativeRewardTracking as ContractArtifact,
|
||||
TestDelegatorRewards: TestDelegatorRewards as ContractArtifact,
|
||||
TestFinalizer: TestFinalizer as ContractArtifact,
|
||||
TestInitTarget: TestInitTarget as ContractArtifact,
|
||||
TestLibFixedMath: TestLibFixedMath as ContractArtifact,
|
||||
TestLibProxy: TestLibProxy as ContractArtifact,
|
||||
|
@@ -45,6 +45,8 @@ export * from '../generated-wrappers/staking_proxy';
|
||||
export * from '../generated-wrappers/test_assert_storage_params';
|
||||
export * from '../generated-wrappers/test_cobb_douglas';
|
||||
export * from '../generated-wrappers/test_cumulative_reward_tracking';
|
||||
export * from '../generated-wrappers/test_delegator_rewards';
|
||||
export * from '../generated-wrappers/test_finalizer';
|
||||
export * from '../generated-wrappers/test_init_target';
|
||||
export * from '../generated-wrappers/test_lib_fixed_math';
|
||||
export * from '../generated-wrappers/test_lib_proxy';
|
||||
|
@@ -45,6 +45,8 @@
|
||||
"generated-artifacts/TestAssertStorageParams.json",
|
||||
"generated-artifacts/TestCobbDouglas.json",
|
||||
"generated-artifacts/TestCumulativeRewardTracking.json",
|
||||
"generated-artifacts/TestDelegatorRewards.json",
|
||||
"generated-artifacts/TestFinalizer.json",
|
||||
"generated-artifacts/TestInitTarget.json",
|
||||
"generated-artifacts/TestLibFixedMath.json",
|
||||
"generated-artifacts/TestLibProxy.json",
|
||||
|
@@ -9,8 +9,6 @@ library LibFractions {
|
||||
|
||||
/// @dev Maximum value for addition result components.
|
||||
uint256 constant internal RESCALE_THRESHOLD = 10 ** 27;
|
||||
/// @dev Rescale factor for addition.
|
||||
uint256 constant internal RESCALE_BASE = 10 ** 9;
|
||||
|
||||
/// @dev Safely adds two fractions `n1/d1 + n2/d2`
|
||||
/// @param n1 numerator of `1`
|
||||
@@ -46,8 +44,10 @@ library LibFractions {
|
||||
// If either the numerator or the denominator are > RESCALE_THRESHOLD,
|
||||
// re-scale them to prevent overflows in future operations.
|
||||
if (numerator > RESCALE_THRESHOLD || denominator > RESCALE_THRESHOLD) {
|
||||
numerator = numerator.safeDiv(RESCALE_BASE);
|
||||
denominator = denominator.safeDiv(RESCALE_BASE);
|
||||
uint256 rescaleBase = numerator >= denominator ? numerator : denominator;
|
||||
rescaleBase /= RESCALE_THRESHOLD;
|
||||
numerator = numerator.safeDiv(rescaleBase);
|
||||
denominator = denominator.safeDiv(rescaleBase);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,6 +29,10 @@ export enum InvalidParamValueErrorCode {
|
||||
InvalidZrxVaultAddress,
|
||||
InvalidEpochDuration,
|
||||
InvalidMinimumPoolStake,
|
||||
InvalidWethProxyAddress,
|
||||
InvalidEthVaultAddress,
|
||||
InvalidRewardVaultAddress,
|
||||
InvalidZrxVaultAddress,
|
||||
}
|
||||
|
||||
export enum InitializationErrorCode {
|
||||
|
Reference in New Issue
Block a user