Cleaned up staking pool mixin
This commit is contained in:
parent
829533d501
commit
ab3246cc71
@ -25,7 +25,7 @@ import "./core/MixinRewardVault.sol";
|
||||
import "./core/MixinScheduler.sol";
|
||||
import "./core/MixinStakeBalances.sol";
|
||||
import "./core/MixinStake.sol";
|
||||
import "./core/MixinPools.sol";
|
||||
import "./core/MixinStakingPool.sol";
|
||||
import "./core/MixinExchangeFees.sol";
|
||||
import "./core/MixinRewards.sol";
|
||||
|
||||
@ -40,7 +40,7 @@ contract Staking is
|
||||
MixinZrxVault,
|
||||
MixinExchangeManager,
|
||||
MixinStakeBalances,
|
||||
MixinPools,
|
||||
MixinStakingPool,
|
||||
MixinRewards,
|
||||
MixinStake,
|
||||
MixinExchangeFees
|
||||
|
@ -25,7 +25,7 @@ import "../immutable/MixinConstants.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
import "./MixinStakeBalances.sol";
|
||||
import "./MixinScheduler.sol";
|
||||
import "./MixinPools.sol";
|
||||
import "./MixinStakingPool.sol";
|
||||
import "./MixinExchangeManager.sol";
|
||||
import "./MixinRewardVault.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
@ -39,14 +39,14 @@ contract MixinExchangeFees is
|
||||
MixinRewardVault,
|
||||
MixinExchangeManager,
|
||||
MixinStakeBalances,
|
||||
MixinPools
|
||||
MixinStakingPool
|
||||
{
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev This mixin contains the logic for 0x protocol fees.
|
||||
/// Protocol fees are sent by 0x exchanges every time there is a trade.
|
||||
/// If the maker has associated their address with a pool (see MixinPools.sol), then
|
||||
/// If the maker has associated their address with a pool (see MixinStakingPool.sol), then
|
||||
/// the fee will be attributed to their pool. At the end of an epoch the maker and
|
||||
/// their pool will receive a rebate that is proportional to (i) the fee volume attributed
|
||||
/// to their pool over the epoch, and (ii) the amount of stake provided by the maker and
|
||||
@ -63,7 +63,7 @@ contract MixinExchangeFees is
|
||||
onlyExchange
|
||||
{
|
||||
uint256 amount = msg.value;
|
||||
bytes32 poolId = getMakerPoolId(makerAddress);
|
||||
bytes32 poolId = getPoolIdOfMaker(makerAddress);
|
||||
uint256 _feesCollectedThisEpoch = protocolFeesThisEpochByPool[poolId];
|
||||
protocolFeesThisEpochByPool[poolId] = _feesCollectedThisEpoch._add(amount);
|
||||
if (_feesCollectedThisEpoch == 0) {
|
||||
|
@ -1,243 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../libs/LibSafeMath.sol";
|
||||
import "../libs/LibSignatureValidator.sol";
|
||||
import "../libs/LibEIP712Hash.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
import "../immutable/MixinStorage.sol";
|
||||
import "./MixinRewardVault.sol";
|
||||
|
||||
|
||||
contract MixinPools is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
MixinStorage,
|
||||
MixinRewardVault
|
||||
{
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev This mixin contains logic for pools.
|
||||
/// A "pool" can be delegated to by any number of stakers.
|
||||
/// A market maker can create a pool
|
||||
|
||||
modifier onlyPoolOperator(bytes32 poolId) {
|
||||
require(
|
||||
msg.sender == getPoolOperator(poolId),
|
||||
"ONLY_CALLABLE_BY_POOL_OPERATOR"
|
||||
);
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
function createPool(uint8 operatorShare)
|
||||
external
|
||||
returns (bytes32 poolId)
|
||||
{
|
||||
address payable operatorAddress = msg.sender;
|
||||
|
||||
poolId = nextPoolId;
|
||||
nextPoolId = _computeNextPoolId(poolId);
|
||||
|
||||
//
|
||||
IStructs.Pool memory pool = IStructs.Pool({
|
||||
operatorAddress: operatorAddress,
|
||||
operatorShare: operatorShare
|
||||
});
|
||||
poolById[poolId] = pool;
|
||||
|
||||
// create pool in reward vault
|
||||
_createPoolInRewardVault(poolId, operatorShare);
|
||||
|
||||
//
|
||||
emit PoolCreated(poolId, operatorAddress, operatorShare);
|
||||
return poolId;
|
||||
}
|
||||
|
||||
function addMakerToPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress,
|
||||
bytes calldata makerSignature
|
||||
)
|
||||
external
|
||||
onlyPoolOperator(poolId)
|
||||
{
|
||||
require(
|
||||
isValidMakerSignature(poolId, makerAddress, makerSignature),
|
||||
"INVALID_MAKER_SIGNATURE"
|
||||
);
|
||||
|
||||
_recordMaker(poolId, makerAddress);
|
||||
}
|
||||
|
||||
function removeMakerFromPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
external
|
||||
onlyPoolOperator(poolId)
|
||||
{
|
||||
_unrecordMaker(poolId, makerAddress);
|
||||
}
|
||||
|
||||
function getNextPoolId()
|
||||
public
|
||||
returns (bytes32)
|
||||
{
|
||||
return nextPoolId;
|
||||
}
|
||||
|
||||
function isValidMakerSignature(bytes32 poolId, address makerAddress, bytes memory makerSignature)
|
||||
public
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
bytes32 approvalHash = getStakingPoolApprovalMessageHash(poolId, makerAddress);
|
||||
isValid = LibSignatureValidator._isValidSignature(approvalHash, makerAddress, makerSignature);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function getStakingPoolApprovalMessageHash(bytes32 poolId, address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
IStructs.StakingPoolApproval memory approval = IStructs.StakingPoolApproval({
|
||||
poolId: poolId,
|
||||
makerAddress: makerAddress
|
||||
});
|
||||
|
||||
// Hash approval message and check signer address
|
||||
address verifierAddress = address(this);
|
||||
approvalHash = LibEIP712Hash._hashStakingPoolApprovalMessage(approval, CHAIN_ID, verifierAddress);
|
||||
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
function getMakerPoolId(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bytes32)
|
||||
{
|
||||
return poolIdByMakerAddress[makerAddress];
|
||||
}
|
||||
|
||||
function getPoolOperator(bytes32 poolId)
|
||||
public
|
||||
view
|
||||
returns (address operatorAddress)
|
||||
{
|
||||
operatorAddress = poolById[poolId].operatorAddress;
|
||||
}
|
||||
|
||||
function isMakerRegistered(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return getMakerPoolId(makerAddress) != NIL_MAKER_ID;
|
||||
}
|
||||
|
||||
function getMakerAddressesForPool(bytes32 poolId)
|
||||
public
|
||||
view
|
||||
returns (address[] memory _makerAddressesByPoolId)
|
||||
{
|
||||
//
|
||||
address[] storage makerAddressesByPoolIdPtr = makerAddressesByPoolId[poolId];
|
||||
uint256 makerAddressesByPoolIdLength = makerAddressesByPoolIdPtr.length;
|
||||
|
||||
//
|
||||
_makerAddressesByPoolId = new address[](makerAddressesByPoolIdLength);
|
||||
for (uint i = 0; i < makerAddressesByPoolIdLength; ++i) {
|
||||
_makerAddressesByPoolId[i] = makerAddressesByPoolIdPtr[i];
|
||||
}
|
||||
|
||||
return _makerAddressesByPoolId;
|
||||
}
|
||||
|
||||
function _getPool(bytes32 poolId)
|
||||
internal
|
||||
view
|
||||
returns (IStructs.Pool memory pool)
|
||||
{
|
||||
pool = poolById[poolId];
|
||||
return pool;
|
||||
}
|
||||
|
||||
function _computeNextPoolId(bytes32 poolId)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32)
|
||||
{
|
||||
return bytes32(uint256(poolId)._add(POOL_ID_INCREMENT_AMOUNT));
|
||||
}
|
||||
|
||||
function _recordMaker(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
private
|
||||
{
|
||||
require(
|
||||
!isMakerRegistered(makerAddress),
|
||||
"MAKER_ADDRESS_ALREADY_REGISTERED"
|
||||
);
|
||||
|
||||
poolIdByMakerAddress[makerAddress] = poolId;
|
||||
makerAddressesByPoolId[poolId].push(makerAddress);
|
||||
}
|
||||
|
||||
function _unrecordMaker(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
private
|
||||
{
|
||||
require(
|
||||
getMakerPoolId(makerAddress) == poolId,
|
||||
"MAKER_ADDRESS_NOT_REGISTERED"
|
||||
);
|
||||
|
||||
//
|
||||
address[] storage makerAddressesByPoolIdPtr = makerAddressesByPoolId[poolId];
|
||||
uint256 makerAddressesByPoolIdLength = makerAddressesByPoolIdPtr.length;
|
||||
|
||||
//
|
||||
uint indexOfMakerAddress = 0;
|
||||
for (; indexOfMakerAddress < makerAddressesByPoolIdLength; ++indexOfMakerAddress) {
|
||||
if (makerAddressesByPoolIdPtr[indexOfMakerAddress] == makerAddress) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
makerAddressesByPoolIdPtr[indexOfMakerAddress] = makerAddressesByPoolIdPtr[makerAddressesByPoolIdLength - 1];
|
||||
makerAddressesByPoolIdPtr[indexOfMakerAddress] = NIL_ADDRESS;
|
||||
makerAddressesByPoolIdPtr.length -= 1;
|
||||
|
||||
//
|
||||
poolIdByMakerAddress[makerAddress] = NIL_MAKER_ID;
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ import "../immutable/MixinStorage.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
import "./MixinStakeBalances.sol";
|
||||
import "./MixinRewardVault.sol";
|
||||
import "./MixinPools.sol";
|
||||
import "./MixinStakingPool.sol";
|
||||
|
||||
|
||||
contract MixinRewards is
|
||||
@ -32,12 +32,12 @@ contract MixinRewards is
|
||||
MixinStorage,
|
||||
MixinRewardVault,
|
||||
MixinStakeBalances,
|
||||
MixinPools
|
||||
MixinStakingPool
|
||||
{
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev This mixin contains logic for rewards
|
||||
/// @dev This mixin contains logic for managing the reward pool
|
||||
|
||||
function withdrawOperatorReward(bytes32 poolId, uint256 amount)
|
||||
external
|
||||
|
334
contracts/staking/contracts/src/core/MixinStakingPool.sol
Normal file
334
contracts/staking/contracts/src/core/MixinStakingPool.sol
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../libs/LibSafeMath.sol";
|
||||
import "../libs/LibSignatureValidator.sol";
|
||||
import "../libs/LibEIP712Hash.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
import "../immutable/MixinStorage.sol";
|
||||
import "./MixinRewardVault.sol";
|
||||
|
||||
|
||||
contract MixinStakingPool is
|
||||
IStakingEvents,
|
||||
MixinConstants,
|
||||
MixinStorage,
|
||||
MixinRewardVault
|
||||
{
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev This mixin contains logic for staking pools.
|
||||
/// A pool has a single operator and any number of delegators (members).
|
||||
/// Any staker can create a pool, although at present it is only beneficial
|
||||
/// for market makers to create staking pools. A market maker *must* create a
|
||||
/// pool in order to receive fee-based rewards at the end of each epoch (see MixinExchangeFees).
|
||||
/// Moreover, creating a staking pool leverages the delegated stake within the pool,
|
||||
/// which is counted towards a maker's total stake when computing rewards. A market maker
|
||||
/// can register any number of makerAddresses with their pool, and can incentivize delegators
|
||||
/// to join their pool by specifying a fixed percentage of their fee-based rewards to be split amonst
|
||||
/// the members of their pool. Any rewards set aside for members of the pool is divided based on
|
||||
/// how much stake each member delegated.
|
||||
///
|
||||
/// Terminology:
|
||||
/// "Pool Id" - A unique id generated by this contract and assigned to each pool when it is created.
|
||||
/// "Pool Operator" - The creator and operator of the pool.
|
||||
/// "Pool Members" - Members of the pool who opted-in by delegating to the pool.
|
||||
/// "Market Makers" - Market makers on the 0x protocol.
|
||||
///
|
||||
/// How-To for Market Makers:
|
||||
/// 1. Create a pool, specifying what percentage of rewards kept for yourself.
|
||||
/// The remaining is divided among members of your pool.
|
||||
/// 2. Add the addresses that you use to market make on 0x.
|
||||
/// 3. Leverage the staking power of others by convincing them to delegate to your pool.
|
||||
|
||||
/// @dev Asserts that the sender is the operator of the input pool.
|
||||
/// @param poolId Pool sender must be operator of.
|
||||
modifier onlyPoolOperator(bytes32 poolId) {
|
||||
require(
|
||||
msg.sender == getPoolOperator(poolId),
|
||||
"ONLY_CALLABLE_BY_POOL_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 onlyPoolOperatorOrMaker(bytes32 poolId, address makerAddress) {
|
||||
require(
|
||||
msg.sender == getPoolOperator(poolId) || msg.sender == makerAddress,
|
||||
"ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER"
|
||||
);
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Create a new staking pool. The sender will be the operator of this pool.
|
||||
/// Note that an operator must be payable.
|
||||
/// @param operatorShare The percentage of any rewards owned by the operator.
|
||||
/// @return poolId The unique pool id generated for this pool.
|
||||
function createPool(uint8 operatorShare)
|
||||
external
|
||||
returns (bytes32 poolId)
|
||||
{
|
||||
// note that an operator must be payable
|
||||
address payable operatorAddress = msg.sender;
|
||||
|
||||
// assign pool id and generate next id
|
||||
poolId = nextPoolId;
|
||||
nextPoolId = _computeNextPoolId(poolId);
|
||||
|
||||
// store metadata about this pool
|
||||
IStructs.Pool memory pool = IStructs.Pool({
|
||||
operatorAddress: operatorAddress,
|
||||
operatorShare: operatorShare
|
||||
});
|
||||
poolById[poolId] = pool;
|
||||
|
||||
// register pool in reward vault
|
||||
_createPoolInRewardVault(poolId, operatorShare);
|
||||
|
||||
// notify
|
||||
emit PoolCreated(poolId, operatorAddress, operatorShare);
|
||||
return poolId;
|
||||
}
|
||||
|
||||
/// @dev Adds a maker to a staking pool. Note that this is only callable by the pool operator.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
/// @param makerSignature Signature proving that maker has agreed to join the pool.
|
||||
function addMakerToPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress,
|
||||
bytes calldata makerSignature
|
||||
)
|
||||
external
|
||||
onlyPoolOperator(poolId)
|
||||
{
|
||||
// sanity check - did maker agree to join this pool?
|
||||
require(
|
||||
isValidMakerSignature(poolId, makerAddress, makerSignature),
|
||||
"INVALID_MAKER_SIGNATURE"
|
||||
);
|
||||
|
||||
// maker has agreed, record their address
|
||||
_recordMaker(poolId, makerAddress);
|
||||
}
|
||||
|
||||
/// @dev Adds a maker to 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 removeMakerFromPool(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
onlyPoolOperatorOrMaker(poolId, makerAddress)
|
||||
external
|
||||
{
|
||||
_unrecordMaker(poolId, makerAddress);
|
||||
}
|
||||
|
||||
/// @dev Returns true iff the input signature is valid; meaning that the maker agrees to
|
||||
/// be added to the pool.
|
||||
/// @param poolId Unique id of pool the maker wishes to join.
|
||||
/// @param makerAddress Address of maker.
|
||||
/// @param makerSignature Signature of the maker.
|
||||
/// @return isValid True iff the maker agrees to be added to the pool.
|
||||
function isValidMakerSignature(bytes32 poolId, address makerAddress, bytes memory makerSignature)
|
||||
public
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
bytes32 approvalHash = getStakingPoolApprovalMessageHash(poolId, makerAddress);
|
||||
isValid = LibSignatureValidator._isValidSignature(approvalHash, makerAddress, makerSignature);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/// @dev Returns the approval message hash - this is what a maker must sign in order to
|
||||
/// be added to a pool.
|
||||
/// @param poolId Unique id of pool the maker wishes to join.
|
||||
/// @param makerAddress Address of maker.
|
||||
/// @return approvalHash Hash of message the maker must sign.
|
||||
function getStakingPoolApprovalMessageHash(bytes32 poolId, address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
IStructs.StakingPoolApproval memory approval = IStructs.StakingPoolApproval({
|
||||
poolId: poolId,
|
||||
makerAddress: makerAddress
|
||||
});
|
||||
|
||||
// hash approval message and check signer address
|
||||
address verifierAddress = address(this);
|
||||
approvalHash = LibEIP712Hash._hashStakingPoolApprovalMessage(approval, CHAIN_ID, verifierAddress);
|
||||
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Returns the pool id of an input maker.
|
||||
function getPoolIdOfMaker(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bytes32)
|
||||
{
|
||||
return poolIdByMakerAddress[makerAddress];
|
||||
}
|
||||
|
||||
/// @dev Returns true iff the maker is assigned to a staking pool.
|
||||
/// @param makerAddress Address of maker
|
||||
/// @return True iff assigned.
|
||||
function isMakerAssignedToPool(address makerAddress)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return getPoolIdOfMaker(makerAddress) != NIL_MAKER_ID;
|
||||
}
|
||||
|
||||
/// @dev Returns the makers for a given pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @return _makerAddressesByPoolId Makers for pool.
|
||||
function getMakersForPool(bytes32 poolId)
|
||||
public
|
||||
view
|
||||
returns (address[] memory _makerAddressesByPoolId)
|
||||
{
|
||||
// Load pointer to addresses of makers
|
||||
address[] storage makerAddressesByPoolIdPtr = makerAddressesByPoolId[poolId];
|
||||
uint256 makerAddressesByPoolIdLength = makerAddressesByPoolIdPtr.length;
|
||||
|
||||
// Construct list of makers
|
||||
_makerAddressesByPoolId = new address[](makerAddressesByPoolIdLength);
|
||||
for (uint i = 0; i < makerAddressesByPoolIdLength; ++i) {
|
||||
_makerAddressesByPoolId[i] = makerAddressesByPoolIdPtr[i];
|
||||
}
|
||||
|
||||
return _makerAddressesByPoolId;
|
||||
}
|
||||
|
||||
/// @dev Returns the unique id that will be assigned to the next pool that is created.
|
||||
/// @return Pool id.
|
||||
function getNextPoolId()
|
||||
public
|
||||
view
|
||||
returns (bytes32)
|
||||
{
|
||||
return nextPoolId;
|
||||
}
|
||||
|
||||
/// @dev Returns the pool operator
|
||||
/// @param poolId Unique id of pool
|
||||
/// @return operatorAddress Operator of the pool
|
||||
function getPoolOperator(bytes32 poolId)
|
||||
public
|
||||
view
|
||||
returns (address operatorAddress)
|
||||
{
|
||||
operatorAddress = poolById[poolId].operatorAddress;
|
||||
}
|
||||
|
||||
/// @dev Convenience function for loading information on a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @return pool Pool info.
|
||||
function _getPool(bytes32 poolId)
|
||||
internal
|
||||
view
|
||||
returns (IStructs.Pool memory pool)
|
||||
{
|
||||
pool = poolById[poolId];
|
||||
return pool;
|
||||
}
|
||||
|
||||
/// @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.
|
||||
function _computeNextPoolId(bytes32 poolId)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32)
|
||||
{
|
||||
return bytes32(uint256(poolId)._add(POOL_ID_INCREMENT_AMOUNT));
|
||||
}
|
||||
|
||||
/// @dev Records a maker for a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function _recordMaker(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
private
|
||||
{
|
||||
require(
|
||||
!isMakerAssignedToPool(makerAddress),
|
||||
"MAKER_ADDRESS_ALREADY_REGISTERED"
|
||||
);
|
||||
poolIdByMakerAddress[makerAddress] = poolId;
|
||||
makerAddressesByPoolId[poolId].push(makerAddress);
|
||||
}
|
||||
|
||||
/// @dev Unrecords a maker for a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param makerAddress Address of maker.
|
||||
function _unrecordMaker(
|
||||
bytes32 poolId,
|
||||
address makerAddress
|
||||
)
|
||||
private
|
||||
{
|
||||
require(
|
||||
getPoolIdOfMaker(makerAddress) == poolId,
|
||||
"MAKER_ADDRESS_NOT_REGISTERED"
|
||||
);
|
||||
|
||||
// load list of makers for the input pool.
|
||||
address[] storage makerAddressesByPoolIdPtr = makerAddressesByPoolId[poolId];
|
||||
uint256 makerAddressesByPoolIdLength = makerAddressesByPoolIdPtr.length;
|
||||
|
||||
// find index of maker to remove.
|
||||
uint indexOfMakerAddress = 0;
|
||||
for (; indexOfMakerAddress < makerAddressesByPoolIdLength; ++indexOfMakerAddress) {
|
||||
if (makerAddressesByPoolIdPtr[indexOfMakerAddress] == makerAddress) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the maker from the list of makers for this pool.
|
||||
// (i) move maker at end of list to the slot occupied by the maker to remove, then
|
||||
// (ii) zero out the slot at the end of the list and decrement the length.
|
||||
uint256 indexOfLastMakerAddress = makerAddressesByPoolIdLength - 1;
|
||||
if (indexOfMakerAddress != indexOfLastMakerAddress) {
|
||||
makerAddressesByPoolIdPtr[indexOfMakerAddress] = makerAddressesByPoolIdPtr[indexOfLastMakerAddress];
|
||||
}
|
||||
makerAddressesByPoolIdPtr[indexOfLastMakerAddress] = NIL_ADDRESS;
|
||||
makerAddressesByPoolIdPtr.length -= 1;
|
||||
|
||||
// reset the pool id assigned to the maker.
|
||||
poolIdByMakerAddress[makerAddress] = NIL_MAKER_ID;
|
||||
}
|
||||
}
|
@ -50,10 +50,10 @@ export class PoolOperatorActor extends BaseActor {
|
||||
}
|
||||
await txReceiptPromise;
|
||||
// check the pool id of the maker
|
||||
const poolIdOfMaker = await this._stakingWrapper.getMakerPoolIdAsync(makerAddress);
|
||||
const poolIdOfMaker = await this._stakingWrapper.getPoolIdOfMakerAsync(makerAddress);
|
||||
expect(poolIdOfMaker, 'pool id of maker').to.be.equal(poolId);
|
||||
// check the list of makers for the pool
|
||||
const makerAddressesForPool = await this._stakingWrapper.getMakerAddressesForPoolAsync(poolId);
|
||||
const makerAddressesForPool = await this._stakingWrapper.getMakersForPoolAsync(poolId);
|
||||
expect(makerAddressesForPool, 'maker addresses for pool').to.include(makerAddress);
|
||||
}
|
||||
public async removeMakerFromPoolAsync(
|
||||
@ -69,10 +69,10 @@ export class PoolOperatorActor extends BaseActor {
|
||||
}
|
||||
await txReceiptPromise;
|
||||
// check the pool id of the maker
|
||||
const poolIdOfMakerAfterRemoving = await this._stakingWrapper.getMakerPoolIdAsync(makerAddress);
|
||||
const poolIdOfMakerAfterRemoving = await this._stakingWrapper.getPoolIdOfMakerAsync(makerAddress);
|
||||
expect(poolIdOfMakerAfterRemoving, 'pool id of maker').to.be.equal(stakingConstants.NIL_POOL_ID);
|
||||
// check the list of makers for the pool
|
||||
const makerAddressesForPoolAfterRemoving = await this._stakingWrapper.getMakerAddressesForPoolAsync(poolId);
|
||||
const makerAddressesForPoolAfterRemoving = await this._stakingWrapper.getMakersForPoolAsync(poolId);
|
||||
expect(makerAddressesForPoolAfterRemoving, 'maker addresses for pool').to.not.include(makerAddress);
|
||||
}
|
||||
}
|
||||
|
@ -118,8 +118,7 @@ describe('Staking Pool Management', () => {
|
||||
// remove maker from pool
|
||||
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[0]);
|
||||
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[1]);
|
||||
// @TODO - this fails with `RuntimeError: VM Exception while processing transaction: revert` on Ganache
|
||||
// await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[2]);
|
||||
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[2]);
|
||||
});
|
||||
it('Should fail to add the same maker twice', async () => {
|
||||
// test parameters
|
||||
@ -260,7 +259,7 @@ describe('Staking Pool Management', () => {
|
||||
// try to remove the maker address from an address other than the operator
|
||||
await expectTransactionFailedAsync(
|
||||
stakingWrapper.removeMakerFromPoolAsync(poolId, makerAddress, notOperatorAddress),
|
||||
RevertReason.OnlyCallableByPoolOperator,
|
||||
RevertReason.OnlyCallableByPoolOperatorOrMaker,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -349,15 +349,15 @@ export class StakingWrapper {
|
||||
const txReceipt = await this._executeTransactionAsync(calldata, operatorAddress);
|
||||
return txReceipt;
|
||||
}
|
||||
public async getMakerPoolIdAsync(makerAddress: string): Promise<string> {
|
||||
const calldata = this.getStakingContract().getMakerPoolId.getABIEncodedTransactionData(makerAddress);
|
||||
public async getPoolIdOfMakerAsync(makerAddress: string): Promise<string> {
|
||||
const calldata = this.getStakingContract().getPoolIdOfMaker.getABIEncodedTransactionData(makerAddress);
|
||||
const poolId = await this._callAsync(calldata);
|
||||
return poolId;
|
||||
}
|
||||
public async getMakerAddressesForPoolAsync(poolId: string): Promise<string[]> {
|
||||
const calldata = this.getStakingContract().getMakerAddressesForPool.getABIEncodedTransactionData(poolId);
|
||||
public async getMakersForPoolAsync(poolId: string): Promise<string[]> {
|
||||
const calldata = this.getStakingContract().getMakersForPool.getABIEncodedTransactionData(poolId);
|
||||
const returndata = await this._callAsync(calldata);
|
||||
const makerAddresses = this.getStakingContract().getMakerAddressesForPool.getABIDecodedReturnData(returndata);
|
||||
const makerAddresses = this.getStakingContract().getMakersForPool.getABIDecodedReturnData(returndata);
|
||||
return makerAddresses;
|
||||
}
|
||||
public async isValidMakerSignatureAsync(
|
||||
|
@ -342,6 +342,7 @@ export enum RevertReason {
|
||||
TransfersSuccessful = 'TRANSFERS_SUCCESSFUL',
|
||||
// Staking
|
||||
OnlyCallableByPoolOperator = 'ONLY_CALLABLE_BY_POOL_OPERATOR',
|
||||
OnlyCallableByPoolOperatorOrMaker = 'ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER',
|
||||
MakerAddressAlreadyRegistered = 'MAKER_ADDRESS_ALREADY_REGISTERED',
|
||||
MakerAddressNotRegistered = 'MAKER_ADDRESS_NOT_REGISTERED',
|
||||
OnlyCallableByExchange = 'ONLY_CALLABLE_BY_EXCHANGE',
|
||||
|
Loading…
x
Reference in New Issue
Block a user