Merge pull request #2366 from 0xProject/feature/fuzz/makers-and-takers

Pool Member Fuzz Tests
This commit is contained in:
mzhu25
2019-12-04 11:12:55 -08:00
committed by GitHub
17 changed files with 668 additions and 154 deletions

View File

@@ -3,9 +3,11 @@ import { KeeperMixin } from './keeper';
import { MakerMixin } from './maker';
import { PoolOperatorMixin } from './pool_operator';
import { StakerMixin } from './staker';
import { TakerMixin } from './taker';
export class OperatorMaker extends PoolOperatorMixin(MakerMixin(Actor)) {}
export class StakerMaker extends StakerMixin(MakerMixin(Actor)) {}
export class StakerOperator extends StakerMixin(PoolOperatorMixin(Actor)) {}
export class OperatorStakerMaker extends PoolOperatorMixin(StakerMixin(MakerMixin(Actor))) {}
export class StakerKeeper extends StakerMixin(KeeperMixin(Actor)) {}
export class MakerTaker extends MakerMixin(TakerMixin(Actor)) {}

View File

@@ -1,6 +1,10 @@
import { constants, OrderFactory } from '@0x/contracts-test-utils';
import { Order, SignedOrder } from '@0x/types';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { AssertionResult } from '../assertions/function_assertion';
import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool';
import { Actor, ActorConfig, Constructor } from './base';
@@ -45,6 +49,12 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
...orderConfig,
};
this.orderFactory = new OrderFactory(this.actor.privateKey, defaultOrderParams);
// Register this mixin's assertion generators
this.actor.simulationActions = {
...this.actor.simulationActions,
validJoinStakingPool: this._validJoinStakingPool(),
};
}
/**
@@ -73,6 +83,19 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
from: this.actor.address,
});
}
private async *_validJoinStakingPool(): AsyncIterableIterator<AssertionResult | void> {
const { stakingPools } = this.actor.simulationEnvironment!;
const assertion = validJoinStakingPoolAssertion(this.actor.deployment);
while (true) {
const poolId = _.sample(Object.keys(stakingPools));
if (poolId === undefined) {
yield undefined;
} else {
yield assertion.executeAsync([poolId], { from: this.actor.address });
}
}
}
};
}

View File

@@ -83,8 +83,8 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
const { stakingPools } = this.actor.simulationEnvironment!;
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
while (true) {
const operatorShare = getRandomInteger(0, constants.PPM);
yield assertion.executeAsync(operatorShare, false, { from: this.actor.address });
const operatorShare = getRandomInteger(0, constants.PPM).toNumber();
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
}
}
@@ -96,8 +96,8 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
if (poolId === undefined) {
yield undefined;
} else {
const operatorShare = getRandomInteger(0, stakingPools[poolId].operatorShare);
yield assertion.executeAsync(poolId, operatorShare, { from: this.actor.address });
const operatorShare = getRandomInteger(0, stakingPools[poolId].operatorShare).toNumber();
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
}
}
}

View File

@@ -76,7 +76,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
await balanceStore.updateErc20BalancesAsync();
const zrxBalance = balanceStore.balances.erc20[this.actor.address][zrx.address];
const amount = getRandomInteger(0, zrxBalance);
yield assertion.executeAsync(amount, { from: this.actor.address });
yield assertion.executeAsync([amount], { from: this.actor.address });
}
}
@@ -95,7 +95,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
undelegatedStake.nextEpochBalance,
);
const amount = getRandomInteger(0, withdrawableStake);
yield assertion.executeAsync(amount, { from: this.actor.address });
yield assertion.executeAsync([amount], { from: this.actor.address });
}
}
@@ -124,7 +124,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
: this.stake[StakeStatus.Delegated][from.poolId].nextEpochBalance;
const amount = getRandomInteger(0, moveableStake);
yield assertion.executeAsync(from, to, amount, { from: this.actor.address });
yield assertion.executeAsync([from, to, amount], { from: this.actor.address });
}
}
};

View File

@@ -1,7 +1,11 @@
import { constants, getRandomInteger } from '@0x/contracts-test-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import * as _ from 'lodash';
import { validFillOrderCompleteFillAssertion } from '../assertions/fillOrder';
import { AssertionResult } from '../assertions/function_assertion';
import { DeploymentManager } from '../deployment_manager';
import { Actor, Constructor } from './base';
@@ -31,6 +35,12 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
// tslint:disable-next-line:no-inferred-empty-object-type
super(...args);
this.actor = (this as any) as Actor;
// Register this mixin's assertion generators
this.actor.simulationActions = {
...this.actor.simulationActions,
validFillOrderCompleteFill: this._validFillOrderCompleteFill(),
};
}
/**
@@ -50,6 +60,37 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
...txData,
});
}
private async *_validFillOrderCompleteFill(): AsyncIterableIterator<AssertionResult | void> {
const { marketMakers } = this.actor.simulationEnvironment!;
const assertion = validFillOrderCompleteFillAssertion(this.actor.deployment);
while (true) {
const maker = _.sample(marketMakers);
if (maker === undefined) {
yield undefined;
} else {
// Configure the maker's token balances so that the order will definitely be fillable.
await Promise.all([
...this.actor.deployment.tokens.erc20.map(async token => maker.configureERC20TokenAsync(token)),
...this.actor.deployment.tokens.erc20.map(async token =>
this.actor.configureERC20TokenAsync(token),
),
this.actor.configureERC20TokenAsync(
this.actor.deployment.tokens.weth,
this.actor.deployment.staking.stakingProxy.address,
),
]);
const order = await maker.signOrderAsync({
makerAssetAmount: getRandomInteger(constants.ZERO_AMOUNT, constants.INITIAL_ERC20_BALANCE),
takerAssetAmount: getRandomInteger(constants.ZERO_AMOUNT, constants.INITIAL_ERC20_BALANCE),
});
yield assertion.executeAsync([order, order.takerAssetAmount, order.signature], {
from: this.actor.address,
});
}
}
}
};
}

View File

@@ -13,42 +13,48 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
* Returns a FunctionAssertion for `createStakingPool` which assumes valid input is provided. The
* FunctionAssertion checks that the new poolId is one more than the last poolId.
*/
/* tslint:disable:no-non-null-assertion */
export function validCreateStakingPoolAssertion(
deployment: DeploymentManager,
pools: StakingPoolById,
): FunctionAssertion<string, string> {
): FunctionAssertion<[number, boolean], string, string> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion(stakingWrapper.createStakingPool, {
// Returns the expected ID of th created pool
before: async () => {
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
// Effectively the last poolId + 1, but as a bytestring
return `0x${new BigNumber(lastPoolId)
.plus(1)
.toString(16)
.padStart(64, '0')}`;
},
after: async (
expectedPoolId: string,
result: FunctionResult,
operatorShare: number,
addOperatorAsMaker: boolean,
txData: Partial<TxData>,
) => {
logUtils.log(`createStakingPool(${operatorShare}, ${addOperatorAsMaker}) => ${expectedPoolId}`);
return new FunctionAssertion<[number, boolean], string, string>(
stakingWrapper.createStakingPool.bind(stakingWrapper),
{
// Returns the expected ID of th created pool
before: async () => {
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
// Effectively the last poolId + 1, but as a bytestring
return `0x${new BigNumber(lastPoolId)
.plus(1)
.toString(16)
.padStart(64, '0')}`;
},
after: async (
expectedPoolId: string,
result: FunctionResult,
args: [number, boolean],
txData: Partial<TxData>,
) => {
const [operatorShare, shouldAddMakerAsOperator] = args;
// 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 actualPoolId = (log as any).args.poolId;
expect(actualPoolId).to.equal(expectedPoolId);
logUtils.log(`createStakingPool(${operatorShare}, ${shouldAddMakerAsOperator}) => ${expectedPoolId}`);
// Adds the new pool to local state
pools[actualPoolId] = {
operator: txData.from as string,
operatorShare,
delegatedStake: new StoredBalance(),
};
// 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*/

View File

@@ -1,6 +1,7 @@
import { StakingPoolById } from '@0x/contracts-staking';
import { expect } from '@0x/contracts-test-utils';
import { logUtils } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { DeploymentManager } from '../deployment_manager';
@@ -13,18 +14,24 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
export function validDecreaseStakingPoolOperatorShareAssertion(
deployment: DeploymentManager,
pools: StakingPoolById,
): FunctionAssertion<{}, void> {
): FunctionAssertion<[string, number], {}, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<{}, void>(stakingWrapper.decreaseStakingPoolOperatorShare, {
after: async (_beforeInfo, _result: FunctionResult, poolId: string, expectedOperatorShare: number) => {
logUtils.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`);
return new FunctionAssertion<[string, number], {}, void>(
stakingWrapper.decreaseStakingPoolOperatorShare.bind(stakingWrapper),
{
after: async (_beforeInfo, _result: FunctionResult, args: [string, number], txData: Partial<TxData>) => {
const [poolId, expectedOperatorShare] = args;
// 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);
// Updates the pool in local state.
pools[poolId].operatorShare = operatorShare;
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);
// Updates the pool in local state.
pools[poolId].operatorShare = operatorShare;
},
},
});
);
}

View File

@@ -0,0 +1,99 @@
import { ERC20TokenEvents, ERC20TokenTransferEventArgs } from '@0x/contracts-erc20';
import { ExchangeEvents, ExchangeFillEventArgs } from '@0x/contracts-exchange';
import { constants, expect, orderHashUtils, verifyEvents } from '@0x/contracts-test-utils';
import { FillResults, Order } from '@0x/types';
import { BigNumber, logUtils } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import * as _ from 'lodash';
import { DeploymentManager } from '../deployment_manager';
import { FunctionAssertion, FunctionResult } from './function_assertion';
function verifyFillEvents(
takerAddress: string,
order: Order,
receipt: TransactionReceiptWithDecodedLogs,
deployment: DeploymentManager,
): void {
// Ensure that the fill event was correct.
verifyEvents<ExchangeFillEventArgs>(
receipt,
[
{
makerAddress: order.makerAddress,
feeRecipientAddress: order.feeRecipientAddress,
makerAssetData: order.makerAssetData,
takerAssetData: order.takerAssetData,
makerFeeAssetData: order.makerFeeAssetData,
takerFeeAssetData: order.takerFeeAssetData,
orderHash: orderHashUtils.getOrderHashHex(order),
takerAddress,
senderAddress: takerAddress,
makerAssetFilledAmount: order.makerAssetAmount,
takerAssetFilledAmount: order.takerAssetAmount,
makerFeePaid: constants.ZERO_AMOUNT,
takerFeePaid: constants.ZERO_AMOUNT,
protocolFeePaid: DeploymentManager.protocolFee,
},
],
ExchangeEvents.Fill,
);
// Ensure that the transfer events were correctly emitted.
verifyEvents<ERC20TokenTransferEventArgs>(
receipt,
[
{
_from: takerAddress,
_to: order.makerAddress,
_value: order.takerAssetAmount,
},
{
_from: order.makerAddress,
_to: takerAddress,
_value: order.makerAssetAmount,
},
{
_from: takerAddress,
_to: deployment.staking.stakingProxy.address,
_value: DeploymentManager.protocolFee,
},
],
ERC20TokenEvents.Transfer,
);
}
/**
* A function assertion that verifies that a complete and valid fill succeeded and emitted the correct logs.
*/
/* tslint:disable:no-unnecessary-type-assertion */
/* tslint:disable:no-non-null-assertion */
export function validFillOrderCompleteFillAssertion(
deployment: DeploymentManager,
): FunctionAssertion<[Order, BigNumber, string], {}, FillResults> {
const exchange = deployment.exchange;
return new FunctionAssertion<[Order, BigNumber, string], {}, FillResults>(exchange.fillOrder.bind(exchange), {
after: async (
_beforeInfo,
result: FunctionResult,
args: [Order, BigNumber, string],
txData: Partial<TxData>,
) => {
const [order] = args;
// Ensure that the tx succeeded.
expect(result.success).to.be.true();
// Ensure that the correct events were emitted.
verifyFillEvents(txData.from!, order, result.receipt!, deployment);
logUtils.log(`Order filled by ${txData.from}`);
// TODO: Add validation for on-chain state (like balances)
},
});
}
/* tslint:enable:no-non-null-assertion */
/* tslint:enable:no-unnecessary-type-assertion */

View File

@@ -1,9 +1,8 @@
import { ContractFunctionObj, ContractTxFunctionObj } from '@0x/base-contract';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import * as _ from 'lodash';
// tslint:disable:max-classes-per-file
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
export interface FunctionResult {
@@ -22,9 +21,9 @@ export interface FunctionResult {
* @param after A function that will be run after a call to the contract wrapper
* function.
*/
export interface Condition<TBefore> {
before: (...args: any[]) => Promise<TBefore>;
after: (beforeInfo: TBefore, result: FunctionResult, ...args: any[]) => Promise<any>;
export interface Condition<TArgs extends any[], TBefore> {
before: (args: TArgs, txData: Partial<TxData>) => Promise<TBefore>;
after: (beforeInfo: TBefore, result: FunctionResult, args: TArgs, txData: Partial<TxData>) => Promise<any>;
}
/**
@@ -34,8 +33,8 @@ export interface Condition<TBefore> {
* our `Assertion` implementations will do in practice).
* @param runAsync The function to execute for the assertion.
*/
export interface Assertion {
executeAsync: (...args: any[]) => Promise<any>;
export interface Assertion<TArgs extends any[]> {
executeAsync: (args: TArgs, txData: TxData) => Promise<any>;
}
export interface AssertionResult<TBefore = unknown> {
@@ -47,24 +46,28 @@ export interface AssertionResult<TBefore = unknown> {
* This class implements `Assertion` and represents a "Hoare Triple" that can be
* executed.
*/
export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> implements Assertion<TArgs> {
// A condition that will be applied to `wrapperFunction`.
public condition: Condition<TBefore>;
public condition: Condition<TArgs, TBefore>;
// The wrapper function that will be wrapped in assertions.
public wrapperFunction: (
...args: any[] // tslint:disable-line:trailing-comma
...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>;
constructor(
wrapperFunction: (
...args: any[] // tslint:disable-line:trailing-comma
...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>,
condition: Partial<Condition<TBefore>> = {},
condition: Partial<Condition<TArgs, TBefore>> = {},
) {
this.condition = {
before: _.noop.bind(this),
after: _.noop.bind(this),
before: async (args: TArgs, txData: Partial<TxData>) => {
return ({} as any) as TBefore;
},
after: async (beforeInfo: TBefore, result: FunctionResult, args: TArgs, txData: Partial<TxData>) => {
return ({} as any) as TBefore;
},
...condition,
};
this.wrapperFunction = wrapperFunction;
@@ -74,9 +77,9 @@ export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
* Runs the wrapped function and fails if the before or after assertions fail.
* @param ...args The args to the contract wrapper function.
*/
public async executeAsync(...args: any[]): Promise<AssertionResult<TBefore>> {
public async executeAsync(args: TArgs, txData: Partial<TxData>): Promise<AssertionResult<TBefore>> {
// Call the before condition.
const beforeInfo = await this.condition.before(...args);
const beforeInfo = await this.condition.before(args, txData);
// Initialize the callResult so that the default success value is true.
const callResult: FunctionResult = { success: true };
@@ -85,10 +88,10 @@ export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
// result and receipt to the after condition.
try {
const functionWithArgs = this.wrapperFunction(...args) as ContractTxFunctionObj<ReturnDataType>;
callResult.data = await functionWithArgs.callAsync();
callResult.data = await functionWithArgs.callAsync(txData);
callResult.receipt =
functionWithArgs.awaitTransactionSuccessAsync !== undefined
? await functionWithArgs.awaitTransactionSuccessAsync() // tslint:disable-line:await-promise
? await functionWithArgs.awaitTransactionSuccessAsync(txData) // tslint:disable-line:await-promise
: undefined;
// tslint:enable:await-promise
} catch (error) {
@@ -98,7 +101,7 @@ export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
}
// Call the after condition.
const afterInfo = await this.condition.after(beforeInfo, callResult, ...args);
const afterInfo = await this.condition.after(beforeInfo, callResult, args, txData);
return {
beforeInfo,

View File

@@ -0,0 +1,43 @@
import { StakingEvents, StakingMakerStakingPoolSetEventArgs } from '@0x/contracts-staking';
import { expect, filterLogsToArguments } from '@0x/contracts-test-utils';
import { logUtils } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { DeploymentManager } from '../deployment_manager';
import { FunctionAssertion, FunctionResult } from './function_assertion';
/**
* Returns a function assertion that verifies valid pool joining.
*/
/* tslint:disable:no-unnecessary-type-assertion */
/* tslint:disable:no-non-null-assertion */
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[string], {}, void>(stakingWrapper.joinStakingPoolAsMaker.bind(stakingWrapper), {
after: async (_beforeInfo, _result: FunctionResult, args: [string], txData: Partial<TxData>) => {
const [poolId] = args;
expect(_result.success).to.be.true();
const logs = _result.receipt!.logs;
const logArgs = filterLogsToArguments<StakingMakerStakingPoolSetEventArgs>(
logs,
StakingEvents.MakerStakingPoolSet,
);
expect(logArgs).to.be.deep.eq([
{
makerAddress: txData.from!,
poolId,
},
]);
const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(txData.from!).callAsync();
expect(joinedPoolId).to.be.eq(poolId);
logUtils.log(`Pool ${poolId} joined by ${txData.from}`);
},
});
}
/* tslint:enable:no-non-null-assertion */
/* tslint:enable:no-unnecessary-type-assertion */

View File

@@ -13,7 +13,7 @@ import * as _ from 'lodash';
import { DeploymentManager } from '../deployment_manager';
import { FunctionAssertion } from './function_assertion';
import { FunctionAssertion, FunctionResult } from './function_assertion';
function incrementNextEpochBalance(stakeBalance: StoredBalance, amount: BigNumber): void {
_.update(stakeBalance, ['nextEpochBalance'], balance => (balance || constants.ZERO_AMOUNT).plus(amount));
@@ -77,63 +77,70 @@ function updateNextEpochBalances(
* Returns a FunctionAssertion for `moveStake` which assumes valid input is provided. The
* FunctionAssertion checks that the staker's
*/
/* tslint:disable:no-unnecessary-type-assertion */
export function validMoveStakeAssertion(
deployment: DeploymentManager,
globalStake: GlobalStakeByStatus,
ownerStake: OwnerStakeByStatus,
pools: StakingPoolById,
): FunctionAssertion<{}, void> {
): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<{}, void>(stakingWrapper.moveStake, {
after: async (
_beforeInfo,
_result,
from: StakeInfo,
to: StakeInfo,
amount: BigNumber,
txData: Partial<TxData>,
) => {
logUtils.log(
`moveStake({status: ${StakeStatus[from.status]}, poolId: ${from.poolId} }, { status: ${
StakeStatus[to.status]
}, poolId: ${to.poolId} }, ${amount})`,
);
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(
stakingWrapper.moveStake.bind(stakingWrapper),
{
after: async (
_beforeInfo: {},
_result: FunctionResult,
args: [StakeInfo, StakeInfo, BigNumber],
txData: Partial<TxData>,
) => {
const [from, to, amount] = args;
const owner = txData.from as string;
logUtils.log(
`moveStake({status: ${StakeStatus[from.status]}, poolId: ${from.poolId} }, { status: ${
StakeStatus[to.status]
}, poolId: ${to.poolId} }, ${amount})`,
);
// Update local balances to match the expected result of this `moveStake` operation
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
const owner = txData.from!; // tslint:disable-line:no-non-null-assertion
// Fetches on-chain owner stake balances and checks against local balances
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);
// Update local balances to match the expected result of this `moveStake` operation
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
// Fetches on-chain global stake balances and checks against local balances
const globalUndelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Undelegated)
.callAsync();
const globalDelegatedStake = await stakingWrapper.getGlobalStakeByStatus(StakeStatus.Delegated).callAsync();
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
// Fetches on-chain owner stake balances and checks against local balances
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 pool stake balances and checks against local balances
for (const poolId of updatedPools) {
const stakeDelegatedByOwner = await stakingWrapper
.getStakeDelegatedToPoolByOwner(owner, poolId)
// Fetches on-chain global stake balances and checks against local balances
const globalUndelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Undelegated)
.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);
}
const globalDelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Delegated)
.callAsync();
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 */

View File

@@ -25,20 +25,23 @@ function expectedUndelegatedStake(
* FunctionAssertion checks that the staker and zrxVault's balances of ZRX decrease and increase,
* respectively, by the input amount.
*/
/* tslint:disable:no-unnecessary-type-assertion */
export function validStakeAssertion(
deployment: DeploymentManager,
balanceStore: BlockchainBalanceStore,
globalStake: GlobalStakeByStatus,
ownerStake: OwnerStakeByStatus,
): FunctionAssertion<LocalBalanceStore, void> {
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
const { stakingWrapper, zrxVault } = deployment.staking;
return new FunctionAssertion(stakingWrapper.stake, {
before: async (amount: BigNumber, txData: Partial<TxData>) => {
return new FunctionAssertion(stakingWrapper.stake.bind(stakingWrapper), {
before: async (args: [BigNumber], txData: Partial<TxData>) => {
const [amount] = args;
// Simulates the transfer of ZRX from staker to vault
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.transferAsset(
txData.from as string,
txData.from!, // tslint:disable-line:no-non-null-assertion
zrxVault.address,
amount,
deployment.assetDataEncoder.ERC20Token(deployment.tokens.zrx.address).getABIEncodedTransactionData(),
@@ -48,9 +51,11 @@ export function validStakeAssertion(
after: async (
expectedBalances: LocalBalanceStore,
_result: FunctionResult,
amount: BigNumber,
args: [BigNumber],
txData: Partial<TxData>,
) => {
const [amount] = args;
logUtils.log(`stake(${amount})`);
// Checks that the ZRX transfer updated balances as expected.
@@ -59,7 +64,7 @@ export function validStakeAssertion(
// Checks that the owner's undelegated stake has increased by the stake amount
const ownerUndelegatedStake = await stakingWrapper
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
.getOwnerStakeByStatus(txData.from!, StakeStatus.Undelegated) // tslint:disable-line:no-non-null-assertion
.callAsync();
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
@@ -77,3 +82,4 @@ export function validStakeAssertion(
},
});
}
/* tslint:enable:no-unnecessary-type-assertion */

View File

@@ -25,21 +25,25 @@ function expectedUndelegatedStake(
* FunctionAssertion checks that the staker and zrxVault's balances of ZRX increase and decrease,
* respectively, by the input amount.
*/
/* tslint:disable:no-unnecessary-type-assertion */
/* tslint:disable:no-non-null-assertion */
export function validUnstakeAssertion(
deployment: DeploymentManager,
balanceStore: BlockchainBalanceStore,
globalStake: GlobalStakeByStatus,
ownerStake: OwnerStakeByStatus,
): FunctionAssertion<LocalBalanceStore, void> {
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
const { stakingWrapper, zrxVault } = deployment.staking;
return new FunctionAssertion(stakingWrapper.unstake, {
before: async (amount: BigNumber, txData: Partial<TxData>) => {
return new FunctionAssertion(stakingWrapper.unstake.bind(stakingWrapper), {
before: async (args: [BigNumber], txData: Partial<TxData>) => {
const [amount] = args;
// Simulates the transfer of ZRX from vault to staker
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.transferAsset(
zrxVault.address,
txData.from as string,
txData.from!,
amount,
deployment.assetDataEncoder.ERC20Token(deployment.tokens.zrx.address).getABIEncodedTransactionData(),
);
@@ -48,9 +52,11 @@ export function validUnstakeAssertion(
after: async (
expectedBalances: LocalBalanceStore,
_result: FunctionResult,
amount: BigNumber,
args: [BigNumber],
txData: Partial<TxData>,
) => {
const [amount] = args;
logUtils.log(`unstake(${amount})`);
// Checks that the ZRX transfer updated balances as expected.
@@ -59,7 +65,7 @@ export function validUnstakeAssertion(
// Checks that the owner's undelegated stake has decreased by the stake amount
const ownerUndelegatedStake = await stakingWrapper
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
.getOwnerStakeByStatus(txData.from!, StakeStatus.Undelegated)
.callAsync();
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
@@ -77,3 +83,5 @@ export function validUnstakeAssertion(
},
});
}
/* tslint:enable:no-non-null-assertion */
/* tslint:enable:no-unnecessary-type-assertion */

View File

@@ -1,6 +1,7 @@
import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking';
import * as _ from 'lodash';
import { Maker } from './actors/maker';
import { AssertionResult } from './assertions/function_assertion';
import { BlockchainBalanceStore } from './balances/blockchain_balance_store';
import { DeploymentManager } from './deployment_manager';
@@ -14,7 +15,11 @@ export class SimulationEnvironment {
};
public stakingPools: StakingPoolById = {};
public constructor(public readonly deployment: DeploymentManager, public balanceStore: BlockchainBalanceStore) {}
public constructor(
public readonly deployment: DeploymentManager,
public balanceStore: BlockchainBalanceStore,
public marketMakers: Maker[] = [],
) {}
}
export abstract class Simulation {

View File

@@ -1,6 +1,6 @@
import { blockchainTests, constants, expect, filterLogsToArguments, getRandomInteger } from '@0x/contracts-test-utils';
import { BigNumber, StringRevertError } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import { artifacts } from '../../artifacts';
import { TestFrameworkContract, TestFrameworkEventEventArgs, TestFrameworkEvents } from '../../wrappers';
@@ -23,84 +23,107 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
describe('executeAsync', () => {
it('should call the before function with the provided arguments', async () => {
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<void, BigNumber>(
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
before: async (_input: BigNumber) => {
before: async (args: [BigNumber], txData: Partial<TxData>) => {
sideEffectTarget = randomInput;
},
},
);
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
await assertion.executeAsync(randomInput);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
});
it('should call the after function with the provided arguments', async () => {
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<void, BigNumber>(
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
after: async (_beforeInfo: any, _result: FunctionResult, input: BigNumber) => {
sideEffectTarget = input;
after: async (
_beforeInfo: any,
_result: FunctionResult,
args: [BigNumber],
txData: Partial<TxData>,
) => {
[sideEffectTarget] = args;
},
},
);
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
await assertion.executeAsync(randomInput);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
});
it('should not fail immediately if the wrapped function fails', async () => {
const assertion = new FunctionAssertion<{}, void>(exampleContract.emptyRevert.bind(exampleContract));
await assertion.executeAsync();
const assertion = new FunctionAssertion<[], {}, void>(exampleContract.emptyRevert.bind(exampleContract));
await assertion.executeAsync([], {});
});
it('should pass the return value of "before" to "after"', async () => {
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<BigNumber, BigNumber>(
const assertion = new FunctionAssertion<[BigNumber], BigNumber, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
before: async (_input: BigNumber) => {
before: async (_args: [BigNumber], _txData: Partial<TxData>) => {
return randomInput;
},
after: async (beforeInfo: any, _result: FunctionResult, _input: BigNumber) => {
after: async (
beforeInfo: any,
_result: FunctionResult,
_args: [BigNumber],
_txData: Partial<TxData>,
) => {
sideEffectTarget = beforeInfo;
},
},
);
await assertion.executeAsync(randomInput);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
});
it('should pass the result from the function call to "after"', async () => {
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<void, BigNumber>(
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
after: async (_beforeInfo: any, result: FunctionResult, _input: BigNumber) => {
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [BigNumber],
_txData: Partial<TxData>,
) => {
sideEffectTarget = result.data;
},
},
);
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
await assertion.executeAsync(randomInput);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
});
it('should pass the receipt from the function call to "after"', async () => {
let sideEffectTarget: TransactionReceiptWithDecodedLogs;
const assertion = new FunctionAssertion<void, void>(exampleContract.emitEvent.bind(exampleContract), {
after: async (_beforeInfo: any, result: FunctionResult, _input: string) => {
if (result.receipt) {
sideEffectTarget = result.receipt;
}
const assertion = new FunctionAssertion<[string], void, void>(
exampleContract.emitEvent.bind(exampleContract),
{
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [string],
_txData: Partial<TxData>,
) => {
if (result.receipt) {
sideEffectTarget = result.receipt;
}
},
},
});
);
const input = 'emitted data';
await assertion.executeAsync(input);
await assertion.executeAsync([input], {});
// Ensure that the correct events were emitted.
const [event] = filterLogsToArguments<TestFrameworkEventEventArgs>(
@@ -112,13 +135,21 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
it('should pass the error to "after" if the function call fails', async () => {
let sideEffectTarget: Error;
const assertion = new FunctionAssertion<void, void>(exampleContract.stringRevert.bind(exampleContract), {
after: async (_beforeInfo: any, result: FunctionResult, _input: string) => {
sideEffectTarget = result.data;
const assertion = new FunctionAssertion<[string], void, void>(
exampleContract.stringRevert.bind(exampleContract),
{
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [string],
_txData: Partial<TxData>,
) => {
sideEffectTarget = result.data;
},
},
});
);
const message = 'error message';
await assertion.executeAsync(message);
await assertion.executeAsync([message], {});
const expectedError = new StringRevertError(message);
return expect(Promise.reject(sideEffectTarget!)).to.revertWith(expectedError); // tslint:disable-line

View File

@@ -0,0 +1,86 @@
import { blockchainTests, constants } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { MakerTaker } from '../framework/actors/hybrids';
import { Maker } from '../framework/actors/maker';
import { AssertionResult } from '../framework/assertions/function_assertion';
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
import { DeploymentManager } from '../framework/deployment_manager';
import { Simulation, SimulationEnvironment } from '../framework/simulation';
import { PoolManagementSimulation } from './pool_management_test';
class PoolMembershipSimulation extends Simulation {
protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
const { deployment } = this.environment;
const poolManagement = new PoolManagementSimulation(this.environment);
const member = new MakerTaker({
name: 'member',
deployment,
simulationEnvironment: this.environment,
});
const actions = [
member.simulationActions.validJoinStakingPool,
member.simulationActions.validFillOrderCompleteFill,
poolManagement.generator,
];
while (true) {
const action = _.sample(actions);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}
}
blockchainTests.skip('pool membership fuzz test', env => {
let deployment: DeploymentManager;
let maker: Maker;
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 2,
numErc721TokensToDeploy: 0,
numErc1155TokensToDeploy: 0,
});
const makerToken = deployment.tokens.erc20[0];
const takerToken = deployment.tokens.erc20[1];
const orderConfig = {
feeRecipientAddress: constants.NULL_ADDRESS,
makerAssetData: deployment.assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
takerAssetData: deployment.assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
makerFeeAssetData: deployment.assetDataEncoder
.ERC20Token(makerToken.address)
.getABIEncodedTransactionData(),
takerFeeAssetData: deployment.assetDataEncoder
.ERC20Token(takerToken.address)
.getABIEncodedTransactionData(),
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
};
maker = new Maker({
name: 'maker',
deployment,
orderConfig,
});
});
it('fuzz', async () => {
const balanceStore = new BlockchainBalanceStore(
{
StakingProxy: deployment.staking.stakingProxy.address,
ZRXVault: deployment.staking.zrxVault.address,
},
{ erc20: { ZRX: deployment.tokens.zrx } },
);
const simulationEnv = new SimulationEnvironment(deployment, balanceStore, [maker]);
const simulation = new PoolMembershipSimulation(simulationEnv);
return simulation.fuzzAsync();
});
});

147
yarn.lock
View File

@@ -668,12 +668,52 @@
lodash "^4.17.11"
valid-url "^1.0.9"
"@0x/assert@^2.2.0-beta.2", "@0x/assert@^2.2.0-beta.3":
version "2.2.0-beta.3"
resolved "https://registry.yarnpkg.com/@0x/assert/-/assert-2.2.0-beta.3.tgz#8fb95c265000532cd8dced44d44d29ca544b2bfc"
integrity sha512-ShENc8QJU4ur/5TkRl3l7J3Yt7WaxHAbuoTRm/djcA0iwYyUVlP5yN9Ab9ua+VLizQQTNw1n+kF1mJWg5lQXuA==
dependencies:
"@0x/json-schemas" "^4.1.0-beta.3"
"@0x/typescript-typings" "^4.4.0-beta.2"
"@0x/utils" "^4.6.0-beta.3"
lodash "^4.17.11"
valid-url "^1.0.9"
"@0x/base-contract@^5.5.0-beta.2", "@0x/base-contract@^5.5.0-beta.3", "@0x/base-contract@^5.5.0-beta.4":
version "5.5.0-beta.4"
resolved "https://registry.yarnpkg.com/@0x/base-contract/-/base-contract-5.5.0-beta.4.tgz#eb26473e033e1a305a9fa87ab9e26325c9face59"
integrity sha512-6OC3Rg2ESoi1k4wABLt67aMGxqdb6FraeEHdAGb07VuPElpyH9jT8dl2aJFAI8tVcnrUQTXeDYJjuK7rimNReg==
dependencies:
"@0x/assert" "^2.2.0-beta.3"
"@0x/json-schemas" "^4.1.0-beta.3"
"@0x/utils" "^4.6.0-beta.3"
"@0x/web3-wrapper" "^6.1.0-beta.3"
ethereumjs-account "^3.0.0"
ethereumjs-blockstream "^7.0.0"
ethereumjs-util "^5.1.1"
ethereumjs-vm "^4.0.0"
ethers "~4.0.4"
js-sha3 "^0.7.0"
uuid "^3.3.2"
"@0x/contract-addresses@3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@0x/contract-addresses/-/contract-addresses-3.3.0-beta.3.tgz#0fa8ad47e22aecdb99f9a044ba3c705c4173b61e"
dependencies:
lodash "^4.17.11"
"@0x/contract-addresses@^3.3.0-beta.3", "@0x/contract-addresses@^3.3.0-beta.4", "@0x/contract-addresses@^3.3.0-beta.5":
version "3.3.0-beta.5"
resolved "https://registry.yarnpkg.com/@0x/contract-addresses/-/contract-addresses-3.3.0-beta.5.tgz#9d5f80a258f1d103b127159c237f9bcdad182e80"
integrity sha512-/8de6W1MnVc2zElnCGjK3zgWabBAJZck+zCmNRIMiGtPJIWEW+F3EkAFPfMX6bSZIyrUBlYJIr0xVIWLPU45Aw==
dependencies:
lodash "^4.17.11"
"@0x/contract-artifacts@^2.3.0-beta.3":
version "2.3.0-beta.4"
resolved "https://registry.yarnpkg.com/@0x/contract-artifacts/-/contract-artifacts-2.3.0-beta.4.tgz#ca056885be387344aaccf5c69fe80aec248df37d"
integrity sha512-NpZk3PVE9c2g5kolCcZej2i1l1XlvCFm9FXAny0tCz+/vNb3RhI0m6ecoiS7b1nFEFZ9q6jjsCCb5OEsxMudnw==
"@0x/contract-wrappers@12.2.0-beta.2":
version "12.2.0-beta.2"
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-12.2.0-beta.2.tgz#62b3c13e35282df14734d1f6b1a617ed51901a27"
@@ -686,6 +726,28 @@
ethers "~4.0.4"
http-status-codes "^1.3.2"
"@0x/contract-wrappers@^12.2.0-beta.4":
version "12.2.0-beta.4"
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-12.2.0-beta.4.tgz#7a7301dd50c28887879df4d385e80a49e040748d"
integrity sha512-JVoYG3Rd430fZw9ogBSqeLOaJXkqp9N7g614X5/bzzuG/dSDdwXl48X02m66aGFWcNofx/iMsT4tpOZrJ2bDBg==
dependencies:
"@0x/assert" "^2.2.0-beta.3"
"@0x/base-contract" "^5.5.0-beta.4"
"@0x/contract-addresses" "^3.3.0-beta.5"
"@0x/json-schemas" "^4.1.0-beta.3"
"@0x/types" "^2.5.0-beta.3"
"@0x/utils" "^4.6.0-beta.3"
"@0x/web3-wrapper" "^6.1.0-beta.3"
ethereum-types "^2.2.0-beta.2"
ethers "~4.0.4"
"@0x/contracts-dev-utils@^0.1.0-beta.2":
version "0.1.0-beta.4"
resolved "https://registry.yarnpkg.com/@0x/contracts-dev-utils/-/contracts-dev-utils-0.1.0-beta.4.tgz#87f51a7ed778b619beb8b32bf08ea6b3e4495d4e"
integrity sha512-6fQ13/L8aKKnO/vs2WIg0D7FXz3QpMKBHrTYdzvpwFrOcd6MPavQEj0KCQdT/4aplMvWnWDSVAOqn8i/lJJ8jQ==
dependencies:
"@0x/base-contract" "^5.5.0-beta.4"
"@0x/contracts-erc20@2.3.0-beta.2":
version "2.3.0-beta.2"
resolved "https://registry.yarnpkg.com/@0x/contracts-erc20/-/contracts-erc20-2.3.0-beta.2.tgz#218239f5594fdbbf8c1ff757a6356ac6fb787421"
@@ -734,6 +796,16 @@
jsonschema "^1.2.0"
lodash.values "^4.3.0"
"@0x/json-schemas@^4.1.0-beta.2", "@0x/json-schemas@^4.1.0-beta.3":
version "4.1.0-beta.3"
resolved "https://registry.yarnpkg.com/@0x/json-schemas/-/json-schemas-4.1.0-beta.3.tgz#af70a35691108ea162140640bae93a7fc84ca6ee"
integrity sha512-vcgzSeaOXiUQ4KjqdLTTBHbkWnp4IE7cXbUblRy8Y0XYPQsPywhs9mtjY4lBVNmm1DDpLhreo1mwrvPS3HW5YA==
dependencies:
"@0x/typescript-typings" "^4.4.0-beta.2"
"@types/node" "*"
jsonschema "^1.2.0"
lodash.values "^4.3.0"
"@0x/mesh-rpc-client@^7.0.4-beta-0xv3":
version "7.0.4-beta-0xv3"
resolved "https://registry.yarnpkg.com/@0x/mesh-rpc-client/-/mesh-rpc-client-7.0.4-beta-0xv3.tgz#5e933a0b9cf20ca900f309fc4adee03b081eb335"
@@ -769,6 +841,20 @@
ethers "~4.0.4"
lodash "^4.17.11"
"@0x/order-utils@^8.5.0-beta.2":
version "8.5.0-beta.4"
resolved "https://registry.yarnpkg.com/@0x/order-utils/-/order-utils-8.5.0-beta.4.tgz#900387631008cc9dc9ece125d28450d2beaea462"
integrity sha512-a3vFDAETaPVo/hN5iFr1gxgshfSzAO23NnaUSjeEhs4Ff8eKxuISc6UARFaNzdQeXygDWHMjwlmZ7iegBHe93g==
dependencies:
"@0x/assert" "^2.2.0-beta.3"
"@0x/contract-wrappers" "^12.2.0-beta.4"
"@0x/json-schemas" "^4.1.0-beta.3"
"@0x/utils" "^4.6.0-beta.3"
"@0x/web3-wrapper" "^6.1.0-beta.3"
ethereumjs-util "^5.1.1"
ethers "~4.0.4"
lodash "^4.17.11"
"@0x/subproviders@5.1.0-beta.2":
version "5.1.0-beta.2"
resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-5.1.0-beta.2.tgz#020369711330755448397b3b8cecf2868ae7c54c"
@@ -827,6 +913,26 @@
bignumber.js "~8.0.2"
ethereum-types "^2.1.6"
"@0x/types@^2.5.0-beta.2", "@0x/types@^2.5.0-beta.3":
version "2.5.0-beta.3"
resolved "https://registry.yarnpkg.com/@0x/types/-/types-2.5.0-beta.3.tgz#e010e9dbf62e37e59177c1d6df8d1acf3a9ea1b4"
integrity sha512-5wJs4/EZGPcU6W5IZ87zuya9vQUPD4DchyP29bXyguGHg9dOxuUOF4WauJZExWlPCS7eivviiUHpZD9DZhni+w==
dependencies:
"@types/node" "*"
bignumber.js "~9.0.0"
ethereum-types "^2.2.0-beta.2"
"@0x/typescript-typings@4.4.0-beta.2", "@0x/typescript-typings@^4.4.0-beta.2":
version "4.4.0-beta.2"
resolved "https://registry.yarnpkg.com/@0x/typescript-typings/-/typescript-typings-4.4.0-beta.2.tgz#67c621252f162914186b8f684ac5e306206c1cf2"
integrity sha512-Fq2nOKvopdLMEjuPiKqomGog06bxAXGjqnodCwv9OKr11V5W1twFTUM3c1TENfHeGvcqf1aMl1hsH3fuVP61jg==
dependencies:
"@types/bn.js" "^4.11.0"
"@types/react" "*"
bignumber.js "~9.0.0"
ethereum-types "^2.2.0-beta.2"
popper.js "1.14.3"
"@0x/typescript-typings@^4.2.2", "@0x/typescript-typings@^4.3.0":
version "4.3.0"
resolved "https://registry.npmjs.org/@0x/typescript-typings/-/typescript-typings-4.3.0.tgz#4813a996ac5101841d1c22f4aa1738ab56168857"
@@ -874,6 +980,25 @@
js-sha3 "^0.7.0"
lodash "^4.17.11"
"@0x/utils@^4.6.0-beta.2", "@0x/utils@^4.6.0-beta.3":
version "4.6.0-beta.3"
resolved "https://registry.yarnpkg.com/@0x/utils/-/utils-4.6.0-beta.3.tgz#d40278916d98c48ea05821ae4987c88f032c7bff"
integrity sha512-aPIUgfhaDhwgddJAlIQJ2Ki87A60ovatBLCjareLUbsQSvFS5i3iujUBQHgFxZAv9tgl35fyg2ISEJ1YkQyubA==
dependencies:
"@0x/types" "^2.5.0-beta.3"
"@0x/typescript-typings" "^4.4.0-beta.2"
"@types/node" "*"
abortcontroller-polyfill "^1.1.9"
bignumber.js "~9.0.0"
chalk "^2.3.0"
detect-node "2.0.3"
ethereum-types "^2.2.0-beta.2"
ethereumjs-util "^5.1.1"
ethers "~4.0.4"
isomorphic-fetch "2.2.1"
js-sha3 "^0.7.0"
lodash "^4.17.11"
"@0x/web3-providers-fork@0.0.7":
version "0.0.7"
resolved "https://registry.yarnpkg.com/@0x/web3-providers-fork/-/web3-providers-fork-0.0.7.tgz#9cf40ebb6a2aa230283c5accb195d92594bb0aa7"
@@ -904,6 +1029,20 @@
ethers "~4.0.4"
lodash "^4.17.11"
"@0x/web3-wrapper@^6.1.0-beta.2", "@0x/web3-wrapper@^6.1.0-beta.3":
version "6.1.0-beta.3"
resolved "https://registry.yarnpkg.com/@0x/web3-wrapper/-/web3-wrapper-6.1.0-beta.3.tgz#82161147e9283391e0c7cd6027c971749c5a2f77"
integrity sha512-mc8120n8w88gICbDm8pkmC83Ul3RgE4BGsjY5BRBFefmKbv/XLeBZiWdhsaWYmkk8v4f+ZxAQ+HHTBDsRH87Og==
dependencies:
"@0x/assert" "^2.2.0-beta.3"
"@0x/json-schemas" "^4.1.0-beta.3"
"@0x/typescript-typings" "^4.4.0-beta.2"
"@0x/utils" "^4.6.0-beta.3"
ethereum-types "^2.2.0-beta.2"
ethereumjs-util "^5.1.1"
ethers "~4.0.4"
lodash "^4.17.11"
"@0xproject/npm-cli-login@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@0xproject/npm-cli-login/-/npm-cli-login-0.0.11.tgz#3f1ec06112ce62aad300ff0575358f68aeecde2e"
@@ -6626,6 +6765,14 @@ ethereum-types@^2.1.6:
"@types/node" "*"
bignumber.js "~8.0.2"
ethereum-types@^2.2.0-beta.2:
version "2.2.0-beta.2"
resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-2.2.0-beta.2.tgz#0b446842474c2afacd351258ed4a2d0841f2608f"
integrity sha512-5ANYHI/InHqf4Nt8oYrpvcph9/D6gi3sbM7Rlr8r0QjXb2mqocqEvOH460Zkf1robc7WDqurp9baeMy+um8kww==
dependencies:
"@types/node" "*"
bignumber.js "~9.0.0"
ethereumjs-abi@0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241"