@0x/contracts-staking
: Fixing tests.
This commit is contained in:
parent
58a5ab4550
commit
d548ddac0d
10
contracts/staking/.prettierrc
Normal file
10
contracts/staking/.prettierrc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "./test/**.ts",
|
||||||
|
"options": {
|
||||||
|
"printWidth": 80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -8,7 +8,12 @@
|
|||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
"details": {
|
||||||
|
"yul": true,
|
||||||
|
"deduplicate": true,
|
||||||
|
"cse": true,
|
||||||
|
"constantOptimizer": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"outputSelection": {
|
"outputSelection": {
|
||||||
"*": {
|
"*": {
|
||||||
|
@ -21,6 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "./interfaces/IStaking.sol";
|
import "./interfaces/IStaking.sol";
|
||||||
import "./sys/MixinParams.sol";
|
import "./sys/MixinParams.sol";
|
||||||
|
import "./sys/MixinFinalizer.sol";
|
||||||
import "./stake/MixinStake.sol";
|
import "./stake/MixinStake.sol";
|
||||||
import "./staking_pools/MixinStakingPool.sol";
|
import "./staking_pools/MixinStakingPool.sol";
|
||||||
import "./fees/MixinExchangeFees.sol";
|
import "./fees/MixinExchangeFees.sol";
|
||||||
@ -31,7 +32,7 @@ contract Staking is
|
|||||||
MixinParams,
|
MixinParams,
|
||||||
MixinStakingPool,
|
MixinStakingPool,
|
||||||
MixinStake,
|
MixinStake,
|
||||||
MixinExchangeFees
|
MixinExchangeFees,
|
||||||
{
|
{
|
||||||
// this contract can receive ETH
|
// this contract can receive ETH
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
|
@ -335,6 +335,15 @@ contract MixinFinalizer is
|
|||||||
rewards.membersStake = pool.delegatedStake;
|
rewards.membersStake = pool.delegatedStake;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Converts the entire WETH balance of the contract into ETH.
|
||||||
|
function _unwrapWETH() internal {
|
||||||
|
uint256 wethBalance = IEtherToken(WETH_ADDRESS)
|
||||||
|
.balanceOf(address(this));
|
||||||
|
if (wethBalance != 0) {
|
||||||
|
IEtherToken(WETH_ADDRESS).withdraw(wethBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Computes the reward owed to a pool during finalization and
|
/// @dev Computes the reward owed to a pool during finalization and
|
||||||
/// credits it to that pool for the CURRENT epoch.
|
/// credits it to that pool for the CURRENT epoch.
|
||||||
/// @param poolId The pool's ID.
|
/// @param poolId The pool's ID.
|
||||||
@ -377,13 +386,4 @@ contract MixinFinalizer is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Converts the entire WETH balance of the contract into ETH.
|
|
||||||
function _unwrapWETH() private {
|
|
||||||
uint256 wethBalance = IEtherToken(WETH_ADDRESS)
|
|
||||||
.balanceOf(address(this));
|
|
||||||
if (wethBalance != 0) {
|
|
||||||
IEtherToken(WETH_ADDRESS).withdraw(wethBalance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
29
contracts/staking/contracts/test/TestDelegatorRewards.sol
Normal file
29
contracts/staking/contracts/test/TestDelegatorRewards.sol
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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/Staking.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestDelegatorRewards is
|
||||||
|
Staking
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from "ethereum-types";
|
||||||
|
|
||||||
import * as EthVault from '../generated-artifacts/EthVault.json';
|
import * as EthVault from '../generated-artifacts/EthVault.json';
|
||||||
import * as IEthVault from '../generated-artifacts/IEthVault.json';
|
import * as IEthVault from '../generated-artifacts/IEthVault.json';
|
||||||
|
@ -62,7 +62,7 @@ export class FinalizerActor extends BaseActor {
|
|||||||
memberRewardByPoolId,
|
memberRewardByPoolId,
|
||||||
);
|
);
|
||||||
// finalize
|
// finalize
|
||||||
await this._stakingApiWrapper.utils.skipToNextEpochAsync();
|
await this._stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
||||||
// assert reward vault changes
|
// assert reward vault changes
|
||||||
const finalRewardVaultBalanceByPoolId = await this._getRewardVaultBalanceByPoolIdAsync(this._poolIds);
|
const finalRewardVaultBalanceByPoolId = await this._getRewardVaultBalanceByPoolIdAsync(this._poolIds);
|
||||||
expect(finalRewardVaultBalanceByPoolId, 'final pool balances in reward vault').to.be.deep.equal(
|
expect(finalRewardVaultBalanceByPoolId, 'final pool balances in reward vault').to.be.deep.equal(
|
||||||
|
@ -151,7 +151,7 @@ export class StakerActor extends BaseActor {
|
|||||||
const initZrxBalanceOfVault = await this._stakingApiWrapper.utils.getZrxTokenBalanceOfZrxVaultAsync();
|
const initZrxBalanceOfVault = await this._stakingApiWrapper.utils.getZrxTokenBalanceOfZrxVaultAsync();
|
||||||
const initBalances = await this._getBalancesAsync();
|
const initBalances = await this._getBalancesAsync();
|
||||||
// go to next epoch
|
// go to next epoch
|
||||||
await this._stakingApiWrapper.utils.skipToNextEpochAsync();
|
await this._stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
||||||
// check balances
|
// check balances
|
||||||
const expectedBalances = this._getNextEpochBalances(initBalances);
|
const expectedBalances = this._getNextEpochBalances(initBalances);
|
||||||
await this._assertBalancesAsync(expectedBalances);
|
await this._assertBalancesAsync(expectedBalances);
|
||||||
|
@ -38,7 +38,7 @@ blockchainTests('Epochs', env => {
|
|||||||
expect(currentEpoch).to.be.bignumber.equal(stakingConstants.INITIAL_EPOCH);
|
expect(currentEpoch).to.be.bignumber.equal(stakingConstants.INITIAL_EPOCH);
|
||||||
}
|
}
|
||||||
///// 3/3 Increment Epoch (TimeLock Should Not Increment) /////
|
///// 3/3 Increment Epoch (TimeLock Should Not Increment) /////
|
||||||
await stakingApiWrapper.utils.skipToNextEpochAsync();
|
await stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
||||||
{
|
{
|
||||||
// epoch
|
// epoch
|
||||||
const currentEpoch = await stakingApiWrapper.stakingContract.currentEpoch.callAsync();
|
const currentEpoch = await stakingApiWrapper.stakingContract.currentEpoch.callAsync();
|
||||||
|
@ -701,8 +701,7 @@ blockchainTests.resets('Testing Rewards', env => {
|
|||||||
const stakeAmounts = [toBaseUnitAmount(5), toBaseUnitAmount(10)];
|
const stakeAmounts = [toBaseUnitAmount(5), toBaseUnitAmount(10)];
|
||||||
const totalStakeAmount = BigNumber.sum(...stakeAmounts);
|
const totalStakeAmount = BigNumber.sum(...stakeAmounts);
|
||||||
// stake and delegate both
|
// stake and delegate both
|
||||||
const stakersAndStake = _.zip(stakers.slice(0, 2), stakeAmounts) as
|
const stakersAndStake = _.zip(stakers.slice(0, 2), stakeAmounts) as Array<[StakerActor, BigNumber]>;
|
||||||
Array<[StakerActor, BigNumber]>;
|
|
||||||
for (const [staker, stakeAmount] of stakersAndStake) {
|
for (const [staker, stakeAmount] of stakersAndStake) {
|
||||||
await staker.stakeAsync(stakeAmount);
|
await staker.stakeAsync(stakeAmount);
|
||||||
await staker.moveStakeAsync(
|
await staker.moveStakeAsync(
|
||||||
@ -724,8 +723,7 @@ blockchainTests.resets('Testing Rewards', env => {
|
|||||||
toBaseUnitAmount(0),
|
toBaseUnitAmount(0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const expectedStakerRewards = stakeAmounts
|
const expectedStakerRewards = stakeAmounts.map(n => reward.times(n).dividedToIntegerBy(totalStakeAmount));
|
||||||
.map(n => reward.times(n).dividedToIntegerBy(totalStakeAmount));
|
|
||||||
await validateEndBalances({
|
await validateEndBalances({
|
||||||
stakerRewardVaultBalance_1: toBaseUnitAmount(0),
|
stakerRewardVaultBalance_1: toBaseUnitAmount(0),
|
||||||
stakerRewardVaultBalance_2: toBaseUnitAmount(0),
|
stakerRewardVaultBalance_2: toBaseUnitAmount(0),
|
||||||
@ -739,8 +737,7 @@ blockchainTests.resets('Testing Rewards', env => {
|
|||||||
const stakeAmounts = [toBaseUnitAmount(5), toBaseUnitAmount(10)];
|
const stakeAmounts = [toBaseUnitAmount(5), toBaseUnitAmount(10)];
|
||||||
const totalStakeAmount = BigNumber.sum(...stakeAmounts);
|
const totalStakeAmount = BigNumber.sum(...stakeAmounts);
|
||||||
// stake and delegate both
|
// stake and delegate both
|
||||||
const stakersAndStake = _.zip(stakers.slice(0, 2), stakeAmounts) as
|
const stakersAndStake = _.zip(stakers.slice(0, 2), stakeAmounts) as Array<[StakerActor, BigNumber]>;
|
||||||
Array<[StakerActor, BigNumber]>;
|
|
||||||
for (const [staker, stakeAmount] of stakersAndStake) {
|
for (const [staker, stakeAmount] of stakersAndStake) {
|
||||||
await staker.stakeAsync(stakeAmount);
|
await staker.stakeAsync(stakeAmount);
|
||||||
await staker.moveStakeAsync(
|
await staker.moveStakeAsync(
|
||||||
@ -754,8 +751,7 @@ blockchainTests.resets('Testing Rewards', env => {
|
|||||||
// finalize
|
// finalize
|
||||||
const reward = toBaseUnitAmount(10);
|
const reward = toBaseUnitAmount(10);
|
||||||
await payProtocolFeeAndFinalize(reward);
|
await payProtocolFeeAndFinalize(reward);
|
||||||
const expectedStakerRewards = stakeAmounts
|
const expectedStakerRewards = stakeAmounts.map(n => reward.times(n).dividedToIntegerBy(totalStakeAmount));
|
||||||
.map(n => reward.times(n).dividedToIntegerBy(totalStakeAmount));
|
|
||||||
await validateEndBalances({
|
await validateEndBalances({
|
||||||
stakerRewardVaultBalance_1: expectedStakerRewards[0],
|
stakerRewardVaultBalance_1: expectedStakerRewards[0],
|
||||||
stakerRewardVaultBalance_2: expectedStakerRewards[1],
|
stakerRewardVaultBalance_2: expectedStakerRewards[1],
|
||||||
@ -776,19 +772,21 @@ blockchainTests.resets('Testing Rewards', env => {
|
|||||||
const sneakyStakerExpectedEthVaultBalance = expectedStakerRewards[0];
|
const sneakyStakerExpectedEthVaultBalance = expectedStakerRewards[0];
|
||||||
await undelegateZeroAsync(sneakyStaker);
|
await undelegateZeroAsync(sneakyStaker);
|
||||||
// Should have been credited the correct amount of rewards.
|
// Should have been credited the correct amount of rewards.
|
||||||
let sneakyStakerEthVaultBalance = await stakingApiWrapper
|
let sneakyStakerEthVaultBalance = await stakingApiWrapper.ethVaultContract.balanceOf.callAsync(
|
||||||
.ethVaultContract.balanceOf
|
sneakyStaker.getOwner(),
|
||||||
.callAsync(sneakyStaker.getOwner());
|
);
|
||||||
expect(sneakyStakerEthVaultBalance, 'EthVault balance after first undelegate')
|
expect(sneakyStakerEthVaultBalance, 'EthVault balance after first undelegate').to.bignumber.eq(
|
||||||
.to.bignumber.eq(sneakyStakerExpectedEthVaultBalance);
|
sneakyStakerExpectedEthVaultBalance,
|
||||||
|
);
|
||||||
// Now he'll try to do it again to see if he gets credited twice.
|
// Now he'll try to do it again to see if he gets credited twice.
|
||||||
await undelegateZeroAsync(sneakyStaker);
|
await undelegateZeroAsync(sneakyStaker);
|
||||||
/// The total amount credited should remain the same.
|
/// The total amount credited should remain the same.
|
||||||
sneakyStakerEthVaultBalance = await stakingApiWrapper
|
sneakyStakerEthVaultBalance = await stakingApiWrapper.ethVaultContract.balanceOf.callAsync(
|
||||||
.ethVaultContract.balanceOf
|
sneakyStaker.getOwner(),
|
||||||
.callAsync(sneakyStaker.getOwner());
|
);
|
||||||
expect(sneakyStakerEthVaultBalance, 'EthVault balance after second undelegate')
|
expect(sneakyStakerEthVaultBalance, 'EthVault balance after second undelegate').to.bignumber.eq(
|
||||||
.to.bignumber.eq(sneakyStakerExpectedEthVaultBalance);
|
sneakyStakerExpectedEthVaultBalance,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
|
||||||
|
|
||||||
|
import { artifacts, TestDelegatorRewardsContract } from '../../src';
|
||||||
|
|
||||||
|
blockchainTests('delegator rewards', env => {
|
||||||
|
let testContract: TestDelegatorRewardsContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestDelegatorRewardsContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestLibFixedMath,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('computeRewardBalanceOfDelegator()', () => {
|
||||||
|
it('does stuff', () => {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -8,6 +8,7 @@ import {
|
|||||||
testCombinatoriallyWithReferenceFunc,
|
testCombinatoriallyWithReferenceFunc,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { StakingRevertErrors } from '@0x/order-utils';
|
import { StakingRevertErrors } from '@0x/order-utils';
|
||||||
|
import { cartesianProduct } from 'js-combinatorics';
|
||||||
|
|
||||||
import { artifacts, TestLibProxyContract, TestLibProxyReceiverContract } from '../../src';
|
import { artifacts, TestLibProxyContract, TestLibProxyReceiverContract } from '../../src';
|
||||||
|
|
||||||
@ -196,25 +197,51 @@ blockchainTests.resets('LibProxy', env => {
|
|||||||
|
|
||||||
describe('Combinatorial Tests', () => {
|
describe('Combinatorial Tests', () => {
|
||||||
// Combinatorial Scenarios for `proxyCall()`.
|
// Combinatorial Scenarios for `proxyCall()`.
|
||||||
const revertRuleScenarios: RevertRule[] = [
|
function getCombinatorialTestDescription(params: [RevertRule, boolean, string, string]): string {
|
||||||
|
const REVERT_RULE_NAMES = [
|
||||||
|
'RevertOnError',
|
||||||
|
'AlwaysRevert',
|
||||||
|
'NeverRevert',
|
||||||
|
];
|
||||||
|
return [
|
||||||
|
`revertRule: ${REVERT_RULE_NAMES[params[0]]}`,
|
||||||
|
`ignoreIngressSelector: ${params[1]}`,
|
||||||
|
`customEgressSelector: ${params[2]}`,
|
||||||
|
`calldata: ${
|
||||||
|
params[3].length / 2 - 2 > 4
|
||||||
|
? // tslint:disable-next-line
|
||||||
|
hexSlice(params[3], 0, 4) + '...'
|
||||||
|
: params[3]
|
||||||
|
}`,
|
||||||
|
].join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const scenarios = [
|
||||||
|
// revertRule
|
||||||
|
[
|
||||||
RevertRule.RevertOnError,
|
RevertRule.RevertOnError,
|
||||||
RevertRule.AlwaysRevert,
|
RevertRule.AlwaysRevert,
|
||||||
RevertRule.NeverRevert,
|
RevertRule.NeverRevert,
|
||||||
];
|
],
|
||||||
const ignoreIngressScenarios: boolean[] = [false, true];
|
// ignoreIngressSelector
|
||||||
const customEgressScenarios: string[] = [
|
[false, true],
|
||||||
|
// customEgressSelector
|
||||||
|
[
|
||||||
constants.NULL_BYTES4,
|
constants.NULL_BYTES4,
|
||||||
constructRandomFailureCalldata(), // Random failure calldata is used because it is nonzero and won't collide.
|
// Random failure calldata is used because it is nonzero and
|
||||||
];
|
// won't collide.
|
||||||
const calldataScenarios: string[] = [constructRandomFailureCalldata(), constructRandomSuccessCalldata()];
|
constructRandomFailureCalldata(),
|
||||||
|
],
|
||||||
|
// calldata
|
||||||
|
[
|
||||||
|
constructRandomFailureCalldata(),
|
||||||
|
constructRandomSuccessCalldata(),
|
||||||
|
],
|
||||||
|
] as [RevertRule[], boolean[], string[], string[]];
|
||||||
|
|
||||||
// A reference function that returns the expected success and returndata values of a given call to `proxyCall()`.
|
for (const params of cartesianProduct(...scenarios).toArray()) {
|
||||||
async function referenceFuncAsync(
|
const [revertRule, shouldIgnoreIngressSelector, customEgressSelector, calldata] = params;
|
||||||
revertRule: RevertRule,
|
it(getCombinatorialTestDescription(params), async () => {
|
||||||
customEgressSelector: string,
|
|
||||||
shouldIgnoreIngressSelector: boolean,
|
|
||||||
calldata: string,
|
|
||||||
): Promise<[boolean, string]> {
|
|
||||||
// Determine whether or not the call should succeed.
|
// Determine whether or not the call should succeed.
|
||||||
let shouldSucceed = true;
|
let shouldSucceed = true;
|
||||||
if (
|
if (
|
||||||
@ -242,33 +269,16 @@ blockchainTests.resets('LibProxy', env => {
|
|||||||
returnData = hexConcat(customEgressSelector, returnData);
|
returnData = hexConcat(customEgressSelector, returnData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the success and return data values.
|
const [didSucceed, actualReturnData] = await publicProxyCallAsync({
|
||||||
return [shouldSucceed, returnData];
|
|
||||||
}
|
|
||||||
|
|
||||||
// A wrapper for `publicProxyCall()` that allow us to combinatorially test `proxyCall()` for the
|
|
||||||
// scenarios defined above.
|
|
||||||
async function testFuncAsync(
|
|
||||||
revertRule: RevertRule,
|
|
||||||
customEgressSelector: string,
|
|
||||||
shouldIgnoreIngressSelector: boolean,
|
|
||||||
calldata: string,
|
|
||||||
): Promise<[boolean, string]> {
|
|
||||||
return publicProxyCallAsync({
|
|
||||||
calldata,
|
calldata,
|
||||||
customEgressSelector,
|
customEgressSelector,
|
||||||
ignoreIngressSelector: shouldIgnoreIngressSelector,
|
ignoreIngressSelector: shouldIgnoreIngressSelector,
|
||||||
revertRule,
|
revertRule,
|
||||||
});
|
});
|
||||||
|
expect(didSucceed).to.be.eq(shouldSucceed);
|
||||||
|
expect(actualReturnData).to.be.eq(returnData);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combinatorially test proxy call.
|
|
||||||
testCombinatoriallyWithReferenceFunc('proxyCall', referenceFuncAsync, testFuncAsync, [
|
|
||||||
revertRuleScenarios,
|
|
||||||
customEgressScenarios,
|
|
||||||
ignoreIngressScenarios,
|
|
||||||
calldataScenarios,
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,28 +1,34 @@
|
|||||||
import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
import { BlockchainTestsEnvironment, constants } 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 { ContractArtifact, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { BlockParamLiteral, ContractArtifact, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
artifacts,
|
artifacts,
|
||||||
EthVaultContract,
|
EthVaultContract,
|
||||||
|
IStakingEventsEpochEndedEventArgs,
|
||||||
|
IStakingEventsStakingPoolActivatedEventArgs,
|
||||||
ReadOnlyProxyContract,
|
ReadOnlyProxyContract,
|
||||||
StakingContract,
|
StakingContract,
|
||||||
|
StakingEvents,
|
||||||
StakingPoolRewardVaultContract,
|
StakingPoolRewardVaultContract,
|
||||||
StakingProxyContract,
|
StakingProxyContract,
|
||||||
ZrxVaultContract,
|
ZrxVaultContract,
|
||||||
} from '../../src';
|
} from '../../src';
|
||||||
|
|
||||||
import { constants as stakingConstants } from './constants';
|
import { constants as stakingConstants } from './constants';
|
||||||
import { StakingParams } from './types';
|
import { EndOfEpochInfo, StakingParams } from './types';
|
||||||
|
|
||||||
export class StakingApiWrapper {
|
export class StakingApiWrapper {
|
||||||
public stakingContractAddress: string; // The address of the real Staking.sol contract
|
// The address of the real Staking.sol contract
|
||||||
public stakingContract: StakingContract; // The StakingProxy.sol contract wrapped as a StakingContract to borrow API
|
public stakingContractAddress: string;
|
||||||
public stakingProxyContract: StakingProxyContract; // The StakingProxy.sol contract as a StakingProxyContract
|
// The StakingProxy.sol contract wrapped as a StakingContract to borrow API
|
||||||
|
public stakingContract: StakingContract;
|
||||||
|
// The StakingProxy.sol contract as a StakingProxyContract
|
||||||
|
public stakingProxyContract: StakingProxyContract;
|
||||||
public zrxVaultContract: ZrxVaultContract;
|
public zrxVaultContract: ZrxVaultContract;
|
||||||
public ethVaultContract: EthVaultContract;
|
public ethVaultContract: EthVaultContract;
|
||||||
public rewardVaultContract: StakingPoolRewardVaultContract;
|
public rewardVaultContract: StakingPoolRewardVaultContract;
|
||||||
@ -30,21 +36,53 @@ export class StakingApiWrapper {
|
|||||||
public utils = {
|
public utils = {
|
||||||
// Epoch Utils
|
// Epoch Utils
|
||||||
fastForwardToNextEpochAsync: async (): Promise<void> => {
|
fastForwardToNextEpochAsync: async (): Promise<void> => {
|
||||||
// increase timestamp of next block
|
// increase timestamp of next block by how many seconds we need to
|
||||||
const { epochDurationInSeconds } = await this.utils.getParamsAsync();
|
// get to the next epoch.
|
||||||
await this._web3Wrapper.increaseTimeAsync(epochDurationInSeconds.toNumber());
|
const epochEndTime = await this.stakingContract.getCurrentEpochEarliestEndTimeInSeconds.callAsync();
|
||||||
|
const lastBlockTime = await this._web3Wrapper.getBlockTimestampAsync('latest');
|
||||||
|
const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber());
|
||||||
|
await this._web3Wrapper.increaseTimeAsync(dt);
|
||||||
// mine next block
|
// mine next block
|
||||||
await this._web3Wrapper.mineBlockAsync();
|
await this._web3Wrapper.mineBlockAsync();
|
||||||
},
|
},
|
||||||
|
|
||||||
skipToNextEpochAsync: async (): Promise<TransactionReceiptWithDecodedLogs> => {
|
skipToNextEpochAndFinalizeAsync: async (): Promise<TransactionReceiptWithDecodedLogs> => {
|
||||||
await this.utils.fastForwardToNextEpochAsync();
|
await this.utils.fastForwardToNextEpochAsync();
|
||||||
// increment epoch in contracts
|
const endOfEpochInfo = await this.utils.endEpochAsync();
|
||||||
const txReceipt = await this.stakingContract.finalizeFees.awaitTransactionSuccessAsync();
|
const receipt = await this.stakingContract.finalizePools.awaitTransactionSuccessAsync(
|
||||||
logUtils.log(`Finalization costed ${txReceipt.gasUsed} gas`);
|
endOfEpochInfo.activePoolIds,
|
||||||
// mine next block
|
);
|
||||||
await this._web3Wrapper.mineBlockAsync();
|
logUtils.log(`Finalization cost ${receipt.gasUsed} gas`);
|
||||||
return txReceipt;
|
return receipt;
|
||||||
|
},
|
||||||
|
|
||||||
|
endEpochAsync: async (): Promise<EndOfEpochInfo> => {
|
||||||
|
const activePoolIds = await this.utils.findActivePoolIdsAsync();
|
||||||
|
const receipt = await this.stakingContract.endEpoch.awaitTransactionSuccessAsync();
|
||||||
|
const [epochEndedEvent] = filterLogsToArguments<IStakingEventsEpochEndedEventArgs>(
|
||||||
|
receipt.logs,
|
||||||
|
StakingEvents.EpochEnded,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
closingEpoch: epochEndedEvent.epoch,
|
||||||
|
activePoolIds,
|
||||||
|
rewardsAvailable: epochEndedEvent.rewardsAvailable,
|
||||||
|
totalFeesCollected: epochEndedEvent.totalFeesCollected,
|
||||||
|
totalWeightedStake: epochEndedEvent.totalWeightedStake,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
findActivePoolIdsAsync: async (epoch?: number): Promise<string[]> => {
|
||||||
|
const _epoch = epoch !== undefined ? epoch : await this.stakingContract.getCurrentEpoch.callAsync();
|
||||||
|
const events = filterLogsToArguments<IStakingEventsStakingPoolActivatedEventArgs>(
|
||||||
|
await this.stakingContract.getLogsAsync(
|
||||||
|
StakingEvents.StakingPoolActivated,
|
||||||
|
{ fromBlock: BlockParamLiteral.Earliest, toBlock: BlockParamLiteral.Latest },
|
||||||
|
{ epoch: _epoch },
|
||||||
|
),
|
||||||
|
StakingEvents.StakingPoolActivated,
|
||||||
|
);
|
||||||
|
return events.map(e => e.poolId);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Other Utils
|
// Other Utils
|
||||||
|
@ -53,6 +53,14 @@ export interface SimulationParams {
|
|||||||
withdrawByUndelegating: boolean;
|
withdrawByUndelegating: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EndOfEpochInfo {
|
||||||
|
closingEpoch: BigNumber;
|
||||||
|
activePoolIds: string[];
|
||||||
|
rewardsAvailable: BigNumber;
|
||||||
|
totalFeesCollected: BigNumber;
|
||||||
|
totalWeightedStake: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
export interface StakeBalance {
|
export interface StakeBalance {
|
||||||
currentEpochBalance: BigNumber;
|
currentEpochBalance: BigNumber;
|
||||||
nextEpochBalance: BigNumber;
|
nextEpochBalance: BigNumber;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user