@0x/contracts-staking
: Updating tests and making the contracts testable.
This commit is contained in:
parent
3ad7728a0e
commit
6a29654d7d
@ -192,24 +192,23 @@ contract MixinExchangeFees is
|
|||||||
private
|
private
|
||||||
view
|
view
|
||||||
{
|
{
|
||||||
if (protocolFeePaid != 0) {
|
if (protocolFeePaid == 0) {
|
||||||
return;
|
LibRichErrors.rrevert(
|
||||||
|
LibStakingRichErrors.InvalidProtocolFeePaymentError(
|
||||||
|
LibStakingRichErrors.ProtocolFeePaymentErrorCodes.ZeroProtocolFeePaid,
|
||||||
|
protocolFeePaid,
|
||||||
|
msg.value
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (msg.value == protocolFeePaid || msg.value == 0) {
|
if (msg.value != protocolFeePaid && msg.value != 0) {
|
||||||
return;
|
LibRichErrors.rrevert(
|
||||||
|
LibStakingRichErrors.InvalidProtocolFeePaymentError(
|
||||||
|
LibStakingRichErrors.ProtocolFeePaymentErrorCodes.MismatchedFeeAndPayment,
|
||||||
|
protocolFeePaid,
|
||||||
|
msg.value
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
LibRichErrors.rrevert(
|
|
||||||
LibStakingRichErrors.InvalidProtocolFeePaymentError(
|
|
||||||
protocolFeePaid == 0 ?
|
|
||||||
LibStakingRichErrors
|
|
||||||
.ProtocolFeePaymentErrorCodes
|
|
||||||
.ZeroProtocolFeePaid :
|
|
||||||
LibStakingRichErrors
|
|
||||||
.ProtocolFeePaymentErrorCodes
|
|
||||||
.MismatchedFeeAndPayment,
|
|
||||||
protocolFeePaid,
|
|
||||||
msg.value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,20 +26,6 @@ import "../interfaces/IStructs.sol";
|
|||||||
/// cyclical dependencies.
|
/// cyclical dependencies.
|
||||||
contract MixinAbstract {
|
contract MixinAbstract {
|
||||||
|
|
||||||
/// @dev Computes the reward owed to a pool during finalization.
|
|
||||||
/// Does nothing if the pool is already finalized.
|
|
||||||
/// @param poolId The pool's ID.
|
|
||||||
/// @return totalReward The total reward owed to a pool.
|
|
||||||
/// @return membersStake The total stake for all non-operator members in
|
|
||||||
/// this pool.
|
|
||||||
function _getUnfinalizedPoolRewards(bytes32 poolId)
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256 totalReward,
|
|
||||||
uint256 membersStake
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @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
|
||||||
/// and eth vault. This can be called by internal functions that need
|
/// and eth vault. This can be called by internal functions that need
|
||||||
@ -57,4 +43,18 @@ contract MixinAbstract {
|
|||||||
uint256 membersReward,
|
uint256 membersReward,
|
||||||
uint256 membersStake
|
uint256 membersStake
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// @dev Computes the reward owed to a pool during finalization.
|
||||||
|
/// Does nothing if the pool is already finalized.
|
||||||
|
/// @param poolId The pool's ID.
|
||||||
|
/// @return totalReward The total reward owed to a pool.
|
||||||
|
/// @return membersStake The total stake for all non-operator members in
|
||||||
|
/// this pool.
|
||||||
|
function _getUnfinalizedPoolRewards(bytes32 poolId)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
uint256 totalReward,
|
||||||
|
uint256 membersStake
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ contract MixinFinalizer is
|
|||||||
IEtherToken weth = IEtherToken(_getWETHAddress());
|
IEtherToken weth = IEtherToken(_getWETHAddress());
|
||||||
uint256 ethBalance = address(this).balance;
|
uint256 ethBalance = address(this).balance;
|
||||||
if (ethBalance != 0) {
|
if (ethBalance != 0) {
|
||||||
weth.deposit.value((address(this).balance));
|
weth.deposit.value((address(this).balance))();
|
||||||
}
|
}
|
||||||
balance = weth.balanceOf(address(this));
|
balance = weth.balanceOf(address(this));
|
||||||
return balance;
|
return balance;
|
||||||
|
@ -235,7 +235,7 @@ contract MixinParams is
|
|||||||
address[2] memory oldSpenders,
|
address[2] memory oldSpenders,
|
||||||
address[2] memory newSpenders
|
address[2] memory newSpenders
|
||||||
)
|
)
|
||||||
private
|
internal
|
||||||
{
|
{
|
||||||
IEtherToken weth = IEtherToken(_getWETHAddress());
|
IEtherToken weth = IEtherToken(_getWETHAddress());
|
||||||
// Grant new allowances.
|
// Grant new allowances.
|
||||||
|
@ -21,7 +21,6 @@ pragma solidity ^0.5.9;
|
|||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "../interfaces/IEthVault.sol";
|
import "../interfaces/IEthVault.sol";
|
||||||
import "../immutable/MixinDeploymentConstants.sol";
|
|
||||||
import "./MixinVaultCore.sol";
|
import "./MixinVaultCore.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -29,15 +28,21 @@ import "./MixinVaultCore.sol";
|
|||||||
contract EthVault is
|
contract EthVault is
|
||||||
IEthVault,
|
IEthVault,
|
||||||
IVaultCore,
|
IVaultCore,
|
||||||
MixinDeploymentConstants,
|
|
||||||
Ownable,
|
Ownable,
|
||||||
MixinVaultCore
|
MixinVaultCore
|
||||||
{
|
{
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
// Address of the WETH contract.
|
||||||
|
IEtherToken public weth;
|
||||||
// mapping from Owner to WETH balance
|
// mapping from Owner to WETH balance
|
||||||
mapping (address => uint256) internal _balances;
|
mapping (address => uint256) internal _balances;
|
||||||
|
|
||||||
|
/// @param wethAddress Address of the WETH contract.
|
||||||
|
constructor(address wethAddress) public {
|
||||||
|
weth = IEtherToken(wethAddress);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Deposit an `amount` of WETH for `owner` into the vault.
|
/// @dev Deposit an `amount` of WETH for `owner` into the vault.
|
||||||
/// The staking contract should have granted the vault an allowance
|
/// The staking contract should have granted the vault an allowance
|
||||||
/// because it will pull the WETH via `transferFrom()`.
|
/// because it will pull the WETH via `transferFrom()`.
|
||||||
@ -49,7 +54,7 @@ contract EthVault is
|
|||||||
onlyStakingProxy
|
onlyStakingProxy
|
||||||
{
|
{
|
||||||
// Transfer WETH from the staking contract into this contract.
|
// Transfer WETH from the staking contract into this contract.
|
||||||
IEtherToken(_getWETHAddress()).transferFrom(msg.sender, address(this), amount);
|
weth.transferFrom(msg.sender, address(this), amount);
|
||||||
// Credit the owner.
|
// Credit the owner.
|
||||||
_balances[owner] = _balances[owner].safeAdd(amount);
|
_balances[owner] = _balances[owner].safeAdd(amount);
|
||||||
emit EthDepositedIntoVault(msg.sender, owner, amount);
|
emit EthDepositedIntoVault(msg.sender, owner, amount);
|
||||||
@ -97,7 +102,7 @@ contract EthVault is
|
|||||||
_balances[owner] = _balances[owner].safeSub(amount);
|
_balances[owner] = _balances[owner].safeSub(amount);
|
||||||
|
|
||||||
// withdraw WETH to owner
|
// withdraw WETH to owner
|
||||||
IEtherToken(_getWETHAddress()).transfer(msg.sender, amount);
|
weth.transfer(msg.sender, amount);
|
||||||
|
|
||||||
// notify
|
// notify
|
||||||
emit EthWithdrawnFromVault(msg.sender, owner, amount);
|
emit EthWithdrawnFromVault(msg.sender, owner, amount);
|
||||||
|
@ -26,22 +26,27 @@ 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 "../immutable/MixinDeploymentConstants.sol";
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev This vault manages staking pool rewards.
|
/// @dev This vault manages staking pool rewards.
|
||||||
contract StakingPoolRewardVault is
|
contract StakingPoolRewardVault is
|
||||||
IStakingPoolRewardVault,
|
IStakingPoolRewardVault,
|
||||||
IVaultCore,
|
IVaultCore,
|
||||||
MixinDeploymentConstants,
|
|
||||||
Ownable,
|
Ownable,
|
||||||
MixinVaultCore
|
MixinVaultCore
|
||||||
{
|
{
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
// Address of the WETH contract.
|
||||||
|
IEtherToken public weth;
|
||||||
// mapping from poolId to Pool metadata
|
// mapping from poolId to Pool metadata
|
||||||
mapping (bytes32 => uint256) internal _balanceByPoolId;
|
mapping (bytes32 => uint256) internal _balanceByPoolId;
|
||||||
|
|
||||||
|
/// @param wethAddress Address of the WETH contract.
|
||||||
|
constructor(address wethAddress) public {
|
||||||
|
weth = IEtherToken(wethAddress);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Deposit an amount of WETH for `poolId` into the vault.
|
/// @dev Deposit an amount of WETH for `poolId` into the vault.
|
||||||
/// The staking contract should have granted the vault an allowance
|
/// The staking contract should have granted the vault an allowance
|
||||||
/// because it will pull the WETH via `transferFrom()`.
|
/// because it will pull the WETH via `transferFrom()`.
|
||||||
@ -53,7 +58,7 @@ contract StakingPoolRewardVault is
|
|||||||
onlyStakingProxy
|
onlyStakingProxy
|
||||||
{
|
{
|
||||||
// Transfer WETH from the staking contract into this contract.
|
// Transfer WETH from the staking contract into this contract.
|
||||||
IEtherToken(_getWETHAddress()).transferFrom(msg.sender, address(this), amount);
|
weth.transferFrom(msg.sender, address(this), amount);
|
||||||
// Credit the pool.
|
// Credit the pool.
|
||||||
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeAdd(amount);
|
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeAdd(amount);
|
||||||
emit EthDepositedIntoVault(msg.sender, poolId, amount);
|
emit EthDepositedIntoVault(msg.sender, poolId, amount);
|
||||||
@ -73,7 +78,7 @@ contract StakingPoolRewardVault is
|
|||||||
onlyStakingProxy
|
onlyStakingProxy
|
||||||
{
|
{
|
||||||
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeSub(amount);
|
_balanceByPoolId[poolId] = _balanceByPoolId[poolId].safeSub(amount);
|
||||||
IEtherToken(_getWETHAddress()).transfer(to, amount);
|
weth.transfer(to, amount);
|
||||||
emit PoolRewardTransferred(
|
emit PoolRewardTransferred(
|
||||||
poolId,
|
poolId,
|
||||||
to,
|
to,
|
||||||
|
@ -21,6 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "./TestStaking.sol";
|
import "./TestStaking.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
contract TestCumulativeRewardTracking is
|
contract TestCumulativeRewardTracking is
|
||||||
TestStaking
|
TestStaking
|
||||||
{
|
{
|
||||||
@ -39,7 +40,8 @@ contract TestCumulativeRewardTracking is
|
|||||||
uint256 epoch
|
uint256 epoch
|
||||||
);
|
);
|
||||||
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
constructor(address wethAddress) public TestStaking(wethAddress) {}
|
||||||
|
|
||||||
function init(address, address, address payable, address) public {}
|
function init(address, address, address payable, address) public {}
|
||||||
|
|
||||||
function _forceSetCumulativeReward(
|
function _forceSetCumulativeReward(
|
||||||
|
54
contracts/staking/contracts/test/TestMixinParams.sol
Normal file
54
contracts/staking/contracts/test/TestMixinParams.sol
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "../src/interfaces/IEthVault.sol";
|
||||||
|
import "../src/interfaces/IStakingPoolRewardVault.sol";
|
||||||
|
import "../src/sys/MixinParams.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
contract TestMixinParams is
|
||||||
|
MixinParams
|
||||||
|
{
|
||||||
|
|
||||||
|
event WETHApprove(address spender, uint256 amount);
|
||||||
|
|
||||||
|
/// @dev Sets the eth and reward vault addresses.
|
||||||
|
function setVaultAddresses(
|
||||||
|
address ethVaultAddress,
|
||||||
|
address rewardVaultAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
ethVault = IEthVault(ethVaultAddress);
|
||||||
|
rewardVault = IStakingPoolRewardVault(rewardVaultAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev WETH `approve()` function that just logs events.
|
||||||
|
function approve(address spender, uint256 amount) external returns (bool) {
|
||||||
|
emit WETHApprove(spender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overridden return this contract's address.
|
||||||
|
function _getWETHAddress() internal view returns (address) {
|
||||||
|
return address(this);
|
||||||
|
}
|
||||||
|
}
|
@ -21,11 +21,11 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
|
||||||
import "../src/interfaces/IStructs.sol";
|
import "../src/interfaces/IStructs.sol";
|
||||||
import "../src/Staking.sol";
|
import "./TestStakingNoWETH.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestProtocolFees is
|
contract TestProtocolFees is
|
||||||
Staking
|
TestStakingNoWETH
|
||||||
{
|
{
|
||||||
struct TestPool {
|
struct TestPool {
|
||||||
uint256 operatorStake;
|
uint256 operatorStake;
|
||||||
@ -33,13 +33,22 @@ contract TestProtocolFees is
|
|||||||
mapping(address => bool) isMaker;
|
mapping(address => bool) isMaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event ERC20ProxyTransferFrom(
|
||||||
|
bytes assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
mapping(bytes32 => TestPool) private _testPools;
|
mapping(bytes32 => TestPool) private _testPools;
|
||||||
mapping(address => bytes32) private _makersToTestPoolIds;
|
mapping(address => bytes32) private _makersToTestPoolIds;
|
||||||
|
|
||||||
constructor(address exchangeAddress, address wethProxyAddress) public {
|
constructor(address exchangeAddress) public {
|
||||||
init(
|
init(
|
||||||
wethProxyAddress,
|
// Use this contract as the ERC20Proxy.
|
||||||
address(1), // vault addresses must be non-zero
|
address(this),
|
||||||
|
// vault addresses must be non-zero
|
||||||
|
address(1),
|
||||||
address(1),
|
address(1),
|
||||||
address(1)
|
address(1)
|
||||||
);
|
);
|
||||||
@ -81,6 +90,18 @@ contract TestProtocolFees is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev The ERC20Proxy `transferFrom()` function.
|
||||||
|
function transferFrom(
|
||||||
|
bytes calldata assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit ERC20ProxyTransferFrom(assetData, from, to, amount);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Overridden to use test pools.
|
/// @dev Overridden to use test pools.
|
||||||
function getStakingPoolIdOfMaker(address makerAddress)
|
function getStakingPoolIdOfMaker(address makerAddress)
|
||||||
public
|
public
|
||||||
|
@ -25,23 +25,17 @@ import "../src/Staking.sol";
|
|||||||
contract TestStaking is
|
contract TestStaking is
|
||||||
Staking
|
Staking
|
||||||
{
|
{
|
||||||
address internal _wethAddress;
|
address public testWethAddress;
|
||||||
|
|
||||||
constructor(address wethAddress) public {
|
constructor(address wethAddress) public {
|
||||||
_wethAddress = wethAddress;
|
testWethAddress = wethAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Overridden to avoid hard-coded WETH.
|
/// @dev Overridden to use testWethAddress;
|
||||||
function getTotalBalance()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256 totalBalance)
|
|
||||||
{
|
|
||||||
totalBalance = address(this).balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Overridden to use _wethAddress;
|
|
||||||
function _getWETHAddress() internal view returns (address) {
|
function _getWETHAddress() internal view returns (address) {
|
||||||
return _wethAddress;
|
// `testWethAddress` will not be set on the proxy this contract is
|
||||||
|
// attached to, so we need to access the storage of the deployed
|
||||||
|
// instance of this contract.
|
||||||
|
return TestStaking(address(uint160(stakingContract))).testWethAddress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,28 +17,28 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/ERC20Proxy.sol";
|
import "../src/Staking.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestProtocolFeesERC20Proxy is
|
// solhint-disable no-empty-blocks
|
||||||
ERC20Proxy
|
/// @dev A version of the staking contract with WETH-related functions
|
||||||
|
/// overridden to do nothing.
|
||||||
|
contract TestStakingNoWETH is
|
||||||
|
Staking
|
||||||
{
|
{
|
||||||
event TransferFromCalled(
|
function _transferWETHAllownces(
|
||||||
bytes assetData,
|
address[2] memory oldSpenders,
|
||||||
address from,
|
address[2] memory newSpenders
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
function transferFrom(
|
|
||||||
bytes calldata assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
)
|
||||||
external
|
internal
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _wrapBalanceToWETHAndGetBalance()
|
||||||
|
internal
|
||||||
|
returns (uint256 balance)
|
||||||
{
|
{
|
||||||
emit TransferFromCalled(assetData, from, to, amount);
|
return address(this).balance;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,8 +2,6 @@ import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
|||||||
import { blockchainTests, describe } from '@0x/contracts-test-utils';
|
import { blockchainTests, describe } from '@0x/contracts-test-utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts } from '../src';
|
|
||||||
|
|
||||||
import { deployAndConfigureContractsAsync, StakingApiWrapper } from './utils/api_wrapper';
|
import { deployAndConfigureContractsAsync, StakingApiWrapper } from './utils/api_wrapper';
|
||||||
import { CumulativeRewardTrackingSimulation, TestAction } from './utils/cumulative_reward_tracking_simulation';
|
import { CumulativeRewardTrackingSimulation, TestAction } from './utils/cumulative_reward_tracking_simulation';
|
||||||
|
|
||||||
|
@ -1,20 +1,28 @@
|
|||||||
import { blockchainTests, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
|
import { blockchainTests, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
|
||||||
import { AuthorizableRevertErrors, BigNumber } from '@0x/utils';
|
import { AuthorizableRevertErrors, BigNumber } from '@0x/utils';
|
||||||
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, IStakingEventsParamsSetEventArgs, MixinParamsContract } from '../src/';
|
import {
|
||||||
|
artifacts,
|
||||||
|
IStakingEventsParamsSetEventArgs,
|
||||||
|
TestMixinParamsContract,
|
||||||
|
TestMixinParamsEvents,
|
||||||
|
TestMixinParamsWETHApproveEventArgs,
|
||||||
|
} from '../src/';
|
||||||
|
|
||||||
import { constants as stakingConstants } from './utils/constants';
|
import { constants as stakingConstants } from './utils/constants';
|
||||||
import { StakingParams } from './utils/types';
|
import { StakingParams } from './utils/types';
|
||||||
|
|
||||||
blockchainTests('Configurable Parameters unit tests', env => {
|
blockchainTests('Configurable Parameters unit tests', env => {
|
||||||
let testContract: MixinParamsContract;
|
let testContract: TestMixinParamsContract;
|
||||||
let authorizedAddress: string;
|
let authorizedAddress: string;
|
||||||
let notAuthorizedAddress: string;
|
let notAuthorizedAddress: string;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[authorizedAddress, notAuthorizedAddress] = await env.getAccountAddressesAsync();
|
[authorizedAddress, notAuthorizedAddress] = await env.getAccountAddressesAsync();
|
||||||
testContract = await MixinParamsContract.deployFrom0xArtifactAsync(
|
testContract = await TestMixinParamsContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.MixinParams,
|
artifacts.TestMixinParams,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
@ -22,7 +30,7 @@ blockchainTests('Configurable Parameters unit tests', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
blockchainTests.resets('setParams()', () => {
|
blockchainTests.resets('setParams()', () => {
|
||||||
async function setParamsAndAssertAsync(params: Partial<StakingParams>, from?: string): Promise<void> {
|
async function setParamsAndAssertAsync(params: Partial<StakingParams>, from?: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
const _params = {
|
const _params = {
|
||||||
...stakingConstants.DEFAULT_PARAMS,
|
...stakingConstants.DEFAULT_PARAMS,
|
||||||
...params,
|
...params,
|
||||||
@ -41,8 +49,9 @@ blockchainTests('Configurable Parameters unit tests', env => {
|
|||||||
{ from },
|
{ from },
|
||||||
);
|
);
|
||||||
// Assert event.
|
// Assert event.
|
||||||
expect(receipt.logs.length).to.eq(1);
|
const events = filterLogsToArguments<IStakingEventsParamsSetEventArgs>(receipt.logs, 'ParamsSet');
|
||||||
const event = filterLogsToArguments<IStakingEventsParamsSetEventArgs>(receipt.logs, 'ParamsSet')[0];
|
expect(events.length).to.eq(1);
|
||||||
|
const event = events[0];
|
||||||
expect(event.epochDurationInSeconds).to.bignumber.eq(_params.epochDurationInSeconds);
|
expect(event.epochDurationInSeconds).to.bignumber.eq(_params.epochDurationInSeconds);
|
||||||
expect(event.rewardDelegatedStakeWeight).to.bignumber.eq(_params.rewardDelegatedStakeWeight);
|
expect(event.rewardDelegatedStakeWeight).to.bignumber.eq(_params.rewardDelegatedStakeWeight);
|
||||||
expect(event.minimumPoolStake).to.bignumber.eq(_params.minimumPoolStake);
|
expect(event.minimumPoolStake).to.bignumber.eq(_params.minimumPoolStake);
|
||||||
@ -65,6 +74,7 @@ blockchainTests('Configurable Parameters unit tests', env => {
|
|||||||
expect(actual[7]).to.eq(_params.ethVaultAddress);
|
expect(actual[7]).to.eq(_params.ethVaultAddress);
|
||||||
expect(actual[8]).to.eq(_params.rewardVaultAddress);
|
expect(actual[8]).to.eq(_params.rewardVaultAddress);
|
||||||
expect(actual[9]).to.eq(_params.zrxVaultAddress);
|
expect(actual[9]).to.eq(_params.zrxVaultAddress);
|
||||||
|
return receipt;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('throws if not called by an authorized address', async () => {
|
it('throws if not called by an authorized address', async () => {
|
||||||
|
@ -17,8 +17,8 @@ import {
|
|||||||
IStakingEventsEvents,
|
IStakingEventsEvents,
|
||||||
IStakingEventsStakingPoolActivatedEventArgs,
|
IStakingEventsStakingPoolActivatedEventArgs,
|
||||||
TestProtocolFeesContract,
|
TestProtocolFeesContract,
|
||||||
TestProtocolFeesERC20ProxyContract,
|
TestProtocolFeesERC20ProxyTransferFromEventArgs,
|
||||||
TestProtocolFeesERC20ProxyTransferFromCalledEventArgs,
|
TestProtocolFeesEvents,
|
||||||
} from '../src';
|
} from '../src';
|
||||||
|
|
||||||
import { getRandomInteger } from './utils/number_utils';
|
import { getRandomInteger } from './utils/number_utils';
|
||||||
@ -34,14 +34,6 @@ blockchainTests('Protocol Fee Unit Tests', env => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
[ownerAddress, exchangeAddress, notExchangeAddress] = await env.web3Wrapper.getAvailableAddressesAsync();
|
[ownerAddress, exchangeAddress, notExchangeAddress] = await env.web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
|
||||||
// Deploy the erc20Proxy for testing.
|
|
||||||
const proxy = await TestProtocolFeesERC20ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestProtocolFeesERC20Proxy,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deploy the protocol fees contract.
|
// Deploy the protocol fees contract.
|
||||||
testContract = await TestProtocolFeesContract.deployFrom0xArtifactAsync(
|
testContract = await TestProtocolFeesContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestProtocolFees,
|
artifacts.TestProtocolFees,
|
||||||
@ -52,7 +44,6 @@ blockchainTests('Protocol Fee Unit Tests', env => {
|
|||||||
},
|
},
|
||||||
artifacts,
|
artifacts,
|
||||||
exchangeAddress,
|
exchangeAddress,
|
||||||
proxy.address,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
wethAssetData = await testContract.getWethAssetData.callAsync();
|
wethAssetData = await testContract.getWethAssetData.callAsync();
|
||||||
@ -168,9 +159,9 @@ blockchainTests('Protocol Fee Unit Tests', env => {
|
|||||||
|
|
||||||
describe('ETH fees', () => {
|
describe('ETH fees', () => {
|
||||||
function assertNoWETHTransferLogs(logs: LogEntry[]): void {
|
function assertNoWETHTransferLogs(logs: LogEntry[]): void {
|
||||||
const logsArgs = filterLogsToArguments<TestProtocolFeesERC20ProxyTransferFromCalledEventArgs>(
|
const logsArgs = filterLogsToArguments<TestProtocolFeesERC20ProxyTransferFromEventArgs>(
|
||||||
logs,
|
logs,
|
||||||
'TransferFromCalled',
|
TestProtocolFeesEvents.ERC20ProxyTransferFrom,
|
||||||
);
|
);
|
||||||
expect(logsArgs).to.deep.eq([]);
|
expect(logsArgs).to.deep.eq([]);
|
||||||
}
|
}
|
||||||
@ -233,9 +224,9 @@ blockchainTests('Protocol Fee Unit Tests', env => {
|
|||||||
|
|
||||||
describe('WETH fees', () => {
|
describe('WETH fees', () => {
|
||||||
function assertWETHTransferLogs(logs: LogEntry[], fromAddress: string, amount: BigNumber): void {
|
function assertWETHTransferLogs(logs: LogEntry[], fromAddress: string, amount: BigNumber): void {
|
||||||
const logsArgs = filterLogsToArguments<TestProtocolFeesERC20ProxyTransferFromCalledEventArgs>(
|
const logsArgs = filterLogsToArguments<TestProtocolFeesERC20ProxyTransferFromEventArgs>(
|
||||||
logs,
|
logs,
|
||||||
'TransferFromCalled',
|
TestProtocolFeesEvents.ERC20ProxyTransferFrom,
|
||||||
);
|
);
|
||||||
expect(logsArgs.length).to.eq(1);
|
expect(logsArgs.length).to.eq(1);
|
||||||
for (const args of logsArgs) {
|
for (const args of logsArgs) {
|
||||||
|
@ -91,36 +91,35 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface FinalizationState {
|
interface FinalizationState {
|
||||||
balance: Numberish;
|
rewardsAvailable: Numberish;
|
||||||
currentEpoch: number;
|
poolsRemaining: number;
|
||||||
closingEpoch: number;
|
totalFeesCollected: Numberish;
|
||||||
numActivePoolsThisEpoch: number;
|
totalWeightedStake: Numberish;
|
||||||
totalFeesCollectedThisEpoch: Numberish;
|
totalRewardsFinalized: Numberish;
|
||||||
totalWeightedStakeThisEpoch: Numberish;
|
|
||||||
unfinalizedPoolsRemaining: number;
|
|
||||||
unfinalizedRewardsAvailable: Numberish;
|
|
||||||
unfinalizedTotalFeesCollected: Numberish;
|
|
||||||
unfinalizedTotalWeightedStake: Numberish;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFinalizationStateAsync(): Promise<FinalizationState> {
|
async function getUnfinalizedStateAsync(): Promise<FinalizationState> {
|
||||||
const r = await testContract.getFinalizationState.callAsync();
|
const r = await testContract.unfinalizedState.callAsync();
|
||||||
return {
|
return {
|
||||||
balance: r[0],
|
rewardsAvailable: r[0],
|
||||||
currentEpoch: r[1].toNumber(),
|
poolsRemaining: r[1].toNumber(),
|
||||||
closingEpoch: r[2].toNumber(),
|
totalFeesCollected: r[2],
|
||||||
numActivePoolsThisEpoch: r[3].toNumber(),
|
totalWeightedStake: r[3],
|
||||||
totalFeesCollectedThisEpoch: r[4],
|
totalRewardsFinalized: r[4],
|
||||||
totalWeightedStakeThisEpoch: r[5],
|
|
||||||
unfinalizedPoolsRemaining: r[6].toNumber(),
|
|
||||||
unfinalizedRewardsAvailable: r[7],
|
|
||||||
unfinalizedTotalFeesCollected: r[8],
|
|
||||||
unfinalizedTotalWeightedStake: r[9],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function finalizePoolsAsync(poolIds: string[]): Promise<LogEntry[]> {
|
||||||
|
const logs = [] as LogEntry[];
|
||||||
|
for (const poolId of poolIds) {
|
||||||
|
const receipt = await testContract.finalizePool.awaitTransactionSuccessAsync(poolId);
|
||||||
|
logs.splice(logs.length - 1, 0, ...receipt.logs);
|
||||||
|
}
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
|
||||||
async function assertFinalizationStateAsync(expected: Partial<FinalizationState>): Promise<void> {
|
async function assertFinalizationStateAsync(expected: Partial<FinalizationState>): Promise<void> {
|
||||||
const actual = await getFinalizationStateAsync();
|
const actual = await getUnfinalizedStateAsync();
|
||||||
assertEqualNumberFields(actual, expected);
|
assertEqualNumberFields(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +247,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
if (new BigNumber(pool.membersStake).isZero()) {
|
if (new BigNumber(pool.membersStake).isZero()) {
|
||||||
return [new BigNumber(totalReward), ZERO_AMOUNT];
|
return [new BigNumber(totalReward), ZERO_AMOUNT];
|
||||||
}
|
}
|
||||||
const operatorShare = new BigNumber(totalReward).times(pool.operatorShare).integerValue(BigNumber.ROUND_DOWN);
|
const operatorShare = new BigNumber(totalReward).times(pool.operatorShare).integerValue(BigNumber.ROUND_UP);
|
||||||
const membersShare = new BigNumber(totalReward).minus(operatorShare);
|
const membersShare = new BigNumber(totalReward).minus(operatorShare);
|
||||||
return [operatorShare, membersShare];
|
return [operatorShare, membersShare];
|
||||||
}
|
}
|
||||||
@ -337,12 +336,12 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
// Add a pool so there is state to clear.
|
// Add a pool so there is state to clear.
|
||||||
await addActivePoolAsync();
|
await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
|
const epoch = await testContract.currentEpoch.callAsync();
|
||||||
|
expect(epoch).to.bignumber.eq(INITIAL_EPOCH + 1);
|
||||||
return assertFinalizationStateAsync({
|
return assertFinalizationStateAsync({
|
||||||
currentEpoch: INITIAL_EPOCH + 1,
|
poolsRemaining: 0,
|
||||||
closingEpoch: INITIAL_EPOCH,
|
totalFeesCollected: 0,
|
||||||
numActivePoolsThisEpoch: 0,
|
totalWeightedStake: 0,
|
||||||
totalFeesCollectedThisEpoch: 0,
|
|
||||||
totalWeightedStakeThisEpoch: 0,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -351,10 +350,10 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const pool = await addActivePoolAsync();
|
const pool = await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
return assertFinalizationStateAsync({
|
return assertFinalizationStateAsync({
|
||||||
unfinalizedPoolsRemaining: 1,
|
poolsRemaining: 1,
|
||||||
unfinalizedRewardsAvailable: INITIAL_BALANCE,
|
rewardsAvailable: INITIAL_BALANCE,
|
||||||
unfinalizedTotalFeesCollected: pool.feesCollected,
|
totalFeesCollected: pool.feesCollected,
|
||||||
unfinalizedTotalWeightedStake: pool.weightedStake,
|
totalWeightedStake: pool.weightedStake,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -367,181 +366,35 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('finalizePools()', () => {
|
|
||||||
it('does nothing if there were no active pools', async () => {
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const poolId = hexRandom();
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync([poolId]);
|
|
||||||
expect(receipt.logs).to.deep.eq([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does nothing if no pools are passed in', async () => {
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync([]);
|
|
||||||
expect(receipt.logs).to.deep.eq([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can finalize a single pool', async () => {
|
|
||||||
const pool = await addActivePoolAsync();
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
|
||||||
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, [pool], receipt.logs);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can finalize multiple pools', async () => {
|
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
|
||||||
const poolIds = pools.map(p => p.poolId);
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, pools, receipt.logs);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can finalize multiple pools over multiple transactions', async () => {
|
|
||||||
const pools = await Promise.all(_.times(2, async () => addActivePoolAsync()));
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipts = await Promise.all(
|
|
||||||
pools.map(pool => testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId])),
|
|
||||||
);
|
|
||||||
const allLogs = _.flatten(receipts.map(r => r.logs));
|
|
||||||
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, pools, allLogs);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can finalize with no rewards', async () => {
|
|
||||||
await testContract.drainBalance.awaitTransactionSuccessAsync();
|
|
||||||
const pools = await Promise.all(_.times(2, async () => addActivePoolAsync()));
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipts = await Promise.all(
|
|
||||||
pools.map(pool => testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId])),
|
|
||||||
);
|
|
||||||
const allLogs = _.flatten(receipts.map(r => r.logs));
|
|
||||||
return assertFinalizationLogsAndBalancesAsync(0, pools, allLogs);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ignores a non-active pool', async () => {
|
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
|
||||||
const nonActivePoolId = hexRandom();
|
|
||||||
const poolIds = _.shuffle([...pools.map(p => p.poolId), nonActivePoolId]);
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
const rewardsPaidEvents = getRewardsPaidEvents(receipt.logs);
|
|
||||||
expect(rewardsPaidEvents.length).to.eq(pools.length);
|
|
||||||
for (const event of rewardsPaidEvents) {
|
|
||||||
expect(event.poolId).to.not.eq(nonActivePoolId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ignores a finalized pool', async () => {
|
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
|
||||||
const poolIds = pools.map(p => p.poolId);
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const [finalizedPool] = _.sampleSize(pools, 1);
|
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([finalizedPool.poolId]);
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
const rewardsPaidEvents = getRewardsPaidEvents(receipt.logs);
|
|
||||||
expect(rewardsPaidEvents.length).to.eq(pools.length - 1);
|
|
||||||
for (const event of rewardsPaidEvents) {
|
|
||||||
expect(event.poolId).to.not.eq(finalizedPool.poolId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('resets pool state after finalizing it', async () => {
|
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
|
||||||
const pool = _.sample(pools) as ActivePoolOpts;
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
|
||||||
const poolState = await testContract.getActivePoolFromEpoch.callAsync(
|
|
||||||
new BigNumber(INITIAL_EPOCH),
|
|
||||||
pool.poolId,
|
|
||||||
);
|
|
||||||
expect(poolState.feesCollected).to.bignumber.eq(0);
|
|
||||||
expect(poolState.weightedStake).to.bignumber.eq(0);
|
|
||||||
expect(poolState.membersStake).to.bignumber.eq(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('`rewardsPaid` is the sum of all pool rewards', async () => {
|
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
|
||||||
const poolIds = pools.map(p => p.poolId);
|
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
const rewardsPaidEvents = getRewardsPaidEvents(receipt.logs);
|
|
||||||
const expectedTotalRewardsPaid = BigNumber.sum(
|
|
||||||
...rewardsPaidEvents.map(e => e.membersReward.plus(e.operatorReward)),
|
|
||||||
);
|
|
||||||
const { rewardsPaid: totalRewardsPaid } = getEpochFinalizedEvents(receipt.logs)[0];
|
|
||||||
expect(totalRewardsPaid).to.bignumber.eq(expectedTotalRewardsPaid);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('`rewardsPaid` <= `rewardsAvailable` <= contract balance at the end of the epoch', async () => {
|
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
|
||||||
const poolIds = pools.map(p => p.poolId);
|
|
||||||
let receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
|
||||||
expect(rewardsAvailable).to.bignumber.lte(INITIAL_BALANCE);
|
|
||||||
receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
const { rewardsPaid } = getEpochFinalizedEvents(receipt.logs)[0];
|
|
||||||
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('`rewardsPaid` <= `rewardsAvailable` with two equal pools', async () => {
|
|
||||||
const pool1 = await addActivePoolAsync();
|
|
||||||
const pool2 = await addActivePoolAsync(_.omit(pool1, 'poolId'));
|
|
||||||
const poolIds = [pool1, pool2].map(p => p.poolId);
|
|
||||||
let receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
|
||||||
receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
const { rewardsPaid } = getEpochFinalizedEvents(receipt.logs)[0];
|
|
||||||
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
|
||||||
});
|
|
||||||
|
|
||||||
blockchainTests.optional('`rewardsPaid` fuzzing', async () => {
|
|
||||||
const numTests = 32;
|
|
||||||
for (const i of _.times(numTests)) {
|
|
||||||
const numPools = _.random(1, 32);
|
|
||||||
it(`${i + 1}/${numTests} \`rewardsPaid\` <= \`rewardsAvailable\` (${numPools} pools)`, async () => {
|
|
||||||
const pools = await Promise.all(_.times(numPools, async () => addActivePoolAsync()));
|
|
||||||
const poolIds = pools.map(p => p.poolId);
|
|
||||||
let receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
|
||||||
receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
|
||||||
const { rewardsPaid } = getEpochFinalizedEvents(receipt.logs)[0];
|
|
||||||
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_finalizePool()', () => {
|
describe('_finalizePool()', () => {
|
||||||
it('does nothing if there were no active pools', async () => {
|
it('does nothing if there were no active pools', async () => {
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const poolId = hexRandom();
|
const poolId = hexRandom();
|
||||||
const receipt = await testContract.finalizePool.awaitTransactionSuccessAsync(poolId);
|
const logs = await finalizePoolsAsync([poolId]);
|
||||||
expect(receipt.logs).to.deep.eq([]);
|
expect(logs).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can finalize a pool', async () => {
|
it('can finalize a pool', async () => {
|
||||||
const pool = await addActivePoolAsync();
|
const pool = await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const receipt = await testContract.finalizePool.awaitTransactionSuccessAsync(pool.poolId);
|
const logs = await finalizePoolsAsync([pool.poolId]);
|
||||||
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, [pool], receipt.logs);
|
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, [pool], logs);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can finalize multiple pools over multiple transactions', async () => {
|
it('can finalize multiple pools over multiple transactions', async () => {
|
||||||
const pools = await Promise.all(_.times(2, async () => addActivePoolAsync()));
|
const pools = await Promise.all(_.times(2, async () => addActivePoolAsync()));
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const receipts = await Promise.all(
|
const logs = await finalizePoolsAsync(pools.map(p => p.poolId));
|
||||||
pools.map(pool => testContract.finalizePool.awaitTransactionSuccessAsync(pool.poolId)),
|
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, pools, logs);
|
||||||
);
|
|
||||||
const allLogs = _.flatten(receipts.map(r => r.logs));
|
|
||||||
return assertFinalizationLogsAndBalancesAsync(INITIAL_BALANCE, pools, allLogs);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores a finalized pool', async () => {
|
it('ignores a finalized pool', async () => {
|
||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const [finalizedPool] = _.sampleSize(pools, 1);
|
const [finalizedPool] = _.sampleSize(pools, 1);
|
||||||
await testContract.finalizePool.awaitTransactionSuccessAsync(finalizedPool.poolId);
|
await finalizePoolsAsync([finalizedPool.poolId]);
|
||||||
const receipt = await testContract.finalizePool.awaitTransactionSuccessAsync(finalizedPool.poolId);
|
const logs = await finalizePoolsAsync([finalizedPool.poolId]);
|
||||||
const rewardsPaidEvents = getRewardsPaidEvents(receipt.logs);
|
const rewardsPaidEvents = getRewardsPaidEvents(logs);
|
||||||
expect(rewardsPaidEvents).to.deep.eq([]);
|
expect(rewardsPaidEvents).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -549,7 +402,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||||
const pool = _.sample(pools) as ActivePoolOpts;
|
const pool = _.sample(pools) as ActivePoolOpts;
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await testContract.finalizePool.awaitTransactionSuccessAsync(pool.poolId);
|
await finalizePoolsAsync([pool.poolId]);
|
||||||
const poolState = await testContract.getActivePoolFromEpoch.callAsync(
|
const poolState = await testContract.getActivePoolFromEpoch.callAsync(
|
||||||
new BigNumber(INITIAL_EPOCH),
|
new BigNumber(INITIAL_EPOCH),
|
||||||
pool.poolId,
|
pool.poolId,
|
||||||
@ -564,11 +417,8 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||||
expect(rewardsAvailable).to.bignumber.lte(INITIAL_BALANCE);
|
expect(rewardsAvailable).to.bignumber.lte(INITIAL_BALANCE);
|
||||||
const receipts = await Promise.all(
|
const logs = await finalizePoolsAsync(pools.map(r => r.poolId));
|
||||||
pools.map(p => testContract.finalizePool.awaitTransactionSuccessAsync(p.poolId)),
|
const { rewardsPaid } = getEpochFinalizedEvents(logs)[0];
|
||||||
);
|
|
||||||
const allLogs = _.flatten(receipts.map(r => r.logs));
|
|
||||||
const { rewardsPaid } = getEpochFinalizedEvents(allLogs)[0];
|
|
||||||
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -577,11 +427,8 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const pool2 = await addActivePoolAsync(_.omit(pool1, 'poolId'));
|
const pool2 = await addActivePoolAsync(_.omit(pool1, 'poolId'));
|
||||||
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||||
const receipts = await Promise.all(
|
const logs = await finalizePoolsAsync([pool1, pool2].map(r => r.poolId));
|
||||||
[pool1, pool2].map(p => testContract.finalizePool.awaitTransactionSuccessAsync(p.poolId)),
|
const { rewardsPaid } = getEpochFinalizedEvents(logs)[0];
|
||||||
);
|
|
||||||
const allLogs = _.flatten(receipts.map(r => r.logs));
|
|
||||||
const { rewardsPaid } = getEpochFinalizedEvents(allLogs)[0];
|
|
||||||
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -593,11 +440,8 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const pools = await Promise.all(_.times(numPools, async () => addActivePoolAsync()));
|
const pools = await Promise.all(_.times(numPools, async () => addActivePoolAsync()));
|
||||||
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||||
const receipts = await Promise.all(
|
const logs = await finalizePoolsAsync(pools.map(r => r.poolId));
|
||||||
pools.map(p => testContract.finalizePool.awaitTransactionSuccessAsync(p.poolId)),
|
const { rewardsPaid } = getEpochFinalizedEvents(logs)[0];
|
||||||
);
|
|
||||||
const allLogs = _.flatten(receipts.map(r => r.logs));
|
|
||||||
const { rewardsPaid } = getEpochFinalizedEvents(allLogs)[0];
|
|
||||||
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
expect(rewardsPaid).to.bignumber.lte(rewardsAvailable);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -608,7 +452,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
it('can advance the epoch after the prior epoch is finalized', async () => {
|
it('can advance the epoch after the prior epoch is finalized', async () => {
|
||||||
const pool = await addActivePoolAsync();
|
const pool = await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
await finalizePoolsAsync([pool.poolId]);
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
return expect(getCurrentEpochAsync()).to.become(INITIAL_EPOCH + 2);
|
return expect(getCurrentEpochAsync()).to.become(INITIAL_EPOCH + 2);
|
||||||
});
|
});
|
||||||
@ -616,25 +460,25 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
it('does not reward a pool that was only active 2 epochs ago', async () => {
|
it('does not reward a pool that was only active 2 epochs ago', async () => {
|
||||||
const pool1 = await addActivePoolAsync();
|
const pool1 = await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool1.poolId]);
|
await finalizePoolsAsync([pool1.poolId]);
|
||||||
await addActivePoolAsync();
|
await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
expect(getCurrentEpochAsync()).to.become(INITIAL_EPOCH + 2);
|
expect(getCurrentEpochAsync()).to.become(INITIAL_EPOCH + 2);
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync([pool1.poolId]);
|
const logs = await finalizePoolsAsync([pool1.poolId]);
|
||||||
const rewardsPaidEvents = getRewardsPaidEvents(receipt.logs);
|
const rewardsPaidEvents = getRewardsPaidEvents(logs);
|
||||||
expect(rewardsPaidEvents).to.deep.eq([]);
|
expect(rewardsPaidEvents).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not reward a pool that was only active 3 epochs ago', async () => {
|
it('does not reward a pool that was only active 3 epochs ago', async () => {
|
||||||
const pool1 = await addActivePoolAsync();
|
const pool1 = await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool1.poolId]);
|
await finalizePoolsAsync([pool1.poolId]);
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await addActivePoolAsync();
|
await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
expect(getCurrentEpochAsync()).to.become(INITIAL_EPOCH + 3);
|
expect(getCurrentEpochAsync()).to.become(INITIAL_EPOCH + 3);
|
||||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync([pool1.poolId]);
|
const logs = await finalizePoolsAsync([pool1.poolId]);
|
||||||
const rewardsPaidEvents = getRewardsPaidEvents(receipt.logs);
|
const rewardsPaidEvents = getRewardsPaidEvents(logs);
|
||||||
expect(rewardsPaidEvents).to.deep.eq([]);
|
expect(rewardsPaidEvents).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -642,11 +486,11 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const poolIds = _.times(3, () => hexRandom());
|
const poolIds = _.times(3, () => hexRandom());
|
||||||
await Promise.all(poolIds.map(async id => addActivePoolAsync({ poolId: id })));
|
await Promise.all(poolIds.map(async id => addActivePoolAsync({ poolId: id })));
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
let receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
const finalizeLogs = await finalizePoolsAsync(poolIds);
|
||||||
const { rewardsRemaining: rolledOverRewards } = getEpochFinalizedEvents(receipt.logs)[0];
|
const { rewardsRemaining: rolledOverRewards } = getEpochFinalizedEvents(finalizeLogs)[0];
|
||||||
await Promise.all(poolIds.map(async id => addActivePoolAsync({ poolId: id })));
|
await Promise.all(poolIds.map(async id => addActivePoolAsync({ poolId: id })));
|
||||||
receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
const {logs: endEpochLogs } = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
const { rewardsAvailable } = getEpochEndedEvents(endEpochLogs)[0];
|
||||||
expect(rewardsAvailable).to.bignumber.eq(rolledOverRewards);
|
expect(rewardsAvailable).to.bignumber.eq(rolledOverRewards);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -694,7 +538,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
it('returns empty if pool was only active in the 2 epochs ago', async () => {
|
it('returns empty if pool was only active in the 2 epochs ago', async () => {
|
||||||
const pool = await addActivePoolAsync();
|
const pool = await addActivePoolAsync();
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
await finalizePoolsAsync([pool.poolId]);
|
||||||
return assertUnfinalizedPoolRewardsAsync(pool.poolId, ZERO_REWARDS);
|
return assertUnfinalizedPoolRewardsAsync(pool.poolId, ZERO_REWARDS);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -702,7 +546,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
|||||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||||
const [pool] = _.sampleSize(pools, 1);
|
const [pool] = _.sampleSize(pools, 1);
|
||||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
await finalizePoolsAsync([pool.poolId]);
|
||||||
return assertUnfinalizedPoolRewardsAsync(pool.poolId, ZERO_REWARDS);
|
return assertUnfinalizedPoolRewardsAsync(pool.poolId, ZERO_REWARDS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } f
|
|||||||
import { BlockchainTestsEnvironment, constants, filterLogsToArguments, txDefaults } from '@0x/contracts-test-utils';
|
import { BlockchainTestsEnvironment, constants, filterLogsToArguments, txDefaults } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber, logUtils } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import { BlockParamLiteral, ContractArtifact, DecodedLogArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { BlockParamLiteral, ContractArtifact, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -12,29 +12,29 @@ import {
|
|||||||
IStakingEventsEpochEndedEventArgs,
|
IStakingEventsEpochEndedEventArgs,
|
||||||
IStakingEventsStakingPoolActivatedEventArgs,
|
IStakingEventsStakingPoolActivatedEventArgs,
|
||||||
ReadOnlyProxyContract,
|
ReadOnlyProxyContract,
|
||||||
StakingContract,
|
|
||||||
StakingEvents,
|
|
||||||
StakingPoolRewardVaultContract,
|
StakingPoolRewardVaultContract,
|
||||||
StakingProxyContract,
|
StakingProxyContract,
|
||||||
TestCobbDouglasContract,
|
TestCobbDouglasContract,
|
||||||
TestStakingContract,
|
TestStakingContract,
|
||||||
|
TestStakingEvents,
|
||||||
ZrxVaultContract,
|
ZrxVaultContract,
|
||||||
} from '../../src';
|
} from '../../src';
|
||||||
|
|
||||||
import { constants as stakingConstants } from './constants';
|
import { constants as stakingConstants } from './constants';
|
||||||
import { EndOfEpochInfo, StakingParams } from './types';
|
import { DecodedLogs, EndOfEpochInfo, StakingParams } from './types';
|
||||||
|
|
||||||
export class StakingApiWrapper {
|
export class StakingApiWrapper {
|
||||||
// The address of the real Staking.sol contract
|
// The address of the real Staking.sol contract
|
||||||
public stakingContractAddress: string;
|
public stakingContractAddress: string;
|
||||||
// The StakingProxy.sol contract wrapped as a StakingContract to borrow API
|
// The StakingProxy.sol contract wrapped as a StakingContract to borrow API
|
||||||
public stakingContract: StakingContract;
|
public stakingContract: TestStakingContract;
|
||||||
// The StakingProxy.sol contract as a StakingProxyContract
|
// The StakingProxy.sol contract as a StakingProxyContract
|
||||||
public stakingProxyContract: StakingProxyContract;
|
public stakingProxyContract: StakingProxyContract;
|
||||||
public zrxVaultContract: ZrxVaultContract;
|
public zrxVaultContract: ZrxVaultContract;
|
||||||
public ethVaultContract: EthVaultContract;
|
public ethVaultContract: EthVaultContract;
|
||||||
public rewardVaultContract: StakingPoolRewardVaultContract;
|
public rewardVaultContract: StakingPoolRewardVaultContract;
|
||||||
public zrxTokenContract: DummyERC20TokenContract;
|
public zrxTokenContract: DummyERC20TokenContract;
|
||||||
|
public wethContract: WETH9Contract;
|
||||||
public cobbDouglasContract: TestCobbDouglasContract;
|
public cobbDouglasContract: TestCobbDouglasContract;
|
||||||
public utils = {
|
public utils = {
|
||||||
// Epoch Utils
|
// Epoch Utils
|
||||||
@ -49,17 +49,17 @@ export class StakingApiWrapper {
|
|||||||
await this._web3Wrapper.mineBlockAsync();
|
await this._web3Wrapper.mineBlockAsync();
|
||||||
},
|
},
|
||||||
|
|
||||||
skipToNextEpochAndFinalizeAsync: async (): Promise<DecodedLogArgs[]> => {
|
skipToNextEpochAndFinalizeAsync: async (): Promise<DecodedLogs> => {
|
||||||
await this.utils.fastForwardToNextEpochAsync();
|
await this.utils.fastForwardToNextEpochAsync();
|
||||||
const endOfEpochInfo = await this.utils.endEpochAsync();
|
const endOfEpochInfo = await this.utils.endEpochAsync();
|
||||||
let totalGasUsed = 0;
|
let totalGasUsed = 0;
|
||||||
const allLogs = [] as LogEntry[];
|
const allLogs = [] as DecodedLogs;
|
||||||
for (const poolId of endOfEpochInfo.activePoolIds) {
|
for (const poolId of endOfEpochInfo.activePoolIds) {
|
||||||
const receipt = await this.stakingContract.finalizePool.awaitTransactionSuccessAsync(
|
const receipt = await this.stakingContract.finalizePool.awaitTransactionSuccessAsync(
|
||||||
poolId,
|
poolId,
|
||||||
);
|
);
|
||||||
totalGasUsed += receipt.gasUsed;
|
totalGasUsed += receipt.gasUsed;
|
||||||
allLogs.splice(allLogs.length, 0, receipt.logs);
|
allLogs.splice(allLogs.length, 0, ...(receipt.logs as DecodedLogs));
|
||||||
}
|
}
|
||||||
logUtils.log(`Finalization cost ${totalGasUsed} gas`);
|
logUtils.log(`Finalization cost ${totalGasUsed} gas`);
|
||||||
return allLogs;
|
return allLogs;
|
||||||
@ -70,7 +70,7 @@ export class StakingApiWrapper {
|
|||||||
const receipt = await this.stakingContract.endEpoch.awaitTransactionSuccessAsync();
|
const receipt = await this.stakingContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
const [epochEndedEvent] = filterLogsToArguments<IStakingEventsEpochEndedEventArgs>(
|
const [epochEndedEvent] = filterLogsToArguments<IStakingEventsEpochEndedEventArgs>(
|
||||||
receipt.logs,
|
receipt.logs,
|
||||||
StakingEvents.EpochEnded,
|
TestStakingEvents.EpochEnded,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
closingEpoch: epochEndedEvent.epoch,
|
closingEpoch: epochEndedEvent.epoch,
|
||||||
@ -85,11 +85,11 @@ export class StakingApiWrapper {
|
|||||||
const _epoch = epoch !== undefined ? epoch : await this.stakingContract.currentEpoch.callAsync();
|
const _epoch = epoch !== undefined ? epoch : await this.stakingContract.currentEpoch.callAsync();
|
||||||
const events = filterLogsToArguments<IStakingEventsStakingPoolActivatedEventArgs>(
|
const events = filterLogsToArguments<IStakingEventsStakingPoolActivatedEventArgs>(
|
||||||
await this.stakingContract.getLogsAsync(
|
await this.stakingContract.getLogsAsync(
|
||||||
StakingEvents.StakingPoolActivated,
|
TestStakingEvents.StakingPoolActivated,
|
||||||
{ fromBlock: BlockParamLiteral.Earliest, toBlock: BlockParamLiteral.Latest },
|
{ fromBlock: BlockParamLiteral.Earliest, toBlock: BlockParamLiteral.Latest },
|
||||||
{ epoch: new BigNumber(_epoch) },
|
{ epoch: new BigNumber(_epoch) },
|
||||||
),
|
),
|
||||||
StakingEvents.StakingPoolActivated,
|
TestStakingEvents.StakingPoolActivated,
|
||||||
);
|
);
|
||||||
return events.map(e => e.poolId);
|
return events.map(e => e.poolId);
|
||||||
},
|
},
|
||||||
@ -177,11 +177,12 @@ export class StakingApiWrapper {
|
|||||||
env: BlockchainTestsEnvironment,
|
env: BlockchainTestsEnvironment,
|
||||||
ownerAddress: string,
|
ownerAddress: string,
|
||||||
stakingProxyContract: StakingProxyContract,
|
stakingProxyContract: StakingProxyContract,
|
||||||
stakingContract: StakingContract,
|
stakingContract: TestStakingContract,
|
||||||
zrxVaultContract: ZrxVaultContract,
|
zrxVaultContract: ZrxVaultContract,
|
||||||
ethVaultContract: EthVaultContract,
|
ethVaultContract: EthVaultContract,
|
||||||
rewardVaultContract: StakingPoolRewardVaultContract,
|
rewardVaultContract: StakingPoolRewardVaultContract,
|
||||||
zrxTokenContract: DummyERC20TokenContract,
|
zrxTokenContract: DummyERC20TokenContract,
|
||||||
|
wethContract: WETH9Contract,
|
||||||
cobbDouglasContract: TestCobbDouglasContract,
|
cobbDouglasContract: TestCobbDouglasContract,
|
||||||
) {
|
) {
|
||||||
this._web3Wrapper = env.web3Wrapper;
|
this._web3Wrapper = env.web3Wrapper;
|
||||||
@ -189,13 +190,14 @@ export class StakingApiWrapper {
|
|||||||
this.ethVaultContract = ethVaultContract;
|
this.ethVaultContract = ethVaultContract;
|
||||||
this.rewardVaultContract = rewardVaultContract;
|
this.rewardVaultContract = rewardVaultContract;
|
||||||
this.zrxTokenContract = zrxTokenContract;
|
this.zrxTokenContract = zrxTokenContract;
|
||||||
|
this.wethContract = wethContract;
|
||||||
this.cobbDouglasContract = cobbDouglasContract;
|
this.cobbDouglasContract = cobbDouglasContract;
|
||||||
|
|
||||||
this.stakingContractAddress = stakingContract.address;
|
this.stakingContractAddress = stakingContract.address;
|
||||||
this.stakingProxyContract = stakingProxyContract;
|
this.stakingProxyContract = stakingProxyContract;
|
||||||
// disguise the staking proxy as a StakingContract
|
// disguise the staking proxy as a StakingContract
|
||||||
const logDecoderDependencies = _.mapValues({ ...artifacts, ...erc20Artifacts }, v => v.compilerOutput.abi);
|
const logDecoderDependencies = _.mapValues({ ...artifacts, ...erc20Artifacts }, v => v.compilerOutput.abi);
|
||||||
this.stakingContract = new StakingContract(
|
this.stakingContract = new TestStakingContract(
|
||||||
stakingProxyContract.address,
|
stakingProxyContract.address,
|
||||||
env.provider,
|
env.provider,
|
||||||
{
|
{
|
||||||
@ -256,6 +258,7 @@ export async function deployAndConfigureContractsAsync(
|
|||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
|
wethContract.address,
|
||||||
);
|
);
|
||||||
// deploy reward vault
|
// deploy reward vault
|
||||||
const rewardVaultContract = await StakingPoolRewardVaultContract.deployFrom0xArtifactAsync(
|
const rewardVaultContract = await StakingPoolRewardVaultContract.deployFrom0xArtifactAsync(
|
||||||
@ -263,6 +266,7 @@ export async function deployAndConfigureContractsAsync(
|
|||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
|
wethContract.address,
|
||||||
);
|
);
|
||||||
// deploy zrx vault
|
// deploy zrx vault
|
||||||
const zrxVaultContract = await ZrxVaultContract.deployFrom0xArtifactAsync(
|
const zrxVaultContract = await ZrxVaultContract.deployFrom0xArtifactAsync(
|
||||||
@ -311,6 +315,7 @@ export async function deployAndConfigureContractsAsync(
|
|||||||
ethVaultContract,
|
ethVaultContract,
|
||||||
rewardVaultContract,
|
rewardVaultContract,
|
||||||
zrxTokenContract,
|
zrxTokenContract,
|
||||||
|
wethContract,
|
||||||
cobbDouglasContract,
|
cobbDouglasContract,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import { DecodedLogEntry, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { DecodedLogEntry, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, TestCumulativeRewardTrackingContract, IStakingEvents } from '../../src';
|
import { artifacts, TestCumulativeRewardTrackingContract, TestCumulativeRewardTrackingEvents } from '../../src';
|
||||||
|
|
||||||
import { StakingApiWrapper } from './api_wrapper';
|
import { StakingApiWrapper } from './api_wrapper';
|
||||||
import { toBaseUnitAmount } from './number_utils';
|
import { toBaseUnitAmount } from './number_utils';
|
||||||
import { StakeInfo, StakeStatus } from './types';
|
import { DecodedLogs, StakeInfo, StakeStatus } from './types';
|
||||||
|
|
||||||
export enum TestAction {
|
export enum TestAction {
|
||||||
Finalize,
|
Finalize,
|
||||||
@ -33,22 +33,22 @@ export class CumulativeRewardTrackingSimulation {
|
|||||||
private _testCumulativeRewardTrackingContract?: TestCumulativeRewardTrackingContract;
|
private _testCumulativeRewardTrackingContract?: TestCumulativeRewardTrackingContract;
|
||||||
private _poolId: string;
|
private _poolId: string;
|
||||||
|
|
||||||
private static _extractTestLogs(txReceiptLogs: DecodedLogArgs[]): TestLog[] {
|
private static _extractTestLogs(txReceiptLogs: DecodedLogs): TestLog[] {
|
||||||
const logs = [];
|
const logs = [];
|
||||||
for (const log of txReceiptLogs) {
|
for (const log of txReceiptLogs) {
|
||||||
if (log.event === 'SetMostRecentCumulativeReward') {
|
if (log.event === TestCumulativeRewardTrackingEvents.SetMostRecentCumulativeReward) {
|
||||||
logs.push({
|
logs.push({
|
||||||
event: 'SetMostRecentCumulativeReward',
|
event: log.event,
|
||||||
epoch: log.args.epoch.toNumber(),
|
epoch: log.args.epoch.toNumber(),
|
||||||
});
|
});
|
||||||
} else if (log.event === 'SetCumulativeReward') {
|
} else if (log.event === TestCumulativeRewardTrackingEvents.SetCumulativeReward) {
|
||||||
logs.push({
|
logs.push({
|
||||||
event: 'SetCumulativeReward',
|
event: log.event,
|
||||||
epoch: log.args.epoch.toNumber(),
|
epoch: log.args.epoch.toNumber(),
|
||||||
});
|
});
|
||||||
} else if (log.event === 'UnsetCumulativeReward') {
|
} else if (log.event === TestCumulativeRewardTrackingEvents.UnsetCumulativeReward) {
|
||||||
logs.push({
|
logs.push({
|
||||||
event: 'UnsetCumulativeReward',
|
event: log.event,
|
||||||
epoch: log.args.epoch.toNumber(),
|
epoch: log.args.epoch.toNumber(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ export class CumulativeRewardTrackingSimulation {
|
|||||||
return logs;
|
return logs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _assertTestLogs(expectedSequence: TestLog[], txReceiptLogs: DecodedLogArgs[]): void {
|
private static _assertTestLogs(expectedSequence: TestLog[], txReceiptLogs: DecodedLogs): void {
|
||||||
const logs = CumulativeRewardTrackingSimulation._extractTestLogs(txReceiptLogs);
|
const logs = CumulativeRewardTrackingSimulation._extractTestLogs(txReceiptLogs);
|
||||||
expect(logs.length).to.be.equal(expectedSequence.length);
|
expect(logs.length).to.be.equal(expectedSequence.length);
|
||||||
for (let i = 0; i < expectedSequence.length; i++) {
|
for (let i = 0; i < expectedSequence.length; i++) {
|
||||||
@ -90,6 +90,7 @@ export class CumulativeRewardTrackingSimulation {
|
|||||||
env.provider,
|
env.provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
|
this._stakingApiWrapper.wethContract.address,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +118,11 @@ export class CumulativeRewardTrackingSimulation {
|
|||||||
CumulativeRewardTrackingSimulation._assertTestLogs(expectedTestLogs, testLogs);
|
CumulativeRewardTrackingSimulation._assertTestLogs(expectedTestLogs, testLogs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _executeActionsAsync(actions: TestAction[]): Promise<Array<DecodedLogEntry<any>>> {
|
private async _executeActionsAsync(actions: TestAction[]): Promise<DecodedLogs> {
|
||||||
const combinedLogs = [] as Array<DecodedLogEntry<any>>;
|
const combinedLogs = [] as DecodedLogs;
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
let receipt: TransactionReceiptWithDecodedLogs;
|
let receipt: TransactionReceiptWithDecodedLogs | undefined;
|
||||||
let logs = [] as DecodedLogEntry<any>;
|
let logs = [] as DecodedLogs;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case TestAction.Finalize:
|
case TestAction.Finalize:
|
||||||
logs = await this._stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
logs = await this._stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
||||||
@ -163,15 +164,18 @@ export class CumulativeRewardTrackingSimulation {
|
|||||||
true,
|
true,
|
||||||
{ from: this._poolOperator },
|
{ from: this._poolOperator },
|
||||||
);
|
);
|
||||||
const createStakingPoolLog = logs[0];
|
const createStakingPoolLog = receipt.logs[0];
|
||||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||||
this._poolId = (createStakingPoolLog as DecodedLogArgs).args.poolId;
|
this._poolId = (createStakingPoolLog as DecodedLogEntry<any>).args.poolId;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Unrecognized test action');
|
throw new Error('Unrecognized test action');
|
||||||
}
|
}
|
||||||
combinedLogs.splice(combinedLogs.length - 1, 0, logs);
|
if (receipt !== undefined) {
|
||||||
|
logs = receipt.logs as DecodedLogs;
|
||||||
|
}
|
||||||
|
combinedLogs.splice(combinedLogs.length - 1, 0, ...logs);
|
||||||
}
|
}
|
||||||
return combinedLogs;
|
return combinedLogs;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Numberish } from '@0x/contracts-test-utils';
|
import { Numberish } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
|
||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
|
|
||||||
@ -133,3 +134,5 @@ export interface OperatorByPoolId {
|
|||||||
export interface DelegatorsByPoolId {
|
export interface DelegatorsByPoolId {
|
||||||
[key: string]: string[];
|
[key: string]: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DecodedLogs = Array<LogWithDecodedArgs<DecodedLogArgs>>;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user