Merge pull request #2190 from 0xProject/feat/3.0/consolidate-pool-mixins
Consolidate staking pool mixins
This commit is contained in:
commit
0c5f0271c7
@ -1,4 +1,4 @@
|
||||
import { chaiSetup, getCodesizeFromArtifact } from '@0x/contracts-test-utils';
|
||||
import { chaiSetup, constants, getCodesizeFromArtifact } from '@0x/contracts-test-utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
chaiSetup.configure();
|
||||
@ -7,12 +7,10 @@ const expect = chai.expect;
|
||||
import { artifacts } from '../src';
|
||||
|
||||
describe('Contract Size Checks', () => {
|
||||
const MAX_CODE_SIZE = 24576;
|
||||
|
||||
describe('Exchange', () => {
|
||||
it('should have a codesize less than the maximum', async () => {
|
||||
const actualSize = getCodesizeFromArtifact(artifacts.Exchange);
|
||||
expect(actualSize).to.be.lt(MAX_CODE_SIZE);
|
||||
expect(actualSize).to.be.lt(constants.MAX_CODE_SIZE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -34,12 +34,10 @@ contract Staking is
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinExchangeManager,
|
||||
MixinScheduler,
|
||||
MixinParams,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards,
|
||||
@ -214,8 +212,8 @@ contract Staking is
|
||||
slot := add(slot, 0x1)
|
||||
|
||||
assertSlotAndOffset(
|
||||
poolJoinedByMakerAddress_slot,
|
||||
poolJoinedByMakerAddress_offset,
|
||||
_poolJoinedByMakerAddress_slot,
|
||||
_poolJoinedByMakerAddress_offset,
|
||||
slot,
|
||||
offset
|
||||
)
|
||||
@ -378,8 +376,8 @@ contract Staking is
|
||||
slot := add(slot, 0x1)
|
||||
|
||||
assertSlotAndOffset(
|
||||
_wethReservedForPoolRewards_slot,
|
||||
_wethReservedForPoolRewards_offset,
|
||||
wethReservedForPoolRewards_slot,
|
||||
wethReservedForPoolRewards_offset,
|
||||
slot,
|
||||
offset
|
||||
)
|
||||
|
@ -38,11 +38,9 @@ contract MixinExchangeFees is
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinExchangeManager,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards,
|
||||
@ -126,20 +124,6 @@ contract MixinExchangeFees is
|
||||
activePoolsThisEpoch[poolId] = pool;
|
||||
}
|
||||
|
||||
/// @dev Returns the total balance of this contract, including WETH,
|
||||
/// minus any WETH that has been reserved for rewards.
|
||||
/// @return totalBalance Total balance.
|
||||
function getAvailableRewardsBalance()
|
||||
external
|
||||
view
|
||||
returns (uint256 totalBalance)
|
||||
{
|
||||
totalBalance = address(this).balance.safeAdd(
|
||||
_getAvailableWethBalance()
|
||||
);
|
||||
return totalBalance;
|
||||
}
|
||||
|
||||
/// @dev Get information on an active staking pool in this epoch.
|
||||
/// @param poolId Pool Id to query.
|
||||
/// @return pool ActivePool struct.
|
||||
@ -170,6 +154,7 @@ contract MixinExchangeFees is
|
||||
_poolById[poolId].operator,
|
||||
poolId
|
||||
).currentEpochBalance;
|
||||
|
||||
membersStake = totalStake.safeSub(operatorStake);
|
||||
weightedStake = operatorStake.safeAdd(
|
||||
LibMath.getPartialAmountFloor(
|
||||
|
@ -77,7 +77,8 @@ contract MixinStorage is
|
||||
|
||||
// mapping from Maker Address to a struct representing the pool the maker has joined and
|
||||
// whether the operator of that pool has subsequently added the maker.
|
||||
mapping (address => IStructs.MakerPoolJoinStatus) public poolJoinedByMakerAddress;
|
||||
// (access externally using `getStakingPoolIdOfMaker`)
|
||||
mapping (address => IStructs.MakerPoolJoinStatus) internal _poolJoinedByMakerAddress;
|
||||
|
||||
// mapping from Pool Id to Pool
|
||||
mapping (bytes32 => IStructs.Pool) internal _poolById;
|
||||
@ -144,7 +145,7 @@ contract MixinStorage is
|
||||
IStructs.UnfinalizedState public unfinalizedState;
|
||||
|
||||
/// @dev The WETH balance of this contract that is reserved for pool reward payouts.
|
||||
uint256 _wethReservedForPoolRewards;
|
||||
uint256 public wethReservedForPoolRewards;
|
||||
|
||||
/// @dev Adds owner as an authorized address.
|
||||
constructor()
|
||||
|
@ -51,11 +51,6 @@ interface IStorage {
|
||||
view
|
||||
returns (bytes32);
|
||||
|
||||
function poolJoinedByMakerAddress(address makerAddress)
|
||||
external
|
||||
view
|
||||
returns (IStructs.MakerPoolJoinStatus memory);
|
||||
|
||||
function numMakersByPoolId(bytes32 poolId)
|
||||
external
|
||||
view
|
||||
|
@ -75,13 +75,9 @@ library LibStakingRichErrors {
|
||||
bytes4 internal constant INSUFFICIENT_BALANCE_ERROR_SELECTOR =
|
||||
0x84c8b7c9;
|
||||
|
||||
// bytes4(keccak256("OnlyCallableByPoolOperatorError(address,address)"))
|
||||
bytes4 internal constant ONLY_CALLABLE_BY_POOL_OPERATOR_ERROR_SELECTOR =
|
||||
0x6cfa0c22;
|
||||
|
||||
// bytes4(keccak256("OnlyCallableByPoolOperatorOrMakerError(address,address,address)"))
|
||||
// bytes4(keccak256("OnlyCallableByPoolOperatorOrMakerError(address,bytes32)"))
|
||||
bytes4 internal constant ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER_ERROR_SELECTOR =
|
||||
0x7d9e1c10;
|
||||
0x7677eb13;
|
||||
|
||||
// bytes4(keccak256("MakerPoolAssignmentError(uint8,address,bytes32)"))
|
||||
bytes4 internal constant MAKER_POOL_ASSIGNMENT_ERROR_SELECTOR =
|
||||
@ -217,25 +213,9 @@ library LibStakingRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function OnlyCallableByPoolOperatorError(
|
||||
address senderAddress,
|
||||
address operator
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
ONLY_CALLABLE_BY_POOL_OPERATOR_ERROR_SELECTOR,
|
||||
senderAddress,
|
||||
operator
|
||||
);
|
||||
}
|
||||
|
||||
function OnlyCallableByPoolOperatorOrMakerError(
|
||||
address senderAddress,
|
||||
address operator,
|
||||
address makerAddress
|
||||
bytes32 poolId
|
||||
)
|
||||
internal
|
||||
pure
|
||||
@ -244,8 +224,7 @@ library LibStakingRichErrors {
|
||||
return abi.encodeWithSelector(
|
||||
ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER_ERROR_SELECTOR,
|
||||
senderAddress,
|
||||
operator,
|
||||
makerAddress
|
||||
poolId
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,8 @@ contract MixinStake is
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards,
|
||||
|
@ -24,7 +24,6 @@ import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../libs/LibStakingRichErrors.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "./MixinStakingPoolRewards.sol";
|
||||
import "./MixinStakingPoolMakers.sol";
|
||||
|
||||
|
||||
contract MixinStakingPool is
|
||||
@ -33,15 +32,21 @@ contract MixinStakingPool is
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers,
|
||||
MixinScheduler,
|
||||
MixinStakeStorage,
|
||||
MixinStakingPoolMakers,
|
||||
MixinStakeBalances,
|
||||
MixinCumulativeRewards,
|
||||
MixinStakingPoolRewards
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
using LibSafeDowncast for uint256;
|
||||
|
||||
/// @dev Asserts that the sender is the operator of the input pool or the input maker.
|
||||
/// @param poolId Pool sender must be operator of.
|
||||
modifier onlyStakingPoolOperatorOrMaker(bytes32 poolId) {
|
||||
_assertSenderIsPoolOperatorOrMaker(poolId);
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Create a new staking pool. The sender will be the operator of this pool.
|
||||
/// Note that an operator must be payable.
|
||||
@ -94,6 +99,7 @@ contract MixinStakingPool is
|
||||
/// @param newOperatorShare The newly decreased percentage of any rewards owned by the operator.
|
||||
function decreaseStakingPoolOperatorShare(bytes32 poolId, uint32 newOperatorShare)
|
||||
external
|
||||
onlyStakingPoolOperatorOrMaker(poolId)
|
||||
{
|
||||
// load pool and assert that we can decrease
|
||||
uint32 currentOperatorShare = _poolById[poolId].operatorShare;
|
||||
@ -112,6 +118,94 @@ contract MixinStakingPool is
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Allows caller to join a staking pool if already assigned.
|
||||
/// @param poolId Unique id of pool.
|
||||
function joinStakingPoolAsMaker(bytes32 poolId)
|
||||
external
|
||||
{
|
||||
// Is the maker already in a pool?
|
||||
address makerAddress = msg.sender;
|
||||
IStructs.MakerPoolJoinStatus memory poolJoinStatus = _poolJoinedByMakerAddress[makerAddress];
|
||||
if (poolJoinStatus.confirmed) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressAlreadyRegistered,
|
||||
makerAddress,
|
||||
poolJoinStatus.poolId
|
||||
));
|
||||
}
|
||||
|
||||
poolJoinStatus.poolId = poolId;
|
||||
_poolJoinedByMakerAddress[makerAddress] = poolJoinStatus;
|
||||
|
||||
// Maker has joined to the pool, awaiting operator confirmation
|
||||
emit PendingAddMakerToPool(
|
||||
poolId,
|
||||
makerAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Adds a maker to a staking pool. Note that this is only callable by the pool operator.
|
||||
/// Note also that the maker must have previously called joinStakingPoolAsMaker.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function addMakerToStakingPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
external
|
||||
onlyStakingPoolOperatorOrMaker(poolId)
|
||||
{
|
||||
_addMakerToStakingPool(poolId, makerAddress);
|
||||
}
|
||||
|
||||
/// @dev Removes a maker from a staking pool. Note that this is only callable by the pool operator or maker.
|
||||
/// Note also that the maker does not have to *agree* to leave the pool; this action is
|
||||
/// at the sole discretion of the pool operator.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function removeMakerFromStakingPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
external
|
||||
onlyStakingPoolOperatorOrMaker(poolId)
|
||||
{
|
||||
bytes32 makerPoolId = getStakingPoolIdOfMaker(makerAddress);
|
||||
if (makerPoolId != poolId) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressNotRegistered,
|
||||
makerAddress,
|
||||
makerPoolId
|
||||
));
|
||||
}
|
||||
|
||||
// remove the pool and confirmation from the maker status
|
||||
delete _poolJoinedByMakerAddress[makerAddress];
|
||||
_poolById[poolId].numberOfMakers = uint256(_poolById[poolId].numberOfMakers).safeSub(1).downcastToUint32();
|
||||
|
||||
// Maker has been removed from the pool`
|
||||
emit MakerRemovedFromStakingPool(
|
||||
poolId,
|
||||
makerAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Returns the pool id of the input maker.
|
||||
/// @param makerAddress Address of maker
|
||||
/// @return Pool id, nil if maker is not yet assigned to a pool.
|
||||
function getStakingPoolIdOfMaker(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bytes32)
|
||||
{
|
||||
IStructs.MakerPoolJoinStatus memory poolJoinStatus = _poolJoinedByMakerAddress[makerAddress];
|
||||
if (poolJoinStatus.confirmed) {
|
||||
return poolJoinStatus.poolId;
|
||||
} else {
|
||||
return NIL_POOL_ID;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Returns a staking pool
|
||||
/// @param poolId Unique id of pool.
|
||||
function getStakingPool(bytes32 poolId)
|
||||
@ -122,6 +216,65 @@ contract MixinStakingPool is
|
||||
return _poolById[poolId];
|
||||
}
|
||||
|
||||
/// @dev Adds a maker to a staking pool. Note that this is only callable by the pool operator.
|
||||
/// Note also that the maker must have previously called joinStakingPoolAsMaker.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function _addMakerToStakingPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
internal
|
||||
{
|
||||
// cache pool and join status for use throughout this function
|
||||
IStructs.Pool memory pool = _poolById[poolId];
|
||||
IStructs.MakerPoolJoinStatus memory poolJoinStatus = _poolJoinedByMakerAddress[makerAddress];
|
||||
|
||||
// Is the maker already in a pool?
|
||||
if (poolJoinStatus.confirmed) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressAlreadyRegistered,
|
||||
makerAddress,
|
||||
poolJoinStatus.poolId
|
||||
));
|
||||
}
|
||||
|
||||
// Is the maker trying to join this pool; or are they the operator?
|
||||
bytes32 makerPendingPoolId = poolJoinStatus.poolId;
|
||||
if (makerPendingPoolId != poolId && makerAddress != pool.operator) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressNotPendingAdd,
|
||||
makerAddress,
|
||||
makerPendingPoolId
|
||||
));
|
||||
}
|
||||
|
||||
// Is the pool already full?
|
||||
// NOTE: If maximumMakersInPool is decreased below the number of makers currently in a pool,
|
||||
// the pool will no longer be able to add more makers.
|
||||
if (pool.numberOfMakers >= maximumMakersInPool) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.PoolIsFull,
|
||||
makerAddress,
|
||||
poolId
|
||||
));
|
||||
}
|
||||
|
||||
// Add maker to pool
|
||||
poolJoinStatus = IStructs.MakerPoolJoinStatus({
|
||||
poolId: poolId,
|
||||
confirmed: true
|
||||
});
|
||||
_poolJoinedByMakerAddress[makerAddress] = poolJoinStatus;
|
||||
_poolById[poolId].numberOfMakers = uint256(pool.numberOfMakers).safeAdd(1).downcastToUint32();
|
||||
|
||||
// Maker has been added to the pool
|
||||
emit MakerAddedToStakingPool(
|
||||
poolId,
|
||||
makerAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Computes the unique id that comes after the input pool id.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @return Next pool id after input pool.
|
||||
@ -180,4 +333,24 @@ contract MixinStakingPool is
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Asserts that the sender is the operator of the input pool or the input maker.
|
||||
/// @param poolId Pool sender must be operator of.
|
||||
function _assertSenderIsPoolOperatorOrMaker(bytes32 poolId)
|
||||
private
|
||||
view
|
||||
{
|
||||
address operator = _poolById[poolId].operator;
|
||||
if (
|
||||
msg.sender != operator &&
|
||||
getStakingPoolIdOfMaker(msg.sender) != poolId
|
||||
) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.OnlyCallableByPoolOperatorOrMakerError(
|
||||
msg.sender,
|
||||
poolId
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,206 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../libs/LibStakingRichErrors.sol";
|
||||
import "../libs/LibSafeDowncast.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
import "../immutable/MixinStorage.sol";
|
||||
import "./MixinStakingPoolModifiers.sol";
|
||||
|
||||
|
||||
/// @dev This mixin contains logic for staking pools.
|
||||
contract MixinStakingPoolMakers is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage,
|
||||
MixinStakingPoolModifiers
|
||||
{
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
using LibSafeDowncast for uint256;
|
||||
|
||||
/// @dev Allows caller to join a staking pool if already assigned.
|
||||
/// @param poolId Unique id of pool.
|
||||
function joinStakingPoolAsMaker(
|
||||
bytes32 poolId
|
||||
)
|
||||
external
|
||||
{
|
||||
// Is the maker already in a pool?
|
||||
address makerAddress = msg.sender;
|
||||
if (isMakerAssignedToStakingPool(makerAddress)) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressAlreadyRegistered,
|
||||
makerAddress,
|
||||
getStakingPoolIdOfMaker(makerAddress)
|
||||
));
|
||||
}
|
||||
|
||||
IStructs.MakerPoolJoinStatus memory poolJoinStatus = IStructs.MakerPoolJoinStatus({
|
||||
poolId: poolId,
|
||||
confirmed: false
|
||||
});
|
||||
poolJoinedByMakerAddress[makerAddress] = poolJoinStatus;
|
||||
|
||||
// Maker has joined to the pool, awaiting operator confirmation
|
||||
emit PendingAddMakerToPool(
|
||||
poolId,
|
||||
makerAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Adds a maker to a staking pool. Note that this is only callable by the pool operator.
|
||||
/// Note also that the maker must have previously called joinStakingPoolAsMaker.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function addMakerToStakingPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
external
|
||||
onlyStakingPoolOperator(poolId)
|
||||
{
|
||||
_addMakerToStakingPool(poolId, makerAddress);
|
||||
}
|
||||
|
||||
/// @dev Removes a maker from a staking pool. Note that this is only callable by the pool operator or maker.
|
||||
/// Note also that the maker does not have to *agree* to leave the pool; this action is
|
||||
/// at the sole discretion of the pool operator.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function removeMakerFromStakingPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
external
|
||||
onlyStakingPoolOperatorOrMaker(poolId, makerAddress)
|
||||
{
|
||||
bytes32 makerPoolId = getStakingPoolIdOfMaker(makerAddress);
|
||||
if (makerPoolId != poolId) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressNotRegistered,
|
||||
makerAddress,
|
||||
makerPoolId
|
||||
));
|
||||
}
|
||||
|
||||
// remove the pool and confirmation from the maker status
|
||||
IStructs.MakerPoolJoinStatus memory poolJoinStatus = IStructs.MakerPoolJoinStatus({
|
||||
poolId: NIL_POOL_ID,
|
||||
confirmed: false
|
||||
});
|
||||
poolJoinedByMakerAddress[makerAddress] = poolJoinStatus;
|
||||
_poolById[poolId].numberOfMakers = uint256(_poolById[poolId].numberOfMakers).safeSub(1).downcastToUint32();
|
||||
|
||||
// Maker has been removed from the pool`
|
||||
emit MakerRemovedFromStakingPool(
|
||||
poolId,
|
||||
makerAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Returns the pool id of the input maker.
|
||||
/// @param makerAddress Address of maker
|
||||
/// @return Pool id, nil if maker is not yet assigned to a pool.
|
||||
function getStakingPoolIdOfMaker(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bytes32)
|
||||
{
|
||||
if (isMakerAssignedToStakingPool(makerAddress)) {
|
||||
return poolJoinedByMakerAddress[makerAddress].poolId;
|
||||
} else {
|
||||
return NIL_POOL_ID;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Returns true iff the maker is assigned to a staking pool.
|
||||
/// @param makerAddress Address of maker
|
||||
/// @return True iff assigned.
|
||||
function isMakerAssignedToStakingPool(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return poolJoinedByMakerAddress[makerAddress].confirmed;
|
||||
}
|
||||
|
||||
/// @dev Adds a maker to a staking pool. Note that this is only callable by the pool operator.
|
||||
/// Note also that the maker must have previously called joinStakingPoolAsMaker.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function _addMakerToStakingPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
internal
|
||||
{
|
||||
// cache pool for use throughout this function
|
||||
IStructs.Pool memory pool = _poolById[poolId];
|
||||
|
||||
// Is the maker already in a pool?
|
||||
if (isMakerAssignedToStakingPool(makerAddress)) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressAlreadyRegistered,
|
||||
makerAddress,
|
||||
getStakingPoolIdOfMaker(makerAddress)
|
||||
));
|
||||
}
|
||||
|
||||
// Is the maker trying to join this pool; or are they the operator?
|
||||
bytes32 makerPendingPoolId = poolJoinedByMakerAddress[makerAddress].poolId;
|
||||
if (makerPendingPoolId != poolId && makerAddress != pool.operator) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressNotPendingAdd,
|
||||
makerAddress,
|
||||
makerPendingPoolId
|
||||
));
|
||||
}
|
||||
|
||||
// Is the pool already full?
|
||||
// NOTE: If maximumMakersInPool is decreased below the number of makers currently in a pool,
|
||||
// the pool will no longer be able to add more makers.
|
||||
if (pool.numberOfMakers >= maximumMakersInPool) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(
|
||||
LibStakingRichErrors.MakerPoolAssignmentErrorCodes.PoolIsFull,
|
||||
makerAddress,
|
||||
poolId
|
||||
));
|
||||
}
|
||||
|
||||
// Add maker to pool
|
||||
IStructs.MakerPoolJoinStatus memory poolJoinStatus = IStructs.MakerPoolJoinStatus({
|
||||
poolId: poolId,
|
||||
confirmed: true
|
||||
});
|
||||
poolJoinedByMakerAddress[makerAddress] = poolJoinStatus;
|
||||
_poolById[poolId].numberOfMakers = uint256(pool.numberOfMakers).safeAdd(1).downcastToUint32();
|
||||
|
||||
// Maker has been added to the pool
|
||||
emit MakerAddedToStakingPool(
|
||||
poolId,
|
||||
makerAddress
|
||||
);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../immutable/MixinStorage.sol";
|
||||
|
||||
|
||||
contract MixinStakingPoolModifiers is
|
||||
MixinConstants,
|
||||
Ownable,
|
||||
MixinStorage
|
||||
{
|
||||
|
||||
/// @dev Asserts that the sender is the operator of the input pool.
|
||||
/// @param poolId Pool sender must be operator of.
|
||||
modifier onlyStakingPoolOperator(bytes32 poolId) {
|
||||
address operator = _poolById[poolId].operator;
|
||||
if (msg.sender != operator) {
|
||||
LibRichErrors.rrevert(LibStakingRichErrors.OnlyCallableByPoolOperatorError(
|
||||
msg.sender,
|
||||
operator
|
||||
));
|
||||
}
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Asserts that the sender is the operator of the input pool or the input maker.
|
||||
/// @param poolId Pool sender must be operator of.
|
||||
/// @param makerAddress Address of a maker in the pool.
|
||||
modifier onlyStakingPoolOperatorOrMaker(bytes32 poolId, address makerAddress) {
|
||||
address operator = _poolById[poolId].operator;
|
||||
if (
|
||||
msg.sender != operator &&
|
||||
msg.sender != makerAddress
|
||||
) {
|
||||
LibRichErrors.rrevert(
|
||||
LibStakingRichErrors.OnlyCallableByPoolOperatorOrMakerError(
|
||||
msg.sender,
|
||||
operator,
|
||||
makerAddress
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_;
|
||||
}
|
||||
}
|
@ -188,8 +188,8 @@ contract MixinStakingPoolRewards is
|
||||
}
|
||||
|
||||
if (membersReward > 0) {
|
||||
// Increment the balance of the pool
|
||||
_incrementPoolRewards(poolId, membersReward);
|
||||
// Increase the balance of the pool
|
||||
_increasePoolRewards(poolId, membersReward);
|
||||
|
||||
// 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.
|
||||
@ -279,8 +279,8 @@ contract MixinStakingPoolRewards is
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrement the balance of the pool
|
||||
_decrementPoolRewards(poolId, balance);
|
||||
// Decrease the balance of the pool
|
||||
_decreasePoolRewards(poolId, balance);
|
||||
|
||||
// Withdraw the member's WETH balance
|
||||
_getWethContract().transfer(member, balance);
|
||||
@ -421,23 +421,23 @@ contract MixinStakingPoolRewards is
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Increments rewards for a pool.
|
||||
/// @dev Increases rewards for a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param amount Amount to increment rewards by.
|
||||
function _incrementPoolRewards(bytes32 poolId, uint256 amount)
|
||||
function _increasePoolRewards(bytes32 poolId, uint256 amount)
|
||||
private
|
||||
{
|
||||
rewardsByPoolId[poolId] = rewardsByPoolId[poolId].safeAdd(amount);
|
||||
_wethReservedForPoolRewards = _wethReservedForPoolRewards.safeAdd(amount);
|
||||
wethReservedForPoolRewards = wethReservedForPoolRewards.safeAdd(amount);
|
||||
}
|
||||
|
||||
/// @dev Decrements rewards for a pool.
|
||||
/// @dev Decreases rewards for a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param amount Amount to decrement rewards by.
|
||||
function _decrementPoolRewards(bytes32 poolId, uint256 amount)
|
||||
function _decreasePoolRewards(bytes32 poolId, uint256 amount)
|
||||
private
|
||||
{
|
||||
rewardsByPoolId[poolId] = rewardsByPoolId[poolId].safeSub(amount);
|
||||
_wethReservedForPoolRewards = _wethReservedForPoolRewards.safeSub(amount);
|
||||
wethReservedForPoolRewards = wethReservedForPoolRewards.safeSub(amount);
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ contract MixinFinalizer is
|
||||
returns (uint256 wethBalance)
|
||||
{
|
||||
wethBalance = _getWethContract().balanceOf(address(this))
|
||||
.safeSub(_wethReservedForPoolRewards);
|
||||
.safeSub(wethReservedForPoolRewards);
|
||||
|
||||
return wethBalance;
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ contract TestProtocolFees is
|
||||
function addMakerToPool(bytes32 poolId, address makerAddress)
|
||||
external
|
||||
{
|
||||
poolJoinedByMakerAddress[makerAddress].poolId = poolId;
|
||||
poolJoinedByMakerAddress[makerAddress].confirmed = true;
|
||||
_poolJoinedByMakerAddress[makerAddress].poolId = poolId;
|
||||
_poolJoinedByMakerAddress[makerAddress].confirmed = true;
|
||||
}
|
||||
|
||||
function advanceEpoch()
|
||||
|
@ -37,7 +37,7 @@
|
||||
},
|
||||
"config": {
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IVaultCore|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolMakers|MixinStakingPoolModifiers|MixinStakingPoolRewards|MixinStorage|MixinVaultCore|ReadOnlyProxy|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestMixinVaultCore|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|ZrxVault).json"
|
||||
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IVaultCore|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|MixinVaultCore|ReadOnlyProxy|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestMixinVaultCore|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|ZrxVault).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -32,8 +32,6 @@ import * as MixinStake from '../generated-artifacts/MixinStake.json';
|
||||
import * as MixinStakeBalances from '../generated-artifacts/MixinStakeBalances.json';
|
||||
import * as MixinStakeStorage from '../generated-artifacts/MixinStakeStorage.json';
|
||||
import * as MixinStakingPool from '../generated-artifacts/MixinStakingPool.json';
|
||||
import * as MixinStakingPoolMakers from '../generated-artifacts/MixinStakingPoolMakers.json';
|
||||
import * as MixinStakingPoolModifiers from '../generated-artifacts/MixinStakingPoolModifiers.json';
|
||||
import * as MixinStakingPoolRewards from '../generated-artifacts/MixinStakingPoolRewards.json';
|
||||
import * as MixinStorage from '../generated-artifacts/MixinStorage.json';
|
||||
import * as MixinVaultCore from '../generated-artifacts/MixinVaultCore.json';
|
||||
@ -84,8 +82,6 @@ export const artifacts = {
|
||||
MixinStakeStorage: MixinStakeStorage as ContractArtifact,
|
||||
MixinCumulativeRewards: MixinCumulativeRewards as ContractArtifact,
|
||||
MixinStakingPool: MixinStakingPool as ContractArtifact,
|
||||
MixinStakingPoolMakers: MixinStakingPoolMakers as ContractArtifact,
|
||||
MixinStakingPoolModifiers: MixinStakingPoolModifiers as ContractArtifact,
|
||||
MixinStakingPoolRewards: MixinStakingPoolRewards as ContractArtifact,
|
||||
MixinAbstract: MixinAbstract as ContractArtifact,
|
||||
MixinFinalizer: MixinFinalizer as ContractArtifact,
|
||||
|
@ -30,8 +30,6 @@ export * from '../generated-wrappers/mixin_stake';
|
||||
export * from '../generated-wrappers/mixin_stake_balances';
|
||||
export * from '../generated-wrappers/mixin_stake_storage';
|
||||
export * from '../generated-wrappers/mixin_staking_pool';
|
||||
export * from '../generated-wrappers/mixin_staking_pool_makers';
|
||||
export * from '../generated-wrappers/mixin_staking_pool_modifiers';
|
||||
export * from '../generated-wrappers/mixin_staking_pool_rewards';
|
||||
export * from '../generated-wrappers/mixin_storage';
|
||||
export * from '../generated-wrappers/mixin_vault_core';
|
||||
|
@ -241,7 +241,7 @@ export class FinalizerActor extends BaseActor {
|
||||
this._stakingApiWrapper.stakingContract.getActiveStakingPoolThisEpoch.callAsync(poolId),
|
||||
),
|
||||
);
|
||||
const totalRewards = await this._stakingApiWrapper.stakingContract.getAvailableRewardsBalance.callAsync();
|
||||
const totalRewards = await this._stakingApiWrapper.utils.getAvailableRewardsBalanceAsync();
|
||||
const totalFeesCollected = BigNumber.sum(...activePools.map(p => p.feesCollected));
|
||||
const totalWeightedStake = BigNumber.sum(...activePools.map(p => p.weightedStake));
|
||||
if (totalRewards.eq(0) || totalFeesCollected.eq(0) || totalWeightedStake.eq(0)) {
|
||||
|
@ -4,9 +4,9 @@ import * as _ from 'lodash';
|
||||
|
||||
import { constants as stakingConstants } from '../utils/constants';
|
||||
|
||||
import { BaseActor } from './base_actor';
|
||||
import { PoolOperatorActor } from './pool_operator_actor';
|
||||
|
||||
export class MakerActor extends BaseActor {
|
||||
export class MakerActor extends PoolOperatorActor {
|
||||
public async joinStakingPoolAsMakerAsync(poolId: string, revertError?: RevertError): Promise<void> {
|
||||
// Join pool
|
||||
const txReceiptPromise = this._stakingApiWrapper.stakingContract.joinStakingPoolAsMaker.awaitTransactionSuccessAsync(
|
||||
@ -26,29 +26,4 @@ export class MakerActor extends BaseActor {
|
||||
);
|
||||
expect(poolIdOfMaker, 'pool id of maker').to.be.equal(stakingConstants.NIL_POOL_ID);
|
||||
}
|
||||
|
||||
public async removeMakerFromStakingPoolAsync(
|
||||
poolId: string,
|
||||
makerAddress: string,
|
||||
revertError?: RevertError,
|
||||
): Promise<void> {
|
||||
// remove maker (should fail if makerAddress !== this._owner)
|
||||
const txReceiptPromise = this._stakingApiWrapper.stakingContract.removeMakerFromStakingPool.awaitTransactionSuccessAsync(
|
||||
poolId,
|
||||
makerAddress,
|
||||
{ from: this._owner },
|
||||
);
|
||||
|
||||
if (revertError !== undefined) {
|
||||
await expect(txReceiptPromise).to.revertWith(revertError);
|
||||
return;
|
||||
}
|
||||
await txReceiptPromise;
|
||||
|
||||
// check the pool id of the maker
|
||||
const poolIdOfMakerAfterRemoving = await this._stakingApiWrapper.stakingContract.getStakingPoolIdOfMaker.callAsync(
|
||||
this._owner,
|
||||
);
|
||||
expect(poolIdOfMakerAfterRemoving, 'pool id of maker').to.be.equal(stakingConstants.NIL_POOL_ID);
|
||||
}
|
||||
}
|
||||
|
18
contracts/staking/test/codesize.ts
Normal file
18
contracts/staking/test/codesize.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { constants, expect, getCodesizeFromArtifact } from '@0x/contracts-test-utils';
|
||||
|
||||
import { artifacts } from '../src';
|
||||
|
||||
describe.skip('Contract Size Checks', () => {
|
||||
describe('Staking', () => {
|
||||
it('should have a codesize less than the maximum', async () => {
|
||||
const actualSize = getCodesizeFromArtifact(artifacts.Staking);
|
||||
expect(actualSize).to.be.lt(constants.MAX_CODE_SIZE);
|
||||
});
|
||||
});
|
||||
describe('StakingProxy', () => {
|
||||
it('should have a codesize less than the maximum', async () => {
|
||||
const actualSize = getCodesizeFromArtifact(artifacts.StakingProxy);
|
||||
expect(actualSize).to.be.lt(constants.MAX_CODE_SIZE);
|
||||
});
|
||||
});
|
||||
});
|
@ -9,6 +9,7 @@ import { deployAndConfigureContractsAsync, StakingApiWrapper } from './utils/api
|
||||
import { constants as stakingConstants } from './utils/constants';
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
// tslint:disable:max-file-line-count
|
||||
blockchainTests('Staking Pool Management', env => {
|
||||
// constants
|
||||
const { PPM_100_PERCENT, PPM_DENOMINATOR } = constants;
|
||||
@ -23,8 +24,7 @@ blockchainTests('Staking Pool Management', env => {
|
||||
before(async () => {
|
||||
// create accounts
|
||||
accounts = await env.getAccountAddressesAsync();
|
||||
owner = accounts[0];
|
||||
users = accounts.slice(1);
|
||||
[owner, ...users] = accounts;
|
||||
// set up ERC20Wrapper
|
||||
erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
|
||||
// deploy staking contracts
|
||||
@ -102,6 +102,65 @@ blockchainTests('Staking Pool Management', env => {
|
||||
// operator removes maker from pool
|
||||
await poolOperator.removeMakerFromStakingPoolAsync(poolId, makerAddress);
|
||||
});
|
||||
it('Should successfully add/remove a maker to a pool if approved by maker', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const maker1Address = users[1];
|
||||
const maker1 = new MakerActor(maker1Address, stakingApiWrapper);
|
||||
const maker2Address = users[2];
|
||||
const maker2 = new MakerActor(maker2Address, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// maker joins pool
|
||||
await maker1.joinStakingPoolAsMakerAsync(poolId);
|
||||
// operator adds maker to pool
|
||||
await poolOperator.addMakerToStakingPoolAsync(poolId, maker1Address);
|
||||
// maker joins pool
|
||||
await maker2.joinStakingPoolAsMakerAsync(poolId);
|
||||
// approved maker adds new maker to pool
|
||||
await maker1.addMakerToStakingPoolAsync(poolId, maker2Address);
|
||||
});
|
||||
it('should fail to add a maker to a pool if called by pending maker', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const makerAddress = users[1];
|
||||
const maker = new MakerActor(makerAddress, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// maker joins pool
|
||||
await maker.joinStakingPoolAsMakerAsync(poolId);
|
||||
await maker.addMakerToStakingPoolAsync(
|
||||
poolId,
|
||||
makerAddress,
|
||||
new StakingRevertErrors.OnlyCallableByPoolOperatorOrMakerError(makerAddress, poolId),
|
||||
);
|
||||
});
|
||||
it('should fail to add a maker to a pool if not called by operator/registered maker', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const maker1Address = users[1];
|
||||
const maker1 = new MakerActor(maker1Address, stakingApiWrapper);
|
||||
const maker2Address = users[2];
|
||||
const maker2 = new MakerActor(maker2Address, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// maker joins pool
|
||||
await maker1.joinStakingPoolAsMakerAsync(poolId);
|
||||
await maker2.addMakerToStakingPoolAsync(
|
||||
poolId,
|
||||
maker1Address,
|
||||
new StakingRevertErrors.OnlyCallableByPoolOperatorOrMakerError(maker2Address, poolId),
|
||||
);
|
||||
});
|
||||
it('Maker should successfully remove themselves from a pool', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
@ -119,6 +178,23 @@ blockchainTests('Staking Pool Management', env => {
|
||||
// maker removes themselves from pool
|
||||
await maker.removeMakerFromStakingPoolAsync(poolId, makerAddress);
|
||||
});
|
||||
it('operator can remove a maker from their pool', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const makerAddress = users[1];
|
||||
const maker = new MakerActor(makerAddress, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// maker joins pool
|
||||
await maker.joinStakingPoolAsMakerAsync(poolId);
|
||||
// operator adds maker to pool
|
||||
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress);
|
||||
// operator removes maker from pool
|
||||
await poolOperator.removeMakerFromStakingPoolAsync(poolId, makerAddress);
|
||||
});
|
||||
it('Should successfully add/remove multiple makers to the same pool', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
@ -152,6 +228,28 @@ blockchainTests('Staking Pool Management', env => {
|
||||
pool = await stakingApiWrapper.stakingContract.getStakingPool.callAsync(poolId);
|
||||
expect(pool.numberOfMakers, 'number of makers in pool after removing').to.be.bignumber.equal(0);
|
||||
});
|
||||
it('should fail to remove a maker from a pool if not called by operator/registered maker', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const maker1Address = users[1];
|
||||
const maker1 = new MakerActor(maker1Address, stakingApiWrapper);
|
||||
const maker2Address = users[2];
|
||||
const maker2 = new MakerActor(maker2Address, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// maker joins pool
|
||||
await maker1.joinStakingPoolAsMakerAsync(poolId);
|
||||
// operator adds maker to pool
|
||||
await poolOperator.addMakerToStakingPoolAsync(poolId, maker1Address);
|
||||
await maker2.removeMakerFromStakingPoolAsync(
|
||||
poolId,
|
||||
maker1Address,
|
||||
new StakingRevertErrors.OnlyCallableByPoolOperatorOrMakerError(maker2Address, poolId),
|
||||
);
|
||||
});
|
||||
it('Should fail if maker already assigned to another pool tries to join', async () => {
|
||||
// test parameters
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
@ -264,30 +362,6 @@ blockchainTests('Staking Pool Management', env => {
|
||||
// remove non-existent maker from pool
|
||||
await poolOperator.removeMakerFromStakingPoolAsync(poolId, makerAddress, revertError);
|
||||
});
|
||||
it('Should fail to add a maker when called by someone other than the pool operator', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const makerAddress = users[1];
|
||||
const maker = new MakerActor(makerAddress, stakingApiWrapper);
|
||||
const notOperatorAddress = users[2];
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// add maker to pool
|
||||
await maker.joinStakingPoolAsMakerAsync(poolId);
|
||||
const revertError = new StakingRevertErrors.OnlyCallableByPoolOperatorError(
|
||||
notOperatorAddress,
|
||||
operatorAddress,
|
||||
);
|
||||
const tx = stakingApiWrapper.stakingContract.addMakerToStakingPool.awaitTransactionSuccessAsync(
|
||||
poolId,
|
||||
makerAddress,
|
||||
{ from: notOperatorAddress },
|
||||
);
|
||||
await expect(tx).to.revertWith(revertError);
|
||||
});
|
||||
it('Should fail to remove a maker when called by someone other than the pool operator or maker', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
@ -305,8 +379,7 @@ blockchainTests('Staking Pool Management', env => {
|
||||
// try to remove the maker address from an address other than the operator
|
||||
const revertError = new StakingRevertErrors.OnlyCallableByPoolOperatorOrMakerError(
|
||||
neitherOperatorNorMakerAddress,
|
||||
operatorAddress,
|
||||
makerAddress,
|
||||
poolId,
|
||||
);
|
||||
const tx = stakingApiWrapper.stakingContract.removeMakerFromStakingPool.awaitTransactionSuccessAsync(
|
||||
poolId,
|
||||
@ -364,6 +437,24 @@ blockchainTests('Staking Pool Management', env => {
|
||||
// decrease operator share
|
||||
await poolOperator.decreaseStakingPoolOperatorShareAsync(poolId, operatorShare - 1);
|
||||
});
|
||||
it('Maker should successfuly decrease their share of rewards', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const makerAddress = users[1];
|
||||
const maker = new MakerActor(makerAddress, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
// maker joins pool
|
||||
await maker.joinStakingPoolAsMakerAsync(poolId);
|
||||
// operator adds maker to pool
|
||||
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress);
|
||||
|
||||
// decrease operator share
|
||||
await maker.decreaseStakingPoolOperatorShareAsync(poolId, operatorShare - 1);
|
||||
});
|
||||
it('Should fail if operator tries to increase their share of rewards', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
@ -401,6 +492,22 @@ blockchainTests('Staking Pool Management', env => {
|
||||
// decrease operator share
|
||||
await poolOperator.decreaseStakingPoolOperatorShareAsync(poolId, operatorShare, revertError);
|
||||
});
|
||||
it('should fail to decrease operator share if not called by operator/registered maker', async () => {
|
||||
// test parameters
|
||||
const operatorAddress = users[0];
|
||||
const operatorShare = (39 / 100) * PPM_DENOMINATOR;
|
||||
const poolOperator = new PoolOperatorActor(operatorAddress, stakingApiWrapper);
|
||||
const makerAddress = users[1];
|
||||
const maker = new MakerActor(makerAddress, stakingApiWrapper);
|
||||
// create pool
|
||||
const poolId = await poolOperator.createStakingPoolAsync(operatorShare, true);
|
||||
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
||||
await maker.decreaseStakingPoolOperatorShareAsync(
|
||||
poolId,
|
||||
operatorShare - 1,
|
||||
new StakingRevertErrors.OnlyCallableByPoolOperatorOrMakerError(makerAddress, poolId),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
||||
|
@ -122,10 +122,13 @@ export class StakingApiWrapper {
|
||||
);
|
||||
},
|
||||
|
||||
getEthAndWethBalanceOfAsync: async (address: string): Promise<BigNumber> => {
|
||||
const ethBalance = await this._web3Wrapper.getBalanceInWeiAsync(address);
|
||||
const wethBalance = await this.wethContract.balanceOf.callAsync(address);
|
||||
return BigNumber.sum(ethBalance, wethBalance);
|
||||
getAvailableRewardsBalanceAsync: async (): Promise<BigNumber> => {
|
||||
const [ethBalance, wethBalance, reservedRewards] = await Promise.all([
|
||||
this._web3Wrapper.getBalanceInWeiAsync(this.stakingProxyContract.address),
|
||||
this.wethContract.balanceOf.callAsync(this.stakingProxyContract.address),
|
||||
this.stakingContract.wethReservedForPoolRewards.callAsync(),
|
||||
]);
|
||||
return BigNumber.sum(ethBalance, wethBalance).minus(reservedRewards);
|
||||
},
|
||||
|
||||
getParamsAsync: async (): Promise<StakingParams> => {
|
||||
|
@ -30,8 +30,6 @@
|
||||
"generated-artifacts/MixinStakeBalances.json",
|
||||
"generated-artifacts/MixinStakeStorage.json",
|
||||
"generated-artifacts/MixinStakingPool.json",
|
||||
"generated-artifacts/MixinStakingPoolMakers.json",
|
||||
"generated-artifacts/MixinStakingPoolModifiers.json",
|
||||
"generated-artifacts/MixinStakingPoolRewards.json",
|
||||
"generated-artifacts/MixinStorage.json",
|
||||
"generated-artifacts/MixinVaultCore.json",
|
||||
|
@ -83,4 +83,5 @@ export const constants = {
|
||||
NUM_TEST_ACCOUNTS: 20,
|
||||
PPM_DENOMINATOR: 1e6,
|
||||
PPM_100_PERCENT: 1e6,
|
||||
MAX_CODE_SIZE: 24576,
|
||||
};
|
||||
|
@ -77,22 +77,12 @@ export class InsufficientBalanceError extends RevertError {
|
||||
}
|
||||
}
|
||||
|
||||
export class OnlyCallableByPoolOperatorError extends RevertError {
|
||||
constructor(senderAddress?: string, poolOperatorAddress?: string) {
|
||||
super(
|
||||
'OnlyCallableByPoolOperatorError',
|
||||
'OnlyCallableByPoolOperatorError(address senderAddress, address poolOperatorAddress)',
|
||||
{ senderAddress, poolOperatorAddress },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class OnlyCallableByPoolOperatorOrMakerError extends RevertError {
|
||||
constructor(senderAddress?: string, poolOperatorAddress?: string, makerAddress?: string) {
|
||||
constructor(senderAddress?: string, poolId?: string) {
|
||||
super(
|
||||
'OnlyCallableByPoolOperatorOrMakerError',
|
||||
'OnlyCallableByPoolOperatorOrMakerError(address senderAddress, address poolOperatorAddress, address makerAddress)',
|
||||
{ senderAddress, poolOperatorAddress, makerAddress },
|
||||
'OnlyCallableByPoolOperatorOrMakerError(address senderAddress, bytes32 poolId)',
|
||||
{ senderAddress, poolId },
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -243,7 +233,6 @@ const types = [
|
||||
MakerPoolAssignmentError,
|
||||
MiscalculatedRewardsError,
|
||||
OnlyCallableByExchangeError,
|
||||
OnlyCallableByPoolOperatorError,
|
||||
OnlyCallableByPoolOperatorOrMakerError,
|
||||
OnlyCallableByStakingContractError,
|
||||
OnlyCallableIfInCatastrophicFailureError,
|
||||
|
Loading…
x
Reference in New Issue
Block a user