Merge pull request #2372 from 0xProject/feature/fuzz/prng
`@0x/contracts-integrations`: Seeded RNG and simulation logging
This commit is contained in:
commit
1283232144
@ -19,6 +19,7 @@
|
|||||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||||
|
"test:fuzz": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/fuzz_tests/*.js' --timeout 0 --bail --exit",
|
||||||
"compile": "sol-compiler",
|
"compile": "sol-compiler",
|
||||||
"watch": "sol-compiler -w",
|
"watch": "sol-compiler -w",
|
||||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||||
@ -69,6 +70,7 @@
|
|||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
"@types/seedrandom": "^2.4.28",
|
||||||
"chai": "^4.0.1",
|
"chai": "^4.0.1",
|
||||||
"chai-as-promised": "^7.1.0",
|
"chai-as-promised": "^7.1.0",
|
||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
@ -78,6 +80,7 @@
|
|||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
"nock": "^10.0.6",
|
"nock": "^10.0.6",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
"seedrandom": "^3.0.5",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
|
@ -33,7 +33,7 @@ import { AssetProxyId } from '@0x/types';
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
import { AssetProxyDispatcher, Authorizable, Ownable } from './framework/wrapper_interfaces';
|
import { AssetProxyDispatcher, Authorizable, Ownable } from './framework/utils/wrapper_interfaces';
|
||||||
|
|
||||||
// tslint:disable:no-unnecessary-type-assertion
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
blockchainTests('Deployment and Configuration End to End Tests', env => {
|
blockchainTests('Deployment and Configuration End to End Tests', env => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { constants, OrderFactory } from '@0x/contracts-test-utils';
|
import { constants, OrderFactory } from '@0x/contracts-test-utils';
|
||||||
import { Order, SignedOrder } from '@0x/types';
|
import { Order, SignedOrder } from '@0x/types';
|
||||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { AssertionResult } from '../assertions/function_assertion';
|
import { AssertionResult } from '../assertions/function_assertion';
|
||||||
import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool';
|
import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool';
|
||||||
|
import { Pseudorandom } from '../utils/pseudorandom';
|
||||||
|
|
||||||
import { Actor, ActorConfig, Constructor } from './base';
|
import { Actor, ActorConfig, Constructor } from './base';
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
|||||||
const { stakingPools } = this.actor.simulationEnvironment!;
|
const { stakingPools } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validJoinStakingPoolAssertion(this.actor.deployment);
|
const assertion = validJoinStakingPoolAssertion(this.actor.deployment);
|
||||||
while (true) {
|
while (true) {
|
||||||
const poolId = _.sample(Object.keys(stakingPools));
|
const poolId = Pseudorandom.sample(Object.keys(stakingPools));
|
||||||
if (poolId === undefined) {
|
if (poolId === undefined) {
|
||||||
yield undefined;
|
yield undefined;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { constants, StakingPoolById } from '@0x/contracts-staking';
|
import { constants, StakingPoolById } from '@0x/contracts-staking';
|
||||||
import { getRandomInteger } from '@0x/contracts-test-utils';
|
|
||||||
import '@azure/core-asynciterator-polyfill';
|
import '@azure/core-asynciterator-polyfill';
|
||||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@ -7,6 +6,7 @@ import * as _ from 'lodash';
|
|||||||
import { validCreateStakingPoolAssertion } from '../assertions/createStakingPool';
|
import { validCreateStakingPoolAssertion } from '../assertions/createStakingPool';
|
||||||
import { validDecreaseStakingPoolOperatorShareAssertion } from '../assertions/decreaseStakingPoolOperatorShare';
|
import { validDecreaseStakingPoolOperatorShareAssertion } from '../assertions/decreaseStakingPoolOperatorShare';
|
||||||
import { AssertionResult } from '../assertions/function_assertion';
|
import { AssertionResult } from '../assertions/function_assertion';
|
||||||
|
import { Pseudorandom } from '../utils/pseudorandom';
|
||||||
|
|
||||||
import { Actor, Constructor } from './base';
|
import { Actor, Constructor } from './base';
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
|||||||
const { stakingPools } = this.actor.simulationEnvironment!;
|
const { stakingPools } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
|
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
|
||||||
while (true) {
|
while (true) {
|
||||||
const operatorShare = getRandomInteger(0, constants.PPM).toNumber();
|
const operatorShare = Pseudorandom.integer(constants.PPM).toNumber();
|
||||||
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
|
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,11 +92,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
|||||||
const { stakingPools } = this.actor.simulationEnvironment!;
|
const { stakingPools } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validDecreaseStakingPoolOperatorShareAssertion(this.actor.deployment, stakingPools);
|
const assertion = validDecreaseStakingPoolOperatorShareAssertion(this.actor.deployment, stakingPools);
|
||||||
while (true) {
|
while (true) {
|
||||||
const poolId = _.sample(this._getOperatorPoolIds(stakingPools));
|
const poolId = Pseudorandom.sample(this._getOperatorPoolIds(stakingPools));
|
||||||
if (poolId === undefined) {
|
if (poolId === undefined) {
|
||||||
yield undefined;
|
yield undefined;
|
||||||
} else {
|
} else {
|
||||||
const operatorShare = getRandomInteger(0, stakingPools[poolId].operatorShare).toNumber();
|
const operatorShare = Pseudorandom.integer(stakingPools[poolId].operatorShare).toNumber();
|
||||||
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
|
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { OwnerStakeByStatus, StakeInfo, StakeStatus, StoredBalance } from '@0x/contracts-staking';
|
import { OwnerStakeByStatus, StakeInfo, StakeStatus, StoredBalance } from '@0x/contracts-staking';
|
||||||
import { getRandomInteger } from '@0x/contracts-test-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import '@azure/core-asynciterator-polyfill';
|
import '@azure/core-asynciterator-polyfill';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@ -8,6 +7,7 @@ import { AssertionResult } from '../assertions/function_assertion';
|
|||||||
import { validMoveStakeAssertion } from '../assertions/moveStake';
|
import { validMoveStakeAssertion } from '../assertions/moveStake';
|
||||||
import { validStakeAssertion } from '../assertions/stake';
|
import { validStakeAssertion } from '../assertions/stake';
|
||||||
import { validUnstakeAssertion } from '../assertions/unstake';
|
import { validUnstakeAssertion } from '../assertions/unstake';
|
||||||
|
import { Pseudorandom } from '../utils/pseudorandom';
|
||||||
|
|
||||||
import { Actor, Constructor } from './base';
|
import { Actor, Constructor } from './base';
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
while (true) {
|
while (true) {
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
const zrxBalance = 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 = Pseudorandom.integer(zrxBalance);
|
||||||
yield assertion.executeAsync([amount], { from: this.actor.address });
|
yield assertion.executeAsync([amount], { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
undelegatedStake.currentEpochBalance,
|
undelegatedStake.currentEpochBalance,
|
||||||
undelegatedStake.nextEpochBalance,
|
undelegatedStake.nextEpochBalance,
|
||||||
);
|
);
|
||||||
const amount = getRandomInteger(0, withdrawableStake);
|
const amount = Pseudorandom.integer(withdrawableStake);
|
||||||
yield assertion.executeAsync([amount], { from: this.actor.address });
|
yield assertion.executeAsync([amount], { from: this.actor.address });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,25 +104,27 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
const assertion = validMoveStakeAssertion(deployment, globalStake, this.stake, stakingPools);
|
const assertion = validMoveStakeAssertion(deployment, globalStake, this.stake, stakingPools);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const fromPoolId = _.sample(Object.keys(_.omit(this.stake[StakeStatus.Delegated], ['total'])));
|
const fromPoolId = Pseudorandom.sample(
|
||||||
|
Object.keys(_.omit(this.stake[StakeStatus.Delegated], ['total'])),
|
||||||
|
);
|
||||||
const fromStatus =
|
const fromStatus =
|
||||||
fromPoolId === undefined
|
fromPoolId === undefined
|
||||||
? StakeStatus.Undelegated
|
? StakeStatus.Undelegated
|
||||||
: (_.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||||
const from = new StakeInfo(fromStatus, fromPoolId);
|
const from = new StakeInfo(fromStatus, fromPoolId);
|
||||||
|
|
||||||
const toPoolId = _.sample(Object.keys(stakingPools));
|
const toPoolId = Pseudorandom.sample(Object.keys(stakingPools));
|
||||||
const toStatus =
|
const toStatus =
|
||||||
toPoolId === undefined
|
toPoolId === undefined
|
||||||
? StakeStatus.Undelegated
|
? StakeStatus.Undelegated
|
||||||
: (_.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||||
const to = new StakeInfo(toStatus, toPoolId);
|
const to = new StakeInfo(toStatus, toPoolId);
|
||||||
|
|
||||||
const moveableStake =
|
const moveableStake =
|
||||||
from.status === StakeStatus.Undelegated
|
from.status === StakeStatus.Undelegated
|
||||||
? this.stake[StakeStatus.Undelegated].nextEpochBalance
|
? this.stake[StakeStatus.Undelegated].nextEpochBalance
|
||||||
: this.stake[StakeStatus.Delegated][from.poolId].nextEpochBalance;
|
: this.stake[StakeStatus.Delegated][from.poolId].nextEpochBalance;
|
||||||
const amount = getRandomInteger(0, moveableStake);
|
const amount = Pseudorandom.integer(moveableStake);
|
||||||
|
|
||||||
yield assertion.executeAsync([from, to, amount], { from: this.actor.address });
|
yield assertion.executeAsync([from, to, amount], { from: this.actor.address });
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { constants, getRandomInteger } from '@0x/contracts-test-utils';
|
import { constants } from '@0x/contracts-test-utils';
|
||||||
import { SignedOrder } from '@0x/types';
|
import { SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { validFillOrderCompleteFillAssertion } from '../assertions/fillOrder';
|
import { validFillOrderCompleteFillAssertion } from '../assertions/fillOrder';
|
||||||
import { AssertionResult } from '../assertions/function_assertion';
|
import { AssertionResult } from '../assertions/function_assertion';
|
||||||
import { DeploymentManager } from '../deployment_manager';
|
import { DeploymentManager } from '../deployment_manager';
|
||||||
|
import { Pseudorandom } from '../utils/pseudorandom';
|
||||||
|
|
||||||
import { Actor, Constructor } from './base';
|
import { Actor, Constructor } from './base';
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
|||||||
const { marketMakers } = this.actor.simulationEnvironment!;
|
const { marketMakers } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validFillOrderCompleteFillAssertion(this.actor.deployment);
|
const assertion = validFillOrderCompleteFillAssertion(this.actor.deployment);
|
||||||
while (true) {
|
while (true) {
|
||||||
const maker = _.sample(marketMakers);
|
const maker = Pseudorandom.sample(marketMakers);
|
||||||
if (maker === undefined) {
|
if (maker === undefined) {
|
||||||
yield undefined;
|
yield undefined;
|
||||||
} else {
|
} else {
|
||||||
@ -82,8 +82,8 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const order = await maker.signOrderAsync({
|
const order = await maker.signOrderAsync({
|
||||||
makerAssetAmount: getRandomInteger(constants.ZERO_AMOUNT, constants.INITIAL_ERC20_BALANCE),
|
makerAssetAmount: Pseudorandom.integer(constants.INITIAL_ERC20_BALANCE),
|
||||||
takerAssetAmount: getRandomInteger(constants.ZERO_AMOUNT, constants.INITIAL_ERC20_BALANCE),
|
takerAssetAmount: Pseudorandom.integer(constants.INITIAL_ERC20_BALANCE),
|
||||||
});
|
});
|
||||||
yield assertion.executeAsync([order, order.takerAssetAmount, order.signature], {
|
yield assertion.executeAsync([order, order.takerAssetAmount, order.signature], {
|
||||||
from: this.actor.address,
|
from: this.actor.address,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { StakingPoolById, StoredBalance } from '@0x/contracts-staking';
|
import { StakingPoolById, StoredBalance } from '@0x/contracts-staking';
|
||||||
import { expect } from '@0x/contracts-test-utils';
|
import { expect } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
import { DeploymentManager } from '../deployment_manager';
|
import { DeploymentManager } from '../deployment_manager';
|
||||||
@ -20,41 +20,36 @@ export function validCreateStakingPoolAssertion(
|
|||||||
): FunctionAssertion<[number, boolean], string, string> {
|
): FunctionAssertion<[number, boolean], string, string> {
|
||||||
const { stakingWrapper } = deployment.staking;
|
const { stakingWrapper } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion<[number, boolean], string, string>(
|
return new FunctionAssertion<[number, boolean], string, string>(stakingWrapper, 'createStakingPool', {
|
||||||
stakingWrapper.createStakingPool.bind(stakingWrapper),
|
// Returns the expected ID of th created pool
|
||||||
{
|
before: async () => {
|
||||||
// Returns the expected ID of th created pool
|
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
|
||||||
before: async () => {
|
// Effectively the last poolId + 1, but as a bytestring
|
||||||
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
|
return `0x${new BigNumber(lastPoolId)
|
||||||
// Effectively the last poolId + 1, but as a bytestring
|
.plus(1)
|
||||||
return `0x${new BigNumber(lastPoolId)
|
.toString(16)
|
||||||
.plus(1)
|
.padStart(64, '0')}`;
|
||||||
.toString(16)
|
|
||||||
.padStart(64, '0')}`;
|
|
||||||
},
|
|
||||||
after: async (
|
|
||||||
expectedPoolId: string,
|
|
||||||
result: FunctionResult,
|
|
||||||
args: [number, boolean],
|
|
||||||
txData: Partial<TxData>,
|
|
||||||
) => {
|
|
||||||
const [operatorShare, shouldAddMakerAsOperator] = args;
|
|
||||||
|
|
||||||
logUtils.log(`createStakingPool(${operatorShare}, ${shouldAddMakerAsOperator}) => ${expectedPoolId}`);
|
|
||||||
|
|
||||||
// Checks the logs for the new poolId, verifies that it is as expected
|
|
||||||
const log = result.receipt!.logs[0];
|
|
||||||
const actualPoolId = (log as any).args.poolId;
|
|
||||||
expect(actualPoolId).to.equal(expectedPoolId);
|
|
||||||
|
|
||||||
// Adds the new pool to local state
|
|
||||||
pools[actualPoolId] = {
|
|
||||||
operator: txData.from!,
|
|
||||||
operatorShare,
|
|
||||||
delegatedStake: new StoredBalance(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
after: async (
|
||||||
|
expectedPoolId: string,
|
||||||
|
result: FunctionResult,
|
||||||
|
args: [number, boolean],
|
||||||
|
txData: Partial<TxData>,
|
||||||
|
) => {
|
||||||
|
const [operatorShare] = args;
|
||||||
|
|
||||||
|
// Checks the logs for the new poolId, verifies that it is as expected
|
||||||
|
const log = result.receipt!.logs[0];
|
||||||
|
const actualPoolId = (log as any).args.poolId;
|
||||||
|
expect(actualPoolId).to.equal(expectedPoolId);
|
||||||
|
|
||||||
|
// Adds the new pool to local state
|
||||||
|
pools[actualPoolId] = {
|
||||||
|
operator: txData.from!,
|
||||||
|
operatorShare,
|
||||||
|
delegatedStake: new StoredBalance(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
/* tslint:enable:no-non-null-assertion*/
|
/* tslint:enable:no-non-null-assertion*/
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { StakingPoolById } from '@0x/contracts-staking';
|
import { StakingPoolById } from '@0x/contracts-staking';
|
||||||
import { expect } from '@0x/contracts-test-utils';
|
import { expect } from '@0x/contracts-test-utils';
|
||||||
import { logUtils } from '@0x/utils';
|
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
import { DeploymentManager } from '../deployment_manager';
|
import { DeploymentManager } from '../deployment_manager';
|
||||||
@ -17,21 +16,16 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
|
|||||||
): FunctionAssertion<[string, number], {}, void> {
|
): FunctionAssertion<[string, number], {}, void> {
|
||||||
const { stakingWrapper } = deployment.staking;
|
const { stakingWrapper } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion<[string, number], {}, void>(
|
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
|
||||||
stakingWrapper.decreaseStakingPoolOperatorShare.bind(stakingWrapper),
|
after: async (_beforeInfo, _result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
|
||||||
{
|
const [poolId, expectedOperatorShare] = args;
|
||||||
after: async (_beforeInfo, _result: FunctionResult, args: [string, number], txData: Partial<TxData>) => {
|
|
||||||
const [poolId, expectedOperatorShare] = args;
|
|
||||||
|
|
||||||
logUtils.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`);
|
// Checks that the on-chain pool's operator share has been updated.
|
||||||
|
const { operatorShare } = await stakingWrapper.getStakingPool(poolId).callAsync();
|
||||||
|
expect(operatorShare).to.bignumber.equal(expectedOperatorShare);
|
||||||
|
|
||||||
// Checks that the on-chain pool's operator share has been updated.
|
// Updates the pool in local state.
|
||||||
const { operatorShare } = await stakingWrapper.getStakingPool(poolId).callAsync();
|
pools[poolId].operatorShare = operatorShare;
|
||||||
expect(operatorShare).to.bignumber.equal(expectedOperatorShare);
|
|
||||||
|
|
||||||
// Updates the pool in local state.
|
|
||||||
pools[poolId].operatorShare = operatorShare;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { ERC20TokenEvents, ERC20TokenTransferEventArgs } from '@0x/contracts-erc
|
|||||||
import { ExchangeEvents, ExchangeFillEventArgs } from '@0x/contracts-exchange';
|
import { ExchangeEvents, ExchangeFillEventArgs } from '@0x/contracts-exchange';
|
||||||
import { constants, expect, orderHashUtils, verifyEvents } from '@0x/contracts-test-utils';
|
import { constants, expect, orderHashUtils, verifyEvents } from '@0x/contracts-test-utils';
|
||||||
import { FillResults, Order } from '@0x/types';
|
import { FillResults, Order } from '@0x/types';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export function validFillOrderCompleteFillAssertion(
|
|||||||
): FunctionAssertion<[Order, BigNumber, string], {}, FillResults> {
|
): FunctionAssertion<[Order, BigNumber, string], {}, FillResults> {
|
||||||
const exchange = deployment.exchange;
|
const exchange = deployment.exchange;
|
||||||
|
|
||||||
return new FunctionAssertion<[Order, BigNumber, string], {}, FillResults>(exchange.fillOrder.bind(exchange), {
|
return new FunctionAssertion<[Order, BigNumber, string], {}, FillResults>(exchange, 'fillOrder', {
|
||||||
after: async (
|
after: async (
|
||||||
_beforeInfo,
|
_beforeInfo,
|
||||||
result: FunctionResult,
|
result: FunctionResult,
|
||||||
@ -89,8 +89,6 @@ export function validFillOrderCompleteFillAssertion(
|
|||||||
// Ensure that the correct events were emitted.
|
// Ensure that the correct events were emitted.
|
||||||
verifyFillEvents(txData.from!, order, result.receipt!, deployment);
|
verifyFillEvents(txData.from!, order, result.receipt!, deployment);
|
||||||
|
|
||||||
logUtils.log(`Order filled by ${txData.from}`);
|
|
||||||
|
|
||||||
// TODO: Add validation for on-chain state (like balances)
|
// TODO: Add validation for on-chain state (like balances)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { ContractFunctionObj, ContractTxFunctionObj } from '@0x/base-contract';
|
import { BaseContract, ContractFunctionObj, ContractTxFunctionObj } from '@0x/base-contract';
|
||||||
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { logger } from '../utils/logger';
|
||||||
|
|
||||||
// tslint:disable:max-classes-per-file
|
// tslint:disable:max-classes-per-file
|
||||||
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
|
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
|
||||||
|
|
||||||
@ -48,29 +50,22 @@ export interface AssertionResult<TBefore = unknown> {
|
|||||||
*/
|
*/
|
||||||
export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> implements Assertion<TArgs> {
|
export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> implements Assertion<TArgs> {
|
||||||
// A condition that will be applied to `wrapperFunction`.
|
// A condition that will be applied to `wrapperFunction`.
|
||||||
public condition: Condition<TArgs, TBefore>;
|
public readonly condition: Condition<TArgs, TBefore>;
|
||||||
|
|
||||||
// The wrapper function that will be wrapped in assertions.
|
|
||||||
public wrapperFunction: (
|
|
||||||
...args: TArgs // tslint:disable-line:trailing-comma
|
|
||||||
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
wrapperFunction: (
|
private readonly _contractWrapper: BaseContract,
|
||||||
...args: TArgs // tslint:disable-line:trailing-comma
|
private readonly _functionName: string,
|
||||||
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>,
|
|
||||||
condition: Partial<Condition<TArgs, TBefore>> = {},
|
condition: Partial<Condition<TArgs, TBefore>> = {},
|
||||||
) {
|
) {
|
||||||
this.condition = {
|
this.condition = {
|
||||||
before: async (args: TArgs, txData: Partial<TxData>) => {
|
before: async (_args: TArgs, _txData: Partial<TxData>) => {
|
||||||
return ({} as any) as TBefore;
|
return ({} as any) as TBefore;
|
||||||
},
|
},
|
||||||
after: async (beforeInfo: TBefore, result: FunctionResult, args: TArgs, txData: Partial<TxData>) => {
|
after: async (_beforeInfo: TBefore, _result: FunctionResult, _args: TArgs, _txData: Partial<TxData>) => {
|
||||||
return ({} as any) as TBefore;
|
return ({} as any) as TBefore;
|
||||||
},
|
},
|
||||||
...condition,
|
...condition,
|
||||||
};
|
};
|
||||||
this.wrapperFunction = wrapperFunction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +82,10 @@ export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> imp
|
|||||||
// Try to make the call to the function. If it is successful, pass the
|
// Try to make the call to the function. If it is successful, pass the
|
||||||
// result and receipt to the after condition.
|
// result and receipt to the after condition.
|
||||||
try {
|
try {
|
||||||
const functionWithArgs = this.wrapperFunction(...args) as ContractTxFunctionObj<ReturnDataType>;
|
const functionWithArgs = (this._contractWrapper as any)[this._functionName](
|
||||||
|
...args,
|
||||||
|
) as ContractTxFunctionObj<ReturnDataType>;
|
||||||
|
logger.logFunctionAssertion(this._functionName, args, txData);
|
||||||
callResult.data = await functionWithArgs.callAsync(txData);
|
callResult.data = await functionWithArgs.callAsync(txData);
|
||||||
callResult.receipt =
|
callResult.receipt =
|
||||||
functionWithArgs.awaitTransactionSuccessAsync !== undefined
|
functionWithArgs.awaitTransactionSuccessAsync !== undefined
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { StakingEvents, StakingMakerStakingPoolSetEventArgs } from '@0x/contracts-staking';
|
import { StakingEvents, StakingMakerStakingPoolSetEventArgs } from '@0x/contracts-staking';
|
||||||
import { expect, filterLogsToArguments } from '@0x/contracts-test-utils';
|
import { expect, filterLogsToArguments } from '@0x/contracts-test-utils';
|
||||||
import { logUtils } from '@0x/utils';
|
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
import { DeploymentManager } from '../deployment_manager';
|
import { DeploymentManager } from '../deployment_manager';
|
||||||
@ -15,7 +14,7 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
|
|||||||
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
|
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
|
||||||
const { stakingWrapper } = deployment.staking;
|
const { stakingWrapper } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion<[string], {}, void>(stakingWrapper.joinStakingPoolAsMaker.bind(stakingWrapper), {
|
return new FunctionAssertion<[string], {}, void>(stakingWrapper, 'joinStakingPoolAsMaker', {
|
||||||
after: async (_beforeInfo, _result: FunctionResult, args: [string], txData: Partial<TxData>) => {
|
after: async (_beforeInfo, _result: FunctionResult, args: [string], txData: Partial<TxData>) => {
|
||||||
const [poolId] = args;
|
const [poolId] = args;
|
||||||
|
|
||||||
@ -34,8 +33,6 @@ export function validJoinStakingPoolAssertion(deployment: DeploymentManager): Fu
|
|||||||
]);
|
]);
|
||||||
const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(txData.from!).callAsync();
|
const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(txData.from!).callAsync();
|
||||||
expect(joinedPoolId).to.be.eq(poolId);
|
expect(joinedPoolId).to.be.eq(poolId);
|
||||||
|
|
||||||
logUtils.log(`Pool ${poolId} joined by ${txData.from}`);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
StoredBalance,
|
StoredBalance,
|
||||||
} from '@0x/contracts-staking';
|
} from '@0x/contracts-staking';
|
||||||
import { constants, expect } from '@0x/contracts-test-utils';
|
import { constants, expect } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@ -86,61 +86,50 @@ export function validMoveStakeAssertion(
|
|||||||
): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> {
|
): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> {
|
||||||
const { stakingWrapper } = deployment.staking;
|
const { stakingWrapper } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(
|
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(stakingWrapper, 'moveStake', {
|
||||||
stakingWrapper.moveStake.bind(stakingWrapper),
|
after: async (
|
||||||
{
|
_beforeInfo: {},
|
||||||
after: async (
|
_result: FunctionResult,
|
||||||
_beforeInfo: {},
|
args: [StakeInfo, StakeInfo, BigNumber],
|
||||||
_result: FunctionResult,
|
txData: Partial<TxData>,
|
||||||
args: [StakeInfo, StakeInfo, BigNumber],
|
) => {
|
||||||
txData: Partial<TxData>,
|
const [from, to, amount] = args;
|
||||||
) => {
|
|
||||||
const [from, to, amount] = args;
|
|
||||||
|
|
||||||
logUtils.log(
|
const owner = txData.from!; // tslint:disable-line:no-non-null-assertion
|
||||||
`moveStake({status: ${StakeStatus[from.status]}, poolId: ${from.poolId} }, { status: ${
|
|
||||||
StakeStatus[to.status]
|
|
||||||
}, poolId: ${to.poolId} }, ${amount})`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const owner = txData.from!; // tslint:disable-line:no-non-null-assertion
|
// Update local balances to match the expected result of this `moveStake` operation
|
||||||
|
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
|
||||||
|
|
||||||
// Update local balances to match the expected result of this `moveStake` operation
|
// Fetches on-chain owner stake balances and checks against local balances
|
||||||
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
|
const ownerUndelegatedStake = {
|
||||||
|
...new StoredBalance(),
|
||||||
|
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Undelegated).callAsync()),
|
||||||
|
};
|
||||||
|
const ownerDelegatedStake = {
|
||||||
|
...new StoredBalance(),
|
||||||
|
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Delegated).callAsync()),
|
||||||
|
};
|
||||||
|
expect(ownerUndelegatedStake).to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
||||||
|
expect(ownerDelegatedStake).to.deep.equal(ownerStake[StakeStatus.Delegated].total);
|
||||||
|
|
||||||
// Fetches on-chain owner stake balances and checks against local balances
|
// Fetches on-chain global stake balances and checks against local balances
|
||||||
const ownerUndelegatedStake = {
|
const globalUndelegatedStake = await stakingWrapper
|
||||||
...new StoredBalance(),
|
.getGlobalStakeByStatus(StakeStatus.Undelegated)
|
||||||
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Undelegated).callAsync()),
|
.callAsync();
|
||||||
};
|
const globalDelegatedStake = await stakingWrapper.getGlobalStakeByStatus(StakeStatus.Delegated).callAsync();
|
||||||
const ownerDelegatedStake = {
|
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
|
||||||
...new StoredBalance(),
|
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
|
||||||
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Delegated).callAsync()),
|
|
||||||
};
|
|
||||||
expect(ownerUndelegatedStake).to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
|
||||||
expect(ownerDelegatedStake).to.deep.equal(ownerStake[StakeStatus.Delegated].total);
|
|
||||||
|
|
||||||
// Fetches on-chain global stake balances and checks against local balances
|
// Fetches on-chain pool stake balances and checks against local balances
|
||||||
const globalUndelegatedStake = await stakingWrapper
|
for (const poolId of updatedPools) {
|
||||||
.getGlobalStakeByStatus(StakeStatus.Undelegated)
|
const stakeDelegatedByOwner = await stakingWrapper
|
||||||
|
.getStakeDelegatedToPoolByOwner(owner, poolId)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
const globalDelegatedStake = await stakingWrapper
|
const totalStakeDelegated = await stakingWrapper.getTotalStakeDelegatedToPool(poolId).callAsync();
|
||||||
.getGlobalStakeByStatus(StakeStatus.Delegated)
|
expect(stakeDelegatedByOwner).to.deep.equal(ownerStake[StakeStatus.Delegated][poolId]);
|
||||||
.callAsync();
|
expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake);
|
||||||
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
|
}
|
||||||
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
|
|
||||||
|
|
||||||
// Fetches on-chain pool stake balances and checks against local balances
|
|
||||||
for (const poolId of updatedPools) {
|
|
||||||
const stakeDelegatedByOwner = await stakingWrapper
|
|
||||||
.getStakeDelegatedToPoolByOwner(owner, poolId)
|
|
||||||
.callAsync();
|
|
||||||
const totalStakeDelegated = await stakingWrapper.getTotalStakeDelegatedToPool(poolId).callAsync();
|
|
||||||
expect(stakeDelegatedByOwner).to.deep.equal(ownerStake[StakeStatus.Delegated][poolId]);
|
|
||||||
expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
/* tslint:enable:no-unnecessary-type-assertion */
|
/* tslint:enable:no-unnecessary-type-assertion */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { GlobalStakeByStatus, OwnerStakeByStatus, StakeStatus, StoredBalance } from '@0x/contracts-staking';
|
import { GlobalStakeByStatus, OwnerStakeByStatus, StakeStatus, StoredBalance } from '@0x/contracts-staking';
|
||||||
import { expect } from '@0x/contracts-test-utils';
|
import { expect } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
|
import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
|
||||||
@ -34,7 +34,7 @@ export function validStakeAssertion(
|
|||||||
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
|
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
|
||||||
const { stakingWrapper, zrxVault } = deployment.staking;
|
const { stakingWrapper, zrxVault } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion(stakingWrapper.stake.bind(stakingWrapper), {
|
return new FunctionAssertion(stakingWrapper, 'stake', {
|
||||||
before: async (args: [BigNumber], txData: Partial<TxData>) => {
|
before: async (args: [BigNumber], txData: Partial<TxData>) => {
|
||||||
const [amount] = args;
|
const [amount] = args;
|
||||||
|
|
||||||
@ -56,8 +56,6 @@ export function validStakeAssertion(
|
|||||||
) => {
|
) => {
|
||||||
const [amount] = args;
|
const [amount] = args;
|
||||||
|
|
||||||
logUtils.log(`stake(${amount})`);
|
|
||||||
|
|
||||||
// Checks that the ZRX transfer updated balances as expected.
|
// Checks that the ZRX transfer updated balances as expected.
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { GlobalStakeByStatus, OwnerStakeByStatus, StakeStatus, StoredBalance } from '@0x/contracts-staking';
|
import { GlobalStakeByStatus, OwnerStakeByStatus, StakeStatus, StoredBalance } from '@0x/contracts-staking';
|
||||||
import { expect } from '@0x/contracts-test-utils';
|
import { expect } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
|
import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
|
||||||
@ -35,7 +35,7 @@ export function validUnstakeAssertion(
|
|||||||
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
|
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
|
||||||
const { stakingWrapper, zrxVault } = deployment.staking;
|
const { stakingWrapper, zrxVault } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion(stakingWrapper.unstake.bind(stakingWrapper), {
|
return new FunctionAssertion(stakingWrapper, 'unstake', {
|
||||||
before: async (args: [BigNumber], txData: Partial<TxData>) => {
|
before: async (args: [BigNumber], txData: Partial<TxData>) => {
|
||||||
const [amount] = args;
|
const [amount] = args;
|
||||||
|
|
||||||
@ -57,8 +57,6 @@ export function validUnstakeAssertion(
|
|||||||
) => {
|
) => {
|
||||||
const [amount] = args;
|
const [amount] = args;
|
||||||
|
|
||||||
logUtils.log(`unstake(${amount})`);
|
|
||||||
|
|
||||||
// Checks that the ZRX transfer updated balances as expected.
|
// Checks that the ZRX transfer updated balances as expected.
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BaseContract } from '@0x/base-contract';
|
import { BaseContract } from '@0x/base-contract';
|
||||||
import { constants, expect, TokenBalances } from '@0x/contracts-test-utils';
|
import { constants, expect, replaceKeysDeep, TokenBalances } from '@0x/contracts-test-utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { TokenAddresses, TokenContractsByName, TokenOwnersByName } from './types';
|
import { TokenAddresses, TokenContractsByName, TokenOwnersByName } from './types';
|
||||||
@ -68,6 +68,14 @@ export class BalanceStore {
|
|||||||
this._addressNames = _.cloneDeep(balanceStore._addressNames);
|
this._addressNames = _.cloneDeep(balanceStore._addressNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version of balances where keys are replaced with their readable counterparts, if
|
||||||
|
* they exist.
|
||||||
|
*/
|
||||||
|
public toReadable(): _.Dictionary<{}> {
|
||||||
|
return replaceKeysDeep(this.balances, this._readableAddressName.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the human-readable name for the given address, if it exists.
|
* Returns the human-readable name for the given address, if it exists.
|
||||||
* @param address The address to get the name for.
|
* @param address The address to get the name for.
|
||||||
|
@ -25,7 +25,7 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { AssetProxyDispatcher, Authorizable, Ownable } from './wrapper_interfaces';
|
import { AssetProxyDispatcher, Authorizable, Ownable } from './utils/wrapper_interfaces';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a batch of authorities to a list of authorizable contracts.
|
* Adds a batch of authorities to a list of authorizable contracts.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking';
|
import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { Maker } from './actors/maker';
|
import { Maker } from './actors/maker';
|
||||||
import { AssertionResult } from './assertions/function_assertion';
|
import { AssertionResult } from './assertions/function_assertion';
|
||||||
import { BlockchainBalanceStore } from './balances/blockchain_balance_store';
|
import { BlockchainBalanceStore } from './balances/blockchain_balance_store';
|
||||||
import { DeploymentManager } from './deployment_manager';
|
import { DeploymentManager } from './deployment_manager';
|
||||||
|
import { logger } from './utils/logger';
|
||||||
|
|
||||||
// tslint:disable:max-classes-per-file
|
// tslint:disable:max-classes-per-file
|
||||||
|
|
||||||
@ -20,6 +20,14 @@ export class SimulationEnvironment {
|
|||||||
public balanceStore: BlockchainBalanceStore,
|
public balanceStore: BlockchainBalanceStore,
|
||||||
public marketMakers: Maker[] = [],
|
public marketMakers: Maker[] = [],
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public state(): any {
|
||||||
|
return {
|
||||||
|
globalStake: this.globalStake,
|
||||||
|
stakingPools: this.stakingPools,
|
||||||
|
balanceStore: this.balanceStore.toReadable(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class Simulation {
|
export abstract class Simulation {
|
||||||
@ -30,14 +38,23 @@ export abstract class Simulation {
|
|||||||
public async fuzzAsync(steps?: number): Promise<void> {
|
public async fuzzAsync(steps?: number): Promise<void> {
|
||||||
if (steps !== undefined) {
|
if (steps !== undefined) {
|
||||||
for (let i = 0; i < steps; i++) {
|
for (let i = 0; i < steps; i++) {
|
||||||
await this.generator.next();
|
await this._stepAsync();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (true) {
|
while (true) {
|
||||||
await this.generator.next();
|
await this._stepAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract _assertionGenerator(): AsyncIterableIterator<AssertionResult | void>;
|
protected abstract _assertionGenerator(): AsyncIterableIterator<AssertionResult | void>;
|
||||||
|
|
||||||
|
private async _stepAsync(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.generator.next();
|
||||||
|
} catch (error) {
|
||||||
|
logger.logFailure(error, this.environment.state());
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { constants as stakingConstants } from '@0x/contracts-staking';
|
|||||||
import { blockchainTests, expect } from '@0x/contracts-test-utils';
|
import { blockchainTests, expect } from '@0x/contracts-test-utils';
|
||||||
|
|
||||||
import { DeploymentManager } from '../deployment_manager';
|
import { DeploymentManager } from '../deployment_manager';
|
||||||
import { Authorizable, Ownable } from '../wrapper_interfaces';
|
import { Authorizable, Ownable } from '../utils/wrapper_interfaces';
|
||||||
|
|
||||||
blockchainTests('Deployment Manager', env => {
|
blockchainTests('Deployment Manager', env => {
|
||||||
let owner: string;
|
let owner: string;
|
||||||
|
@ -23,14 +23,11 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
|||||||
describe('executeAsync', () => {
|
describe('executeAsync', () => {
|
||||||
it('should call the before function with the provided arguments', async () => {
|
it('should call the before function with the provided arguments', async () => {
|
||||||
let sideEffectTarget = ZERO_AMOUNT;
|
let sideEffectTarget = ZERO_AMOUNT;
|
||||||
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
|
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(exampleContract, 'returnInteger', {
|
||||||
exampleContract.returnInteger.bind(exampleContract),
|
before: async (args: [BigNumber], txData: Partial<TxData>) => {
|
||||||
{
|
sideEffectTarget = randomInput;
|
||||||
before: async (args: [BigNumber], txData: Partial<TxData>) => {
|
|
||||||
sideEffectTarget = randomInput;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||||
await assertion.executeAsync([randomInput], {});
|
await assertion.executeAsync([randomInput], {});
|
||||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||||
@ -38,26 +35,23 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
|||||||
|
|
||||||
it('should call the after function with the provided arguments', async () => {
|
it('should call the after function with the provided arguments', async () => {
|
||||||
let sideEffectTarget = ZERO_AMOUNT;
|
let sideEffectTarget = ZERO_AMOUNT;
|
||||||
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
|
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(exampleContract, 'returnInteger', {
|
||||||
exampleContract.returnInteger.bind(exampleContract),
|
after: async (
|
||||||
{
|
_beforeInfo: any,
|
||||||
after: async (
|
_result: FunctionResult,
|
||||||
_beforeInfo: any,
|
args: [BigNumber],
|
||||||
_result: FunctionResult,
|
txData: Partial<TxData>,
|
||||||
args: [BigNumber],
|
) => {
|
||||||
txData: Partial<TxData>,
|
[sideEffectTarget] = args;
|
||||||
) => {
|
|
||||||
[sideEffectTarget] = args;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||||
await assertion.executeAsync([randomInput], {});
|
await assertion.executeAsync([randomInput], {});
|
||||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not fail immediately if the wrapped function fails', async () => {
|
it('should not fail immediately if the wrapped function fails', async () => {
|
||||||
const assertion = new FunctionAssertion<[], {}, void>(exampleContract.emptyRevert.bind(exampleContract));
|
const assertion = new FunctionAssertion<[], {}, void>(exampleContract, 'emptyRevert');
|
||||||
await assertion.executeAsync([], {});
|
await assertion.executeAsync([], {});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,7 +59,8 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
|||||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||||
let sideEffectTarget = ZERO_AMOUNT;
|
let sideEffectTarget = ZERO_AMOUNT;
|
||||||
const assertion = new FunctionAssertion<[BigNumber], BigNumber, BigNumber>(
|
const assertion = new FunctionAssertion<[BigNumber], BigNumber, BigNumber>(
|
||||||
exampleContract.returnInteger.bind(exampleContract),
|
exampleContract,
|
||||||
|
'returnInteger',
|
||||||
{
|
{
|
||||||
before: async (_args: [BigNumber], _txData: Partial<TxData>) => {
|
before: async (_args: [BigNumber], _txData: Partial<TxData>) => {
|
||||||
return randomInput;
|
return randomInput;
|
||||||
@ -86,19 +81,16 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
|||||||
|
|
||||||
it('should pass the result from the function call to "after"', async () => {
|
it('should pass the result from the function call to "after"', async () => {
|
||||||
let sideEffectTarget = ZERO_AMOUNT;
|
let sideEffectTarget = ZERO_AMOUNT;
|
||||||
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
|
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(exampleContract, 'returnInteger', {
|
||||||
exampleContract.returnInteger.bind(exampleContract),
|
after: async (
|
||||||
{
|
_beforeInfo: any,
|
||||||
after: async (
|
result: FunctionResult,
|
||||||
_beforeInfo: any,
|
_args: [BigNumber],
|
||||||
result: FunctionResult,
|
_txData: Partial<TxData>,
|
||||||
_args: [BigNumber],
|
) => {
|
||||||
_txData: Partial<TxData>,
|
sideEffectTarget = result.data;
|
||||||
) => {
|
|
||||||
sideEffectTarget = result.data;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||||
await assertion.executeAsync([randomInput], {});
|
await assertion.executeAsync([randomInput], {});
|
||||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||||
@ -106,21 +98,13 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
|||||||
|
|
||||||
it('should pass the receipt from the function call to "after"', async () => {
|
it('should pass the receipt from the function call to "after"', async () => {
|
||||||
let sideEffectTarget: TransactionReceiptWithDecodedLogs;
|
let sideEffectTarget: TransactionReceiptWithDecodedLogs;
|
||||||
const assertion = new FunctionAssertion<[string], void, void>(
|
const assertion = new FunctionAssertion<[string], void, void>(exampleContract, 'emitEvent', {
|
||||||
exampleContract.emitEvent.bind(exampleContract),
|
after: async (_beforeInfo: any, result: FunctionResult, _args: [string], _txData: Partial<TxData>) => {
|
||||||
{
|
if (result.receipt) {
|
||||||
after: async (
|
sideEffectTarget = result.receipt;
|
||||||
_beforeInfo: any,
|
}
|
||||||
result: FunctionResult,
|
|
||||||
_args: [string],
|
|
||||||
_txData: Partial<TxData>,
|
|
||||||
) => {
|
|
||||||
if (result.receipt) {
|
|
||||||
sideEffectTarget = result.receipt;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
const input = 'emitted data';
|
const input = 'emitted data';
|
||||||
await assertion.executeAsync([input], {});
|
await assertion.executeAsync([input], {});
|
||||||
@ -135,19 +119,11 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
|||||||
|
|
||||||
it('should pass the error to "after" if the function call fails', async () => {
|
it('should pass the error to "after" if the function call fails', async () => {
|
||||||
let sideEffectTarget: Error;
|
let sideEffectTarget: Error;
|
||||||
const assertion = new FunctionAssertion<[string], void, void>(
|
const assertion = new FunctionAssertion<[string], void, void>(exampleContract, 'stringRevert', {
|
||||||
exampleContract.stringRevert.bind(exampleContract),
|
after: async (_beforeInfo: any, result: FunctionResult, _args: [string], _txData: Partial<TxData>) => {
|
||||||
{
|
sideEffectTarget = result.data;
|
||||||
after: async (
|
|
||||||
_beforeInfo: any,
|
|
||||||
result: FunctionResult,
|
|
||||||
_args: [string],
|
|
||||||
_txData: Partial<TxData>,
|
|
||||||
) => {
|
|
||||||
sideEffectTarget = result.data;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
const message = 'error message';
|
const message = 'error message';
|
||||||
await assertion.executeAsync([message], {});
|
await assertion.executeAsync([message], {});
|
||||||
|
|
||||||
|
55
contracts/integrations/test/framework/utils/logger.ts
Normal file
55
contracts/integrations/test/framework/utils/logger.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { TxData } from 'ethereum-types';
|
||||||
|
|
||||||
|
import { Pseudorandom } from '../utils/pseudorandom';
|
||||||
|
|
||||||
|
// tslint:disable:no-console
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
private _step = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.warn(
|
||||||
|
JSON.stringify({
|
||||||
|
level: 'info',
|
||||||
|
time: new Date(),
|
||||||
|
msg: `Pseudorandom seed: ${Pseudorandom.seed}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Logs the name of the function executed, the arguments and transaction data it was
|
||||||
|
* called with, and the current step of the simulation.
|
||||||
|
*/
|
||||||
|
public logFunctionAssertion(functionName: string, functionArgs: any[], txData: Partial<TxData>): void {
|
||||||
|
console.warn(
|
||||||
|
JSON.stringify({
|
||||||
|
level: 'info',
|
||||||
|
time: new Date(),
|
||||||
|
msg: `Function called: ${functionName}(${functionArgs
|
||||||
|
.map(arg => JSON.stringify(arg).replace(/"/g, "'"))
|
||||||
|
.join(', ')})`,
|
||||||
|
step: this._step++,
|
||||||
|
txData,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Logs information about a assertion failure. Dumps the error thrown and arbitrary data from
|
||||||
|
* the calling context.
|
||||||
|
*/
|
||||||
|
public logFailure(error: Error, data: string): void {
|
||||||
|
console.warn(
|
||||||
|
JSON.stringify({
|
||||||
|
level: 'error',
|
||||||
|
time: new Date(),
|
||||||
|
step: this._step,
|
||||||
|
error,
|
||||||
|
data,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logger = new Logger();
|
39
contracts/integrations/test/framework/utils/pseudorandom.ts
Normal file
39
contracts/integrations/test/framework/utils/pseudorandom.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Numberish } from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import * as seedrandom from 'seedrandom';
|
||||||
|
|
||||||
|
class PRNGWrapper {
|
||||||
|
public readonly seed = process.env.UUID || Math.random().toString();
|
||||||
|
private readonly _rng = seedrandom(this.seed);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pseudorandom version of _.sample. Picks an element of the given array with uniform probability.
|
||||||
|
* Return undefined if the array is empty.
|
||||||
|
*/
|
||||||
|
public sample<T>(arr: T[]): T | undefined {
|
||||||
|
if (arr.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const index = Math.abs(this._rng.int32()) % arr.length;
|
||||||
|
return arr[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable:unified-signatures
|
||||||
|
/*
|
||||||
|
* Pseudorandom version of getRandomPortion/getRandomInteger. If two arguments are provided,
|
||||||
|
* treats those arguments as the min and max (inclusive) of the desired range. If only one
|
||||||
|
* argument is provided, picks an integer between 0 and the argument.
|
||||||
|
*/
|
||||||
|
public integer(max: Numberish): BigNumber;
|
||||||
|
public integer(min: Numberish, max: Numberish): BigNumber;
|
||||||
|
public integer(a: Numberish, b?: Numberish): BigNumber {
|
||||||
|
if (b === undefined) {
|
||||||
|
return new BigNumber(this._rng()).times(a).integerValue(BigNumber.ROUND_HALF_UP);
|
||||||
|
} else {
|
||||||
|
const range = new BigNumber(b).minus(a);
|
||||||
|
return this.integer(range).plus(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Pseudorandom = new PRNGWrapper();
|
@ -1,5 +1,4 @@
|
|||||||
import { blockchainTests } from '@0x/contracts-test-utils';
|
import { blockchainTests } from '@0x/contracts-test-utils';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { Actor } from '../framework/actors/base';
|
import { Actor } from '../framework/actors/base';
|
||||||
import { PoolOperator } from '../framework/actors/pool_operator';
|
import { PoolOperator } from '../framework/actors/pool_operator';
|
||||||
@ -7,6 +6,7 @@ import { AssertionResult } from '../framework/assertions/function_assertion';
|
|||||||
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
||||||
import { DeploymentManager } from '../framework/deployment_manager';
|
import { DeploymentManager } from '../framework/deployment_manager';
|
||||||
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
||||||
|
import { Pseudorandom } from '../framework/utils/pseudorandom';
|
||||||
|
|
||||||
export class PoolManagementSimulation extends Simulation {
|
export class PoolManagementSimulation extends Simulation {
|
||||||
protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
|
protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
|
||||||
@ -22,13 +22,18 @@ export class PoolManagementSimulation extends Simulation {
|
|||||||
operator.simulationActions.validDecreaseStakingPoolOperatorShare,
|
operator.simulationActions.validDecreaseStakingPoolOperatorShare,
|
||||||
];
|
];
|
||||||
while (true) {
|
while (true) {
|
||||||
const action = _.sample(actions);
|
const action = Pseudorandom.sample(actions);
|
||||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blockchainTests.skip('Pool management fuzz test', env => {
|
blockchainTests('Pool management fuzz test', env => {
|
||||||
|
before(function(): void {
|
||||||
|
if (process.env.FUZZ_TEST !== 'pool_management') {
|
||||||
|
this.skip();
|
||||||
|
}
|
||||||
|
});
|
||||||
after(async () => {
|
after(async () => {
|
||||||
Actor.reset();
|
Actor.reset();
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { blockchainTests, constants } from '@0x/contracts-test-utils';
|
import { blockchainTests, constants } from '@0x/contracts-test-utils';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { MakerTaker } from '../framework/actors/hybrids';
|
import { MakerTaker } from '../framework/actors/hybrids';
|
||||||
import { Maker } from '../framework/actors/maker';
|
import { Maker } from '../framework/actors/maker';
|
||||||
@ -7,6 +6,7 @@ import { AssertionResult } from '../framework/assertions/function_assertion';
|
|||||||
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
||||||
import { DeploymentManager } from '../framework/deployment_manager';
|
import { DeploymentManager } from '../framework/deployment_manager';
|
||||||
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
||||||
|
import { Pseudorandom } from '../framework/utils/pseudorandom';
|
||||||
|
|
||||||
import { PoolManagementSimulation } from './pool_management_test';
|
import { PoolManagementSimulation } from './pool_management_test';
|
||||||
|
|
||||||
@ -29,17 +29,21 @@ class PoolMembershipSimulation extends Simulation {
|
|||||||
];
|
];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const action = _.sample(actions);
|
const action = Pseudorandom.sample(actions);
|
||||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blockchainTests.skip('pool membership fuzz test', env => {
|
blockchainTests('pool membership fuzz test', env => {
|
||||||
let deployment: DeploymentManager;
|
let deployment: DeploymentManager;
|
||||||
let maker: Maker;
|
let maker: Maker;
|
||||||
|
|
||||||
before(async () => {
|
before(async function(): Promise<void> {
|
||||||
|
if (process.env.FUZZ_TEST !== 'pool_membership') {
|
||||||
|
this.skip();
|
||||||
|
}
|
||||||
|
|
||||||
deployment = await DeploymentManager.deployAsync(env, {
|
deployment = await DeploymentManager.deployAsync(env, {
|
||||||
numErc20TokensToDeploy: 2,
|
numErc20TokensToDeploy: 2,
|
||||||
numErc721TokensToDeploy: 0,
|
numErc721TokensToDeploy: 0,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { blockchainTests } from '@0x/contracts-test-utils';
|
import { blockchainTests } from '@0x/contracts-test-utils';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { Actor } from '../framework/actors/base';
|
import { Actor } from '../framework/actors/base';
|
||||||
import { Staker } from '../framework/actors/staker';
|
import { Staker } from '../framework/actors/staker';
|
||||||
@ -7,6 +6,7 @@ import { AssertionResult } from '../framework/assertions/function_assertion';
|
|||||||
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
||||||
import { DeploymentManager } from '../framework/deployment_manager';
|
import { DeploymentManager } from '../framework/deployment_manager';
|
||||||
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
||||||
|
import { Pseudorandom } from '../framework/utils/pseudorandom';
|
||||||
|
|
||||||
import { PoolManagementSimulation } from './pool_management_test';
|
import { PoolManagementSimulation } from './pool_management_test';
|
||||||
|
|
||||||
@ -26,13 +26,19 @@ export class StakeManagementSimulation extends Simulation {
|
|||||||
poolManagement.generator,
|
poolManagement.generator,
|
||||||
];
|
];
|
||||||
while (true) {
|
while (true) {
|
||||||
const action = _.sample(actions);
|
const action = Pseudorandom.sample(actions);
|
||||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blockchainTests.skip('Stake management fuzz test', env => {
|
blockchainTests('Stake management fuzz test', env => {
|
||||||
|
before(function(): void {
|
||||||
|
if (process.env.FUZZ_TEST !== 'stake_management') {
|
||||||
|
this.skip();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
Actor.reset();
|
Actor.reset();
|
||||||
});
|
});
|
||||||
|
6
contracts/integrations/test/fuzz_tests/tslint.json
Normal file
6
contracts/integrations/test/fuzz_tests/tslint.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": ["@0x/tslint-config"],
|
||||||
|
"rules": {
|
||||||
|
"no-invalid-this": false
|
||||||
|
}
|
||||||
|
}
|
@ -50,7 +50,7 @@ export {
|
|||||||
export { blockchainTests, BlockchainTestsEnvironment, describe } from './mocha_blockchain';
|
export { blockchainTests, BlockchainTestsEnvironment, describe } from './mocha_blockchain';
|
||||||
export { chaiSetup, expect } from './chai_setup';
|
export { chaiSetup, expect } from './chai_setup';
|
||||||
export { getCodesizeFromArtifact } from './codesize';
|
export { getCodesizeFromArtifact } from './codesize';
|
||||||
export { shortZip } from './lang_utils';
|
export { replaceKeysDeep, shortZip } from './lang_utils';
|
||||||
export {
|
export {
|
||||||
assertIntegerRoughlyEquals,
|
assertIntegerRoughlyEquals,
|
||||||
assertRoughlyEquals,
|
assertRoughlyEquals,
|
||||||
|
@ -7,3 +7,13 @@ export function shortZip<T1, T2>(a: T1[], b: T2[]): Array<[T1, T2]> {
|
|||||||
const minLength = Math.min(a.length, b.length);
|
const minLength = Math.min(a.length, b.length);
|
||||||
return _.zip(a.slice(0, minLength), b.slice(0, minLength)) as Array<[T1, T2]>;
|
return _.zip(a.slice(0, minLength), b.slice(0, minLength)) as Array<[T1, T2]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the keys in a deeply nested object. Adapted from https://stackoverflow.com/a/39126851
|
||||||
|
*/
|
||||||
|
export function replaceKeysDeep(obj: {}, mapKeys: (key: string) => string | void): _.Dictionary<{}> {
|
||||||
|
return _.transform(obj, (result, value, key) => {
|
||||||
|
const currentKey = mapKeys(key) || key;
|
||||||
|
result[currentKey] = _.isObject(value) ? replaceKeysDeep(value, mapKeys) : value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -2347,6 +2347,11 @@
|
|||||||
"@types/glob" "*"
|
"@types/glob" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/seedrandom@^2.4.28":
|
||||||
|
version "2.4.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f"
|
||||||
|
integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA==
|
||||||
|
|
||||||
"@types/semver@5.5.0":
|
"@types/semver@5.5.0":
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
|
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
|
||||||
@ -14672,6 +14677,11 @@ seedrandom@2.4.4:
|
|||||||
version "2.4.4"
|
version "2.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.4.tgz#b25ea98632c73e45f58b77cfaa931678df01f9ba"
|
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.4.tgz#b25ea98632c73e45f58b77cfaa931678df01f9ba"
|
||||||
|
|
||||||
|
seedrandom@^3.0.5:
|
||||||
|
version "3.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"
|
||||||
|
integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==
|
||||||
|
|
||||||
seek-bzip@^1.0.5:
|
seek-bzip@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
|
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user