From 3c7a0bcd8558880a63acf882f5a3e283ac87c104 Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Mon, 4 Nov 2019 17:38:28 -0800 Subject: [PATCH] add createStakingPool and decreaseStakingPoolOperatorShare --- contracts/integrations/test/actors/base.ts | 2 +- .../integrations/test/actors/pool_operator.ts | 39 +++++++++++++++++++ contracts/integrations/test/actors/staker.ts | 2 +- .../function-assertions/createStakingPool.ts | 36 +++++++++++++++++ .../decreaseStakingPoolOperatorShare.ts | 22 +++++++++++ .../test/function-assertions/index.ts | 2 + .../simulation/pool_management_fuzz_test.ts | 13 +++++-- .../test/simulation/simulation.ts | 1 + 8 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 contracts/integrations/test/function-assertions/createStakingPool.ts create mode 100644 contracts/integrations/test/function-assertions/decreaseStakingPoolOperatorShare.ts diff --git a/contracts/integrations/test/actors/base.ts b/contracts/integrations/test/actors/base.ts index c32d9033e8..128aedc854 100644 --- a/contracts/integrations/test/actors/base.ts +++ b/contracts/integrations/test/actors/base.ts @@ -26,7 +26,7 @@ export class Actor { public readonly deployment: DeploymentManager; public readonly simulation?: Simulation; public simulationActions: { - [action: string]: (...args: any[]) => Promise>; + [action: string]: (...args: any[]) => Promise>; } = {}; protected readonly _transactionFactory: TransactionFactory; diff --git a/contracts/integrations/test/actors/pool_operator.ts b/contracts/integrations/test/actors/pool_operator.ts index 22763f76ba..30189c7348 100644 --- a/contracts/integrations/test/actors/pool_operator.ts +++ b/contracts/integrations/test/actors/pool_operator.ts @@ -1,4 +1,13 @@ +import { constants } from '@0x/contracts-staking'; +import { getRandomInteger } from '@0x/contracts-test-utils'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { + validCreateStakingPoolAssertion, + validDecreaseStakingPoolOperatorShareAssertion, +} from '../function-assertions'; +import { AssertionResult } from '../utils/function_assertions'; import { Actor, Constructor } from './base'; @@ -33,6 +42,15 @@ export function PoolOperatorMixin(Base: TBase): TBase // tslint:disable-next-line:no-inferred-empty-object-type super(...args); this.actor = (this as any) as Actor; + + // Register this mixin's assertion generators + if (this.actor.simulation !== undefined) { + this.actor.simulationActions = { + ...this.actor.simulationActions, + validCreateStakingPool: this._validCreateStakingPool().next, + validDecreaseStakingPoolOperatorShare: this._validDecreaseStakingPoolOperatorShare().next, + }; + } } /** @@ -70,6 +88,27 @@ export function PoolOperatorMixin(Base: TBase): TBase { from: this.actor.address }, ); } + + private async *_validCreateStakingPool(): AsyncIterableIterator { + const assertion = validCreateStakingPoolAssertion(this.actor.deployment, this); + while (true) { + const operatorShare = getRandomInteger(0, constants.PPM); + yield assertion.executeAsync(operatorShare, false, { from: this.actor.address }); + } + } + + private async *_validDecreaseStakingPoolOperatorShare(): AsyncIterableIterator { + const assertion = validDecreaseStakingPoolOperatorShareAssertion(this.actor.deployment, this); + while (true) { + const poolId = _.sample(Object.keys(this.operatorShares)); + if (poolId === undefined) { + yield undefined; + } else { + const operatorShare = getRandomInteger(0, this.operatorShares[poolId]); + yield assertion.executeAsync(poolId, operatorShare, { from: this.actor.address }); + } + } + } }; } diff --git a/contracts/integrations/test/actors/staker.ts b/contracts/integrations/test/actors/staker.ts index 2088e64b10..74747e75c0 100644 --- a/contracts/integrations/test/actors/staker.ts +++ b/contracts/integrations/test/actors/staker.ts @@ -3,8 +3,8 @@ import { getRandomInteger } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; import { validStakeAssertion, validUnstakeAssertion } from '../function-assertions'; -import { AssertionResult } from '../utils/function_assertions'; import { Simulation } from '../simulation/simulation'; +import { AssertionResult } from '../utils/function_assertions'; import { Actor, Constructor } from './base'; diff --git a/contracts/integrations/test/function-assertions/createStakingPool.ts b/contracts/integrations/test/function-assertions/createStakingPool.ts new file mode 100644 index 0000000000..d1a176641b --- /dev/null +++ b/contracts/integrations/test/function-assertions/createStakingPool.ts @@ -0,0 +1,36 @@ +import { expect } from '@0x/contracts-test-utils'; +import { BigNumber } from '@0x/utils'; + +import { DeploymentManager } from '../utils/deployment_manager'; +import { FunctionAssertion, FunctionResult } from '../utils/function_assertions'; + +export function validCreateStakingPoolAssertion( + deployment: DeploymentManager, + context?: any, +): FunctionAssertion { + const { stakingWrapper } = deployment.staking; + + return new FunctionAssertion(stakingWrapper.createStakingPool, { + before: async () => { + const lastPoolId = await stakingWrapper.lastPoolId.callAsync(); + return `0x${new BigNumber(lastPoolId) + .plus(1) + .toString(16) + .padStart(64, '0')}`; + }, + after: async ( + expectedPoolId: string, + result: FunctionResult, + operatorShare: number, + addOperatorAsMaker: boolean, + ) => { + const log = result.receipt!.logs[0]; + const actualPoolId = (log as any).args.poolId; + expect(actualPoolId).to.equal(expectedPoolId); + console.log(`createStakingPool(${operatorShare}, ${addOperatorAsMaker}) => ${actualPoolId}`); + if (context !== undefined) { + context.operatorShares[actualPoolId] = operatorShare; + } + }, + }); +} diff --git a/contracts/integrations/test/function-assertions/decreaseStakingPoolOperatorShare.ts b/contracts/integrations/test/function-assertions/decreaseStakingPoolOperatorShare.ts new file mode 100644 index 0000000000..d5c005f609 --- /dev/null +++ b/contracts/integrations/test/function-assertions/decreaseStakingPoolOperatorShare.ts @@ -0,0 +1,22 @@ +import { expect } from '@0x/contracts-test-utils'; + +import { DeploymentManager } from '../utils/deployment_manager'; +import { FunctionAssertion, FunctionResult } from '../utils/function_assertions'; + +export function validDecreaseStakingPoolOperatorShareAssertion( + deployment: DeploymentManager, + context?: any, +): FunctionAssertion<{}> { + const { stakingWrapper } = deployment.staking; + + return new FunctionAssertion(stakingWrapper.decreaseStakingPoolOperatorShare, { + after: async (_beforeInfo, _result: FunctionResult, poolId: string, expectedOperatorShare: number) => { + const { operatorShare } = await stakingWrapper.getStakingPool.callAsync(poolId); + expect(operatorShare).to.bignumber.equal(expectedOperatorShare); + console.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`); + if (context !== undefined) { + context.operatorShares[poolId] = operatorShare; + } + }, + }); +} diff --git a/contracts/integrations/test/function-assertions/index.ts b/contracts/integrations/test/function-assertions/index.ts index f8e684eaa1..2603ccff87 100644 --- a/contracts/integrations/test/function-assertions/index.ts +++ b/contracts/integrations/test/function-assertions/index.ts @@ -1,2 +1,4 @@ export * from './stake'; export * from './unstake'; +export * from './createStakingPool'; +export * from './decreaseStakingPoolOperatorShare'; diff --git a/contracts/integrations/test/simulation/pool_management_fuzz_test.ts b/contracts/integrations/test/simulation/pool_management_fuzz_test.ts index 47e4d703a7..91e03c4c23 100644 --- a/contracts/integrations/test/simulation/pool_management_fuzz_test.ts +++ b/contracts/integrations/test/simulation/pool_management_fuzz_test.ts @@ -2,7 +2,7 @@ import { BlockchainBalanceStore } from '@0x/contracts-exchange'; import { blockchainTests } from '@0x/contracts-test-utils'; import * as _ from 'lodash'; -import { Staker } from '../actors'; +import { PoolOperator, Staker } from '../actors'; import { DeploymentManager } from '../utils/deployment_manager'; import { AssertionResult } from '../utils/function_assertions'; @@ -18,7 +18,14 @@ class PoolManagementSimulation extends Simulation { await staker.configureERC20TokenAsync(this._deployment.tokens.zrx); this.balanceStore.registerTokenOwner(staker.address, staker.name); - const actions = [staker.simulationActions.validStake, staker.simulationActions.validUnstake]; + const operator = new PoolOperator({ name: 'Operator', deployment: this._deployment, simulation: this }); + + const actions = [ + staker.simulationActions.validStake, + staker.simulationActions.validUnstake, + operator.simulationActions.validCreateStakingPool, + operator.simulationActions.validDecreaseStakingPoolOperatorShare, + ]; while (true) { const action = _.sample(actions); await action!(); @@ -26,7 +33,7 @@ class PoolManagementSimulation extends Simulation { } } -blockchainTests.skip('Pool management fuzz test', env => { +blockchainTests.only('Pool management fuzz test', env => { it('fuzz', async () => { const deployment = await DeploymentManager.deployAsync(env, { numErc20TokensToDeploy: 0, diff --git a/contracts/integrations/test/simulation/simulation.ts b/contracts/integrations/test/simulation/simulation.ts index 72934341fc..8643180881 100644 --- a/contracts/integrations/test/simulation/simulation.ts +++ b/contracts/integrations/test/simulation/simulation.ts @@ -5,6 +5,7 @@ import { DeploymentManager } from '../utils/deployment_manager'; import { AssertionResult } from '../utils/function_assertions'; export abstract class Simulation { + public poolIds = []; private readonly _generator = this._assertionGenerator(); protected constructor(