Rich Reverts in new staking mechanics

This commit is contained in:
Greg Hysen
2019-09-03 22:47:04 -07:00
parent c0cb78bb3f
commit 7e5e2241cb
8 changed files with 130 additions and 29 deletions

View File

@@ -84,9 +84,10 @@ contract MixinStorage is
// pools that were active in the current epoch
bytes32[] internal activePoolsThisEpoch;
// reward ratios by epoch
// mapping from Pool Id to Epoch to Reward Ratio
mapping (bytes32 => mapping (uint256 => IStructs.Fraction)) internal cumulativeRewardsByPool;
// mapping from Pool Id to Epoch
mapping (bytes32 => uint256) internal cumulativeRewardsByPoolLastStored;
// registered 0x Exchange contracts

View File

@@ -122,6 +122,18 @@ library LibStakingRichErrors {
bytes4 internal constant INVALID_COBB_DOUGLAS_ALPHA_ERROR_SELECTOR =
0x8f8e73de;
// bytes4(keccak256("EthVaultNotSet()"))
bytes4 internal constant ETH_VAULT_NOT_SET =
0xdb3f0be8;
// bytes4(keccak256("RewardVaultNotSet()"))
bytes4 internal constant REWARD_VAULT_NOT_SET =
0xfcb260f7;
// bytes4(keccak256("InvalidStakeState(uint256)"))
bytes4 internal constant INVALID_STAKE_STATE =
0xe6586728;
// solhint-disable func-name-mixedcase
function MiscalculatedRewardsError(
uint256 totalRewardsPaid,
@@ -462,4 +474,35 @@ library LibStakingRichErrors {
denominator
);
}
function EthVaultNotSet()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ETH_VAULT_NOT_SET
);
}
function RewardVaultNotSet()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
REWARD_VAULT_NOT_SET
);
}
function InvalidStakeState(uint256 state)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_STAKE_STATE,
state
);
}
}

View File

@@ -27,6 +27,7 @@ import "./MixinZrxVault.sol";
import "../staking_pools/MixinStakingPoolRewardVault.sol";
import "../staking_pools/MixinStakingPoolRewards.sol";
import "../sys/MixinScheduler.sol";
import "../libs/LibStakingRichErrors.sol";
import "./MixinStakeBalances.sol";
import "./MixinStakeStorage.sol";
@@ -78,10 +79,14 @@ contract MixinStake is
// sanity check
uint256 currentWithdrawableStake = getWithdrawableStake(owner);
require(
amount <= currentWithdrawableStake,
"INSUFFICIENT_FUNDS"
);
if (amount > currentWithdrawableStake) {
LibRichErrors.rrevert(
LibStakingRichErrors.InsufficientBalanceError(
amount,
currentWithdrawableStake
)
);
}
// burn inactive stake
_burnBalance(inactiveStakeByOwner[owner], amount);
@@ -219,6 +224,7 @@ contract MixinStake is
/// @return a storage pointer to the corresponding stake stake
function _getBalancePtrFromState(IStructs.StakeState state)
private
view
returns (IStructs.DelayedBalance storage)
{
// lookup state
@@ -231,7 +237,12 @@ contract MixinStake is
return delegatedStakeByOwner[owner];
}
// not found
revert("Unrecognized State");
// invalid state
LibRichErrors.rrevert(
LibStakingRichErrors.InvalidStakeState(uint256(state))
);
// required to compile ~ we should never hit this.
revert("INVALID_STATE");
}
}

View File

@@ -63,7 +63,12 @@ contract MixinStakeStorage is
// sanity check on balance
if (amount > from.next) {
revert("Insufficient Balance");
LibRichErrors.rrevert(
LibStakingRichErrors.InsufficientBalanceError(
amount,
from.next
)
);
}
// move stake for next epoch
@@ -183,6 +188,7 @@ contract MixinStakeStorage is
IStructs.DelayedBalance storage balancePtrB
)
private
pure
returns (bool areEqual)
{
assembly {

View File

@@ -21,6 +21,7 @@ pragma solidity ^0.5.9;
import "../interfaces/IStakingEvents.sol";
import "../interfaces/IStakingPoolRewardVault.sol";
import "../immutable/MixinStorage.sol";
import "../libs/LibStakingRichErrors.sol";
/// @dev This mixin contains logic for interfacing with the Staking Pool Reward Vault (vaults/StakingPoolRewardVault.sol)
@@ -71,7 +72,15 @@ contract MixinStakingPoolRewardVault is
function _depositIntoStakingPoolRewardVault(uint256 amount)
internal
{
// cast to payable and sanity check
address payable rewardVaultAddress = address(uint160(address(rewardVault)));
if (rewardVaultAddress == NIL_ADDRESS) {
LibRichErrors.rrevert(
LibStakingRichErrors.RewardVaultNotSet()
);
}
// perform transfer
rewardVaultAddress.transfer(amount);
}
@@ -86,11 +95,15 @@ contract MixinStakingPoolRewardVault is
)
internal
{
// sanity check
IStakingPoolRewardVault _rewardVault = rewardVault;
require(
address(_rewardVault) != NIL_ADDRESS,
"REWARD_VAULT_NOT_SET"
);
if (address(_rewardVault) == NIL_ADDRESS) {
LibRichErrors.rrevert(
LibStakingRichErrors.RewardVaultNotSet()
);
}
// perform transfer
_rewardVault.transferMemberBalanceToEthVault(poolId, member, amount);
}
}

View File

@@ -232,6 +232,7 @@ contract MixinStakingPoolRewards is
/// @dev returns true iff Cumulative Rewards are set
function _isCumulativeRewardSet(IStructs.Fraction memory cumulativeReward)
private
pure
returns (bool)
{
// we use the denominator as a proxy for whether the cumulative

View File

@@ -120,13 +120,6 @@ contract StakingPoolRewardVault is
return;
}
// sanity check on eth vault
IEthVault _ethVault = ethVault;
require(
address(_ethVault) != address(0),
"ETH_VAULT_NOT_SET"
);
// sanity check - sufficient balance?
uint256 operatorBalance = uint256(balanceByPoolId[poolId].operatorBalance);
if (amount > operatorBalance) {
@@ -138,7 +131,7 @@ contract StakingPoolRewardVault is
// update balance and transfer `amount` in ETH to staking contract
balanceByPoolId[poolId].operatorBalance = operatorBalance.safeSub(amount).downcastToUint96();
_ethVault.depositFor.value(amount)(operator);
_transferToEthVault(operator, amount);
// notify
emit RewardWithdrawnForOperator(poolId, amount);
@@ -157,12 +150,9 @@ contract StakingPoolRewardVault is
external
onlyStakingContract
{
// sanity check on eth vault
IEthVault _ethVault = ethVault;
require(
address(_ethVault) != address(0),
"ETH_VAULT_NOT_SET"
);
if (amount == 0) {
return;
}
// sanity check - sufficient balance?
uint256 membersBalance = uint256(balanceByPoolId[poolId].membersBalance);
@@ -175,7 +165,7 @@ contract StakingPoolRewardVault is
// update balance and transfer `amount` in ETH to staking contract
balanceByPoolId[poolId].membersBalance = membersBalance.safeSub(amount).downcastToUint96();
_ethVault.depositFor.value(amount)(member);
_transferToEthVault(member, amount);
// notify
emit RewardWithdrawnForMember(poolId, amount);
@@ -277,12 +267,27 @@ contract StakingPoolRewardVault is
uint256 newMembersBalance = uint256(balance.membersBalance).safeAdd(poolPortion);
// save new balances
balance.operatorBalance = LibSafeDowncast.downcastToUint96(newOperatorBalance);
balance.membersBalance = LibSafeDowncast.downcastToUint96(newMembersBalance);
balance.operatorBalance = newOperatorBalance.downcastToUint96();
balance.membersBalance = newMembersBalance.downcastToUint96();
return (
operatorPortion,
poolPortion
);
}
function _transferToEthVault(address from, uint256 amount)
private
{
// sanity check on eth vault
IEthVault _ethVault = ethVault;
if (address(_ethVault) == address(0)) {
LibRichErrors.rrevert(
LibStakingRichErrors.EthVaultNotSet()
);
}
// perform xfer
_ethVault.depositFor.value(amount)(from);
}
}

View File

@@ -204,6 +204,24 @@ export class InvalidCobbDouglasAlphaError extends RevertError {
}
}
export class EthVaultNotSet extends RevertError {
constructor() {
super('EthVaultNotSet', 'EthVaultNotSet()');
}
}
export class RewardVaultNotSet extends RevertError {
constructor() {
super('RewardVaultNotSet', 'RewardVaultNotSet()');
}
}
export class InvalidStakeState extends RevertError {
constructor(state?: BigNumber) {
super('InvalidStakeState', 'InvalidStakeState(uint256 state)', { state });
}
}
const types = [
MiscalculatedRewardsError,
OnlyCallableByExchangeError,
@@ -230,6 +248,9 @@ const types = [
InvalidPoolOperatorShareError,
PoolAlreadyExistsError,
InvalidCobbDouglasAlphaError,
EthVaultNotSet,
RewardVaultNotSet,
InvalidStakeState
];
// Register the types we've defined.