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