address comments
This commit is contained in:
parent
1f5a0987cb
commit
48ecd32d5d
@ -1,5 +1,5 @@
|
|||||||
import { BaseContract } from '@0x/base-contract';
|
import { BaseContract } from '@0x/base-contract';
|
||||||
import { expect, TokenBalances } from '@0x/contracts-test-utils';
|
import { constants, expect, TokenBalances } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ export class BalanceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the given token owner in this balance store. The token owner's balane will be
|
* Registers the given token owner in this balance store. The token owner's balance will be
|
||||||
* tracked in subsequent operations.
|
* tracked in subsequent operations.
|
||||||
* @param address Address of the token owner
|
* @param address Address of the token owner
|
||||||
* @param name Name of the token owner
|
* @param name Name of the token owner
|
||||||
@ -83,8 +83,8 @@ export class BalanceStore {
|
|||||||
*/
|
*/
|
||||||
private _assertEthBalancesEqual(rhs: BalanceStore): void {
|
private _assertEthBalancesEqual(rhs: BalanceStore): void {
|
||||||
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
||||||
const thisBalance = _.get(this.balances.eth, [ownerAddress], new BigNumber(0));
|
const thisBalance = _.get(this.balances.eth, [ownerAddress], constants.ZERO_AMOUNT);
|
||||||
const rhsBalance = _.get(rhs.balances.eth, [ownerAddress], new BigNumber(0));
|
const rhsBalance = _.get(rhs.balances.eth, [ownerAddress], constants.ZERO_AMOUNT);
|
||||||
expect(thisBalance, `${this._readableAddressName(ownerAddress)} ETH balance`).to.bignumber.equal(
|
expect(thisBalance, `${this._readableAddressName(ownerAddress)} ETH balance`).to.bignumber.equal(
|
||||||
rhsBalance,
|
rhsBalance,
|
||||||
);
|
);
|
||||||
@ -98,8 +98,8 @@ export class BalanceStore {
|
|||||||
private _assertErc20BalancesEqual(rhs: BalanceStore): void {
|
private _assertErc20BalancesEqual(rhs: BalanceStore): void {
|
||||||
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
||||||
for (const tokenAddress of [...this._tokenAddresses.erc20, ...rhs._tokenAddresses.erc20]) {
|
for (const tokenAddress of [...this._tokenAddresses.erc20, ...rhs._tokenAddresses.erc20]) {
|
||||||
const thisBalance = _.get(this.balances.erc20, [ownerAddress, tokenAddress], new BigNumber(0));
|
const thisBalance = _.get(this.balances.erc20, [ownerAddress, tokenAddress], constants.ZERO_AMOUNT);
|
||||||
const rhsBalance = _.get(rhs.balances.erc20, [ownerAddress, tokenAddress], new BigNumber(0));
|
const rhsBalance = _.get(rhs.balances.erc20, [ownerAddress, tokenAddress], constants.ZERO_AMOUNT);
|
||||||
expect(
|
expect(
|
||||||
thisBalance,
|
thisBalance,
|
||||||
`${this._readableAddressName(ownerAddress)} ${this._readableAddressName(tokenAddress)} balance`,
|
`${this._readableAddressName(ownerAddress)} ${this._readableAddressName(tokenAddress)} balance`,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { constants } from '@0x/contracts-staking';
|
import { constants, StakingPoolById } from '@0x/contracts-staking';
|
||||||
import { getRandomInteger } from '@0x/contracts-test-utils';
|
import { getRandomInteger } from '@0x/contracts-test-utils';
|
||||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@ -7,7 +7,6 @@ import {
|
|||||||
validCreateStakingPoolAssertion,
|
validCreateStakingPoolAssertion,
|
||||||
validDecreaseStakingPoolOperatorShareAssertion,
|
validDecreaseStakingPoolOperatorShareAssertion,
|
||||||
} from '../function-assertions';
|
} from '../function-assertions';
|
||||||
import { SimulationEnvironment } from '../simulation/simulation';
|
|
||||||
import { AssertionResult } from '../utils/function_assertions';
|
import { AssertionResult } from '../utils/function_assertions';
|
||||||
|
|
||||||
import { Actor, Constructor } from './base';
|
import { Actor, Constructor } from './base';
|
||||||
@ -39,16 +38,12 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
|||||||
this.actor = (this as any) as Actor;
|
this.actor = (this as any) as Actor;
|
||||||
|
|
||||||
// Register this mixin's assertion generators
|
// Register this mixin's assertion generators
|
||||||
if (this.actor.simulationEnvironment !== undefined) {
|
|
||||||
this.actor.simulationActions = {
|
this.actor.simulationActions = {
|
||||||
...this.actor.simulationActions,
|
...this.actor.simulationActions,
|
||||||
validCreateStakingPool: this._validCreateStakingPool(this.actor.simulationEnvironment),
|
validCreateStakingPool: this._validCreateStakingPool(),
|
||||||
validDecreaseStakingPoolOperatorShare: this._validDecreaseStakingPoolOperatorShare(
|
validDecreaseStakingPoolOperatorShare: this._validDecreaseStakingPoolOperatorShare(),
|
||||||
this.actor.simulationEnvironment,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a staking pool and returns the ID of the new pool.
|
* Creates a staking pool and returns the ID of the new pool.
|
||||||
@ -84,40 +79,29 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getOperatorPoolIds(simulationEnvironment: SimulationEnvironment): string[] {
|
private _getOperatorPoolIds(stakingPools: StakingPoolById): string[] {
|
||||||
const operatorPools = _.pickBy(
|
const operatorPools = _.pickBy(stakingPools, pool => pool.operator === this.actor.address);
|
||||||
simulationEnvironment.stakingPools,
|
|
||||||
pool => pool.operator === this.actor.address,
|
|
||||||
);
|
|
||||||
return Object.keys(operatorPools);
|
return Object.keys(operatorPools);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *_validCreateStakingPool(
|
private async *_validCreateStakingPool(): AsyncIterableIterator<AssertionResult> {
|
||||||
simulationEnvironment: SimulationEnvironment,
|
const { stakingPools } = this.actor.simulationEnvironment!;
|
||||||
): AsyncIterableIterator<AssertionResult> {
|
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
|
||||||
const assertion = validCreateStakingPoolAssertion(
|
|
||||||
this.actor.deployment,
|
|
||||||
simulationEnvironment.stakingPools,
|
|
||||||
);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const operatorShare = getRandomInteger(0, constants.PPM);
|
const operatorShare = getRandomInteger(0, constants.PPM);
|
||||||
yield assertion.executeAsync(operatorShare, false, { from: this.actor.address });
|
yield assertion.executeAsync(operatorShare, false, { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *_validDecreaseStakingPoolOperatorShare(
|
private async *_validDecreaseStakingPoolOperatorShare(): AsyncIterableIterator<AssertionResult | void> {
|
||||||
simulationEnvironment: SimulationEnvironment,
|
const { stakingPools } = this.actor.simulationEnvironment!;
|
||||||
): AsyncIterableIterator<AssertionResult | void> {
|
const assertion = validDecreaseStakingPoolOperatorShareAssertion(this.actor.deployment, stakingPools);
|
||||||
const assertion = validDecreaseStakingPoolOperatorShareAssertion(
|
|
||||||
this.actor.deployment,
|
|
||||||
simulationEnvironment.stakingPools,
|
|
||||||
);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const poolId = _.sample(this._getOperatorPoolIds(simulationEnvironment));
|
const poolId = _.sample(this._getOperatorPoolIds(stakingPools));
|
||||||
if (poolId === undefined) {
|
if (poolId === undefined) {
|
||||||
yield undefined;
|
yield undefined;
|
||||||
} else {
|
} else {
|
||||||
const operatorShare = getRandomInteger(0, simulationEnvironment.stakingPools[poolId].operatorShare);
|
const operatorShare = getRandomInteger(0, stakingPools[poolId].operatorShare);
|
||||||
yield assertion.executeAsync(poolId, operatorShare, { from: this.actor.address });
|
yield assertion.executeAsync(poolId, operatorShare, { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { validMoveStakeAssertion, validStakeAssertion, validUnstakeAssertion } from '../function-assertions';
|
import { validMoveStakeAssertion, validStakeAssertion, validUnstakeAssertion } from '../function-assertions';
|
||||||
import { SimulationEnvironment } from '../simulation/simulation';
|
|
||||||
import { AssertionResult } from '../utils/function_assertions';
|
import { AssertionResult } from '../utils/function_assertions';
|
||||||
|
|
||||||
import { Actor, Constructor } from './base';
|
import { Actor, Constructor } from './base';
|
||||||
@ -37,15 +36,13 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Register this mixin's assertion generators
|
// Register this mixin's assertion generators
|
||||||
if (this.actor.simulationEnvironment !== undefined) {
|
|
||||||
this.actor.simulationActions = {
|
this.actor.simulationActions = {
|
||||||
...this.actor.simulationActions,
|
...this.actor.simulationActions,
|
||||||
validStake: this._validStake(this.actor.simulationEnvironment),
|
validStake: this._validStake(),
|
||||||
validUnstake: this._validUnstake(this.actor.simulationEnvironment),
|
validUnstake: this._validUnstake(),
|
||||||
validMoveStake: this._validMoveStake(this.actor.simulationEnvironment),
|
validMoveStake: this._validMoveStake(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stakes the given amount of ZRX. If `poolId` is provided, subsequently delegates the newly
|
* Stakes the given amount of ZRX. If `poolId` is provided, subsequently delegates the newly
|
||||||
@ -66,30 +63,26 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *_validStake(
|
private async *_validStake(): AsyncIterableIterator<AssertionResult> {
|
||||||
simulationEnvironment: SimulationEnvironment,
|
|
||||||
): AsyncIterableIterator<AssertionResult> {
|
|
||||||
const { zrx } = this.actor.deployment.tokens;
|
const { zrx } = this.actor.deployment.tokens;
|
||||||
const { deployment, balanceStore, globalStake } = simulationEnvironment;
|
const { deployment, balanceStore, globalStake } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validStakeAssertion(deployment, balanceStore, globalStake, this.stake);
|
const assertion = validStakeAssertion(deployment, balanceStore, globalStake, this.stake);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
await simulationEnvironment.balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
const zrxBalance = simulationEnvironment.balanceStore.balances.erc20[this.actor.address][zrx.address];
|
const zrxBalance = balanceStore.balances.erc20[this.actor.address][zrx.address];
|
||||||
const amount = getRandomInteger(0, zrxBalance);
|
const amount = getRandomInteger(0, zrxBalance);
|
||||||
yield assertion.executeAsync(amount, { from: this.actor.address });
|
yield assertion.executeAsync(amount, { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *_validUnstake(
|
private async *_validUnstake(): AsyncIterableIterator<AssertionResult> {
|
||||||
simulationEnvironment: SimulationEnvironment,
|
|
||||||
): AsyncIterableIterator<AssertionResult> {
|
|
||||||
const { stakingWrapper } = this.actor.deployment.staking;
|
const { stakingWrapper } = this.actor.deployment.staking;
|
||||||
const { deployment, balanceStore, globalStake } = simulationEnvironment;
|
const { deployment, balanceStore, globalStake } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validUnstakeAssertion(deployment, balanceStore, globalStake, this.stake);
|
const assertion = validUnstakeAssertion(deployment, balanceStore, globalStake, this.stake);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
await simulationEnvironment.balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
const undelegatedStake = await stakingWrapper.getOwnerStakeByStatus.callAsync(
|
const undelegatedStake = await stakingWrapper.getOwnerStakeByStatus.callAsync(
|
||||||
this.actor.address,
|
this.actor.address,
|
||||||
StakeStatus.Undelegated,
|
StakeStatus.Undelegated,
|
||||||
@ -103,16 +96,9 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *_validMoveStake(
|
private async *_validMoveStake(): AsyncIterableIterator<AssertionResult> {
|
||||||
simulationEnvironment: SimulationEnvironment,
|
const { deployment, globalStake, stakingPools } = this.actor.simulationEnvironment!;
|
||||||
): AsyncIterableIterator<AssertionResult> {
|
const assertion = validMoveStakeAssertion(deployment, globalStake, this.stake, stakingPools);
|
||||||
const { deployment, globalStake } = simulationEnvironment;
|
|
||||||
const assertion = validMoveStakeAssertion(
|
|
||||||
deployment,
|
|
||||||
globalStake,
|
|
||||||
this.stake,
|
|
||||||
simulationEnvironment.stakingPools,
|
|
||||||
);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const fromPoolId = _.sample(Object.keys(_.omit(this.stake[StakeStatus.Delegated], ['total'])));
|
const fromPoolId = _.sample(Object.keys(_.omit(this.stake[StakeStatus.Delegated], ['total'])));
|
||||||
@ -122,7 +108,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
: (_.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
: (_.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||||
const from = new StakeInfo(fromStatus, fromPoolId);
|
const from = new StakeInfo(fromStatus, fromPoolId);
|
||||||
|
|
||||||
const toPoolId = _.sample(Object.keys(simulationEnvironment.stakingPools));
|
const toPoolId = _.sample(Object.keys(stakingPools));
|
||||||
const toStatus =
|
const toStatus =
|
||||||
toPoolId === undefined
|
toPoolId === undefined
|
||||||
? StakeStatus.Undelegated
|
? StakeStatus.Undelegated
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": ["@0x/tslint-config"],
|
"extends": ["@0x/tslint-config"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"max-classes-per-file": false
|
"max-classes-per-file": false,
|
||||||
|
"no-non-null-assertion": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,10 @@ export function validCreateStakingPoolAssertion(
|
|||||||
const { stakingWrapper } = deployment.staking;
|
const { stakingWrapper } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion(stakingWrapper.createStakingPool, {
|
return new FunctionAssertion(stakingWrapper.createStakingPool, {
|
||||||
|
// Returns the expected ID of th created pool
|
||||||
before: async () => {
|
before: async () => {
|
||||||
const lastPoolId = await stakingWrapper.lastPoolId.callAsync();
|
const lastPoolId = await stakingWrapper.lastPoolId.callAsync();
|
||||||
|
// Effectively the last poolId + 1, but as a bytestring
|
||||||
return `0x${new BigNumber(lastPoolId)
|
return `0x${new BigNumber(lastPoolId)
|
||||||
.plus(1)
|
.plus(1)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
@ -35,9 +37,12 @@ export function validCreateStakingPoolAssertion(
|
|||||||
) => {
|
) => {
|
||||||
logUtils.log(`createStakingPool(${operatorShare}, ${addOperatorAsMaker}) => ${expectedPoolId}`);
|
logUtils.log(`createStakingPool(${operatorShare}, ${addOperatorAsMaker}) => ${expectedPoolId}`);
|
||||||
|
|
||||||
|
// Checks the logs for the new poolId, verifies that it is as expected
|
||||||
const log = result.receipt!.logs[0]; // tslint:disable-line:no-non-null-assertion
|
const log = result.receipt!.logs[0]; // tslint:disable-line:no-non-null-assertion
|
||||||
const actualPoolId = (log as any).args.poolId;
|
const actualPoolId = (log as any).args.poolId;
|
||||||
expect(actualPoolId).to.equal(expectedPoolId);
|
expect(actualPoolId).to.equal(expectedPoolId);
|
||||||
|
|
||||||
|
// Adds the new pool to local state
|
||||||
pools[actualPoolId] = {
|
pools[actualPoolId] = {
|
||||||
operator: txData.from as string,
|
operator: txData.from as string,
|
||||||
operatorShare,
|
operatorShare,
|
||||||
|
@ -19,8 +19,10 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
|
|||||||
after: async (_beforeInfo, _result: FunctionResult, poolId: string, expectedOperatorShare: number) => {
|
after: async (_beforeInfo, _result: FunctionResult, poolId: string, expectedOperatorShare: number) => {
|
||||||
logUtils.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`);
|
logUtils.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`);
|
||||||
|
|
||||||
|
// Checks that the on-chain pool's operator share has been updated.
|
||||||
const { operatorShare } = await stakingWrapper.getStakingPool.callAsync(poolId);
|
const { operatorShare } = await stakingWrapper.getStakingPool.callAsync(poolId);
|
||||||
expect(operatorShare).to.bignumber.equal(expectedOperatorShare);
|
expect(operatorShare).to.bignumber.equal(expectedOperatorShare);
|
||||||
|
// Updates the pool in local state.
|
||||||
pools[poolId].operatorShare = operatorShare;
|
pools[poolId].operatorShare = operatorShare;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -22,6 +22,56 @@ function decrementNextEpochBalance(stakeBalance: StoredBalance, amount: BigNumbe
|
|||||||
_.update(stakeBalance, ['nextEpochBalance'], balance => (balance || constants.ZERO_AMOUNT).minus(amount));
|
_.update(stakeBalance, ['nextEpochBalance'], balance => (balance || constants.ZERO_AMOUNT).minus(amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateNextEpochBalances(
|
||||||
|
globalStake: GlobalStakeByStatus,
|
||||||
|
ownerStake: OwnerStakeByStatus,
|
||||||
|
pools: StakingPoolById,
|
||||||
|
from: StakeInfo,
|
||||||
|
to: StakeInfo,
|
||||||
|
amount: BigNumber,
|
||||||
|
): string[] {
|
||||||
|
// The on-chain state of these updated pools will be verified in the `after` of the assertion.
|
||||||
|
const updatedPools = [];
|
||||||
|
|
||||||
|
// Decrement next epoch balances associated with the `from` stake
|
||||||
|
if (from.status === StakeStatus.Undelegated) {
|
||||||
|
// Decrement owner undelegated stake
|
||||||
|
decrementNextEpochBalance(ownerStake[StakeStatus.Undelegated], amount);
|
||||||
|
// Decrement global undelegated stake
|
||||||
|
decrementNextEpochBalance(globalStake[StakeStatus.Undelegated], amount);
|
||||||
|
} else if (from.status === StakeStatus.Delegated) {
|
||||||
|
// Decrement owner's delegated stake to this pool
|
||||||
|
decrementNextEpochBalance(ownerStake[StakeStatus.Delegated][from.poolId], amount);
|
||||||
|
// Decrement owner's total delegated stake
|
||||||
|
decrementNextEpochBalance(ownerStake[StakeStatus.Delegated].total, amount);
|
||||||
|
// Decrement global delegated stake
|
||||||
|
decrementNextEpochBalance(globalStake[StakeStatus.Delegated], amount);
|
||||||
|
// Decrement pool's delegated stake
|
||||||
|
decrementNextEpochBalance(pools[from.poolId].delegatedStake, amount);
|
||||||
|
updatedPools.push(from.poolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment next epoch balances associated with the `to` stake
|
||||||
|
if (to.status === StakeStatus.Undelegated) {
|
||||||
|
incrementNextEpochBalance(ownerStake[StakeStatus.Undelegated], amount);
|
||||||
|
incrementNextEpochBalance(globalStake[StakeStatus.Undelegated], amount);
|
||||||
|
} else if (to.status === StakeStatus.Delegated) {
|
||||||
|
// Initializes the balance for this pool if the user has not previously delegated to it
|
||||||
|
_.defaults(ownerStake[StakeStatus.Delegated], {
|
||||||
|
[to.poolId]: new StoredBalance(),
|
||||||
|
});
|
||||||
|
// Increment owner's delegated stake to this pool
|
||||||
|
incrementNextEpochBalance(ownerStake[StakeStatus.Delegated][to.poolId], amount);
|
||||||
|
// Increment owner's total delegated stake
|
||||||
|
incrementNextEpochBalance(ownerStake[StakeStatus.Delegated].total, amount);
|
||||||
|
// Increment global delegated stake
|
||||||
|
incrementNextEpochBalance(globalStake[StakeStatus.Delegated], amount);
|
||||||
|
// Increment pool's delegated stake
|
||||||
|
incrementNextEpochBalance(pools[to.poolId].delegatedStake, amount);
|
||||||
|
updatedPools.push(to.poolId);
|
||||||
|
}
|
||||||
|
return updatedPools;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Returns a FunctionAssertion for `moveStake` which assumes valid input is provided. The
|
* Returns a FunctionAssertion for `moveStake` which assumes valid input is provided. The
|
||||||
* FunctionAssertion checks that the staker's
|
* FunctionAssertion checks that the staker's
|
||||||
@ -50,35 +100,11 @@ export function validMoveStakeAssertion(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const owner = txData.from as string;
|
const owner = txData.from as string;
|
||||||
const updatedPools = [];
|
|
||||||
if (from.status === StakeStatus.Undelegated) {
|
|
||||||
decrementNextEpochBalance(ownerStake[StakeStatus.Undelegated], amount);
|
|
||||||
decrementNextEpochBalance(globalStake[StakeStatus.Undelegated], amount);
|
|
||||||
} else if (from.status === StakeStatus.Delegated) {
|
|
||||||
_.defaults(ownerStake[StakeStatus.Delegated], {
|
|
||||||
[from.poolId]: new StoredBalance(),
|
|
||||||
});
|
|
||||||
decrementNextEpochBalance(ownerStake[StakeStatus.Delegated][from.poolId], amount);
|
|
||||||
decrementNextEpochBalance(ownerStake[StakeStatus.Delegated].total, amount);
|
|
||||||
decrementNextEpochBalance(globalStake[StakeStatus.Delegated], amount);
|
|
||||||
decrementNextEpochBalance(pools[from.poolId].delegatedStake, amount);
|
|
||||||
updatedPools.push(from.poolId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to.status === StakeStatus.Undelegated) {
|
// Update local balances to match the expected result of this `moveStake` operation
|
||||||
incrementNextEpochBalance(ownerStake[StakeStatus.Undelegated], amount);
|
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
|
||||||
incrementNextEpochBalance(globalStake[StakeStatus.Undelegated], amount);
|
|
||||||
} else if (to.status === StakeStatus.Delegated) {
|
|
||||||
_.defaults(ownerStake[StakeStatus.Delegated], {
|
|
||||||
[to.poolId]: new StoredBalance(),
|
|
||||||
});
|
|
||||||
incrementNextEpochBalance(ownerStake[StakeStatus.Delegated][to.poolId], amount);
|
|
||||||
incrementNextEpochBalance(ownerStake[StakeStatus.Delegated].total, amount);
|
|
||||||
incrementNextEpochBalance(globalStake[StakeStatus.Delegated], amount);
|
|
||||||
incrementNextEpochBalance(pools[to.poolId].delegatedStake, amount);
|
|
||||||
updatedPools.push(to.poolId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Fetches on-chain owner stake balances and checks against local balances
|
||||||
const ownerUndelegatedStake = {
|
const ownerUndelegatedStake = {
|
||||||
...new StoredBalance(),
|
...new StoredBalance(),
|
||||||
...(await stakingWrapper.getOwnerStakeByStatus.callAsync(owner, StakeStatus.Undelegated)),
|
...(await stakingWrapper.getOwnerStakeByStatus.callAsync(owner, StakeStatus.Undelegated)),
|
||||||
@ -90,6 +116,7 @@ export function validMoveStakeAssertion(
|
|||||||
expect(ownerUndelegatedStake).to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
expect(ownerUndelegatedStake).to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
||||||
expect(ownerDelegatedStake).to.deep.equal(ownerStake[StakeStatus.Delegated].total);
|
expect(ownerDelegatedStake).to.deep.equal(ownerStake[StakeStatus.Delegated].total);
|
||||||
|
|
||||||
|
// Fetches on-chain global stake balances and checks against local balances
|
||||||
const globalUndelegatedStake = await stakingWrapper.getGlobalStakeByStatus.callAsync(
|
const globalUndelegatedStake = await stakingWrapper.getGlobalStakeByStatus.callAsync(
|
||||||
StakeStatus.Undelegated,
|
StakeStatus.Undelegated,
|
||||||
);
|
);
|
||||||
@ -97,6 +124,7 @@ export function validMoveStakeAssertion(
|
|||||||
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
|
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
|
||||||
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
|
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
|
||||||
|
|
||||||
|
// Fetches on-chain pool stake balances and checks against local balances
|
||||||
for (const poolId of updatedPools) {
|
for (const poolId of updatedPools) {
|
||||||
const stakeDelegatedByOwner = await stakingWrapper.getStakeDelegatedToPoolByOwner.callAsync(
|
const stakeDelegatedByOwner = await stakingWrapper.getStakeDelegatedToPoolByOwner.callAsync(
|
||||||
owner,
|
owner,
|
||||||
|
@ -35,6 +35,7 @@ export function validStakeAssertion(
|
|||||||
|
|
||||||
return new FunctionAssertion(stakingWrapper.stake, {
|
return new FunctionAssertion(stakingWrapper.stake, {
|
||||||
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
||||||
|
// Simulates the transfer of ZRX from staker to vault
|
||||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||||
expectedBalances.transferAsset(
|
expectedBalances.transferAsset(
|
||||||
txData.from as string,
|
txData.from as string,
|
||||||
@ -52,22 +53,27 @@ export function validStakeAssertion(
|
|||||||
) => {
|
) => {
|
||||||
logUtils.log(`stake(${amount})`);
|
logUtils.log(`stake(${amount})`);
|
||||||
|
|
||||||
|
// Checks that the ZRX transfer updated balances as expected.
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
|
// Checks that the owner's undelegated stake has increased by the stake amount
|
||||||
const ownerUndelegatedStake = await stakingWrapper.getOwnerStakeByStatus.callAsync(
|
const ownerUndelegatedStake = await stakingWrapper.getOwnerStakeByStatus.callAsync(
|
||||||
txData.from as string,
|
txData.from as string,
|
||||||
StakeStatus.Undelegated,
|
StakeStatus.Undelegated,
|
||||||
);
|
);
|
||||||
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
|
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
|
||||||
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
|
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
|
||||||
|
// Updates local state accordingly
|
||||||
ownerStake[StakeStatus.Undelegated] = expectedOwnerUndelegatedStake;
|
ownerStake[StakeStatus.Undelegated] = expectedOwnerUndelegatedStake;
|
||||||
|
|
||||||
|
// Checks that the global undelegated stake has also increased by the stake amount
|
||||||
const globalUndelegatedStake = await stakingWrapper.getGlobalStakeByStatus.callAsync(
|
const globalUndelegatedStake = await stakingWrapper.getGlobalStakeByStatus.callAsync(
|
||||||
StakeStatus.Undelegated,
|
StakeStatus.Undelegated,
|
||||||
);
|
);
|
||||||
const expectedGlobalUndelegatedStake = expectedUndelegatedStake(globalStake, amount);
|
const expectedGlobalUndelegatedStake = expectedUndelegatedStake(globalStake, amount);
|
||||||
expect(globalUndelegatedStake, 'Global undelegated stake').to.deep.equal(expectedGlobalUndelegatedStake);
|
expect(globalUndelegatedStake, 'Global undelegated stake').to.deep.equal(expectedGlobalUndelegatedStake);
|
||||||
|
// Updates local state accordingly
|
||||||
globalStake[StakeStatus.Undelegated] = expectedGlobalUndelegatedStake;
|
globalStake[StakeStatus.Undelegated] = expectedGlobalUndelegatedStake;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -35,6 +35,7 @@ export function validUnstakeAssertion(
|
|||||||
|
|
||||||
return new FunctionAssertion(stakingWrapper.unstake, {
|
return new FunctionAssertion(stakingWrapper.unstake, {
|
||||||
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
||||||
|
// Simulates the transfer of ZRX from vault to staker
|
||||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||||
expectedBalances.transferAsset(
|
expectedBalances.transferAsset(
|
||||||
zrxVault.address,
|
zrxVault.address,
|
||||||
@ -52,22 +53,27 @@ export function validUnstakeAssertion(
|
|||||||
) => {
|
) => {
|
||||||
logUtils.log(`unstake(${amount})`);
|
logUtils.log(`unstake(${amount})`);
|
||||||
|
|
||||||
|
// Checks that the ZRX transfer updated balances as expected.
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
|
// Checks that the owner's undelegated stake has decreased by the stake amount
|
||||||
const ownerUndelegatedStake = await stakingWrapper.getOwnerStakeByStatus.callAsync(
|
const ownerUndelegatedStake = await stakingWrapper.getOwnerStakeByStatus.callAsync(
|
||||||
txData.from as string,
|
txData.from as string,
|
||||||
StakeStatus.Undelegated,
|
StakeStatus.Undelegated,
|
||||||
);
|
);
|
||||||
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
|
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
|
||||||
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
|
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
|
||||||
|
// Updates local state accordingly
|
||||||
ownerStake[StakeStatus.Undelegated] = expectedOwnerUndelegatedStake;
|
ownerStake[StakeStatus.Undelegated] = expectedOwnerUndelegatedStake;
|
||||||
|
|
||||||
|
// Checks that the global undelegated stake has also decreased by the stake amount
|
||||||
const globalUndelegatedStake = await stakingWrapper.getGlobalStakeByStatus.callAsync(
|
const globalUndelegatedStake = await stakingWrapper.getGlobalStakeByStatus.callAsync(
|
||||||
StakeStatus.Undelegated,
|
StakeStatus.Undelegated,
|
||||||
);
|
);
|
||||||
const expectedGlobalUndelegatedStake = expectedUndelegatedStake(globalStake, amount);
|
const expectedGlobalUndelegatedStake = expectedUndelegatedStake(globalStake, amount);
|
||||||
expect(globalUndelegatedStake, 'Global undelegated stake').to.deep.equal(expectedGlobalUndelegatedStake);
|
expect(globalUndelegatedStake, 'Global undelegated stake').to.deep.equal(expectedGlobalUndelegatedStake);
|
||||||
|
// Updates local state accordingly
|
||||||
globalStake[StakeStatus.Undelegated] = expectedGlobalUndelegatedStake;
|
globalStake[StakeStatus.Undelegated] = expectedGlobalUndelegatedStake;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
ExchangeFillEventArgs,
|
ExchangeFillEventArgs,
|
||||||
LocalBalanceStore,
|
LocalBalanceStore,
|
||||||
} from '@0x/contracts-exchange';
|
} from '@0x/contracts-exchange';
|
||||||
|
import { ReferenceFunctions } from '@0x/contracts-exchange-libs';
|
||||||
import {
|
import {
|
||||||
constants as stakingConstants,
|
constants as stakingConstants,
|
||||||
IStakingEventsEpochEndedEventArgs,
|
IStakingEventsEpochEndedEventArgs,
|
||||||
@ -284,7 +285,11 @@ blockchainTests.resets('fillOrder integration tests', env => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// The rewards are split between the operator and delegator based on the pool's operatorShare
|
// The rewards are split between the operator and delegator based on the pool's operatorShare
|
||||||
const operatorReward = rewardsAvailable.times(operatorShare).dividedToIntegerBy(constants.PPM_DENOMINATOR);
|
const operatorReward = ReferenceFunctions.getPartialAmountFloor(
|
||||||
|
new BigNumber(operatorShare),
|
||||||
|
new BigNumber(constants.PPM_DENOMINATOR),
|
||||||
|
rewardsAvailable,
|
||||||
|
);
|
||||||
const delegatorReward = rewardsAvailable.minus(operatorReward);
|
const delegatorReward = rewardsAvailable.minus(operatorReward);
|
||||||
|
|
||||||
// Finalize the pool. This should automatically pay the operator in WETH.
|
// Finalize the pool. This should automatically pay the operator in WETH.
|
||||||
@ -363,7 +368,11 @@ blockchainTests.resets('fillOrder integration tests', env => {
|
|||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
// The rewards are split between the operator and delegator based on the pool's operatorShare
|
// The rewards are split between the operator and delegator based on the pool's operatorShare
|
||||||
const operatorReward = rewardsAvailable.times(operatorShare).dividedToIntegerBy(constants.PPM_DENOMINATOR);
|
const operatorReward = ReferenceFunctions.getPartialAmountFloor(
|
||||||
|
new BigNumber(operatorShare),
|
||||||
|
new BigNumber(constants.PPM_DENOMINATOR),
|
||||||
|
rewardsAvailable,
|
||||||
|
);
|
||||||
|
|
||||||
// Finalize the pool. This should automatically pay the operator in WETH.
|
// Finalize the pool. This should automatically pay the operator in WETH.
|
||||||
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
|
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user