diff --git a/contracts/integrations/test/framework/actors/hybrids.ts b/contracts/integrations/test/framework/actors/hybrids.ts index 7265b0fb93..51a73a43b2 100644 --- a/contracts/integrations/test/framework/actors/hybrids.ts +++ b/contracts/integrations/test/framework/actors/hybrids.ts @@ -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)) {} diff --git a/contracts/integrations/test/framework/actors/maker.ts b/contracts/integrations/test/framework/actors/maker.ts index e701e29306..b0dbdf9077 100644 --- a/contracts/integrations/test/framework/actors/maker.ts +++ b/contracts/integrations/test/framework/actors/maker.ts @@ -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(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(Base: TBase): TBase & Cons from: this.actor.address, }); } + + private async *_validJoinStakingPool(): AsyncIterableIterator { + 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 }); + } + } + } }; } diff --git a/contracts/integrations/test/framework/actors/pool_operator.ts b/contracts/integrations/test/framework/actors/pool_operator.ts index 19d9e06d12..176e26fd06 100644 --- a/contracts/integrations/test/framework/actors/pool_operator.ts +++ b/contracts/integrations/test/framework/actors/pool_operator.ts @@ -83,8 +83,8 @@ export function PoolOperatorMixin(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(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 }); } } } diff --git a/contracts/integrations/test/framework/actors/staker.ts b/contracts/integrations/test/framework/actors/staker.ts index 1261933b85..b92f98475d 100644 --- a/contracts/integrations/test/framework/actors/staker.ts +++ b/contracts/integrations/test/framework/actors/staker.ts @@ -76,7 +76,7 @@ export function StakerMixin(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(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(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 }); } } }; diff --git a/contracts/integrations/test/framework/actors/taker.ts b/contracts/integrations/test/framework/actors/taker.ts index 65672f9a52..ca545966cd 100644 --- a/contracts/integrations/test/framework/actors/taker.ts +++ b/contracts/integrations/test/framework/actors/taker.ts @@ -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(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(Base: TBase): TBase & Cons ...txData, }); } + + private async *_validFillOrderCompleteFill(): AsyncIterableIterator { + 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, + }); + } + } + } }; } diff --git a/contracts/integrations/test/framework/assertions/createStakingPool.ts b/contracts/integrations/test/framework/assertions/createStakingPool.ts index a1829a0e62..6748b44271 100644 --- a/contracts/integrations/test/framework/assertions/createStakingPool.ts +++ b/contracts/integrations/test/framework/assertions/createStakingPool.ts @@ -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 { +): 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, - ) => { - 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, + ) => { + 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*/ diff --git a/contracts/integrations/test/framework/assertions/decreaseStakingPoolOperatorShare.ts b/contracts/integrations/test/framework/assertions/decreaseStakingPoolOperatorShare.ts index dfcee98ad2..9a6ff1a0c4 100644 --- a/contracts/integrations/test/framework/assertions/decreaseStakingPoolOperatorShare.ts +++ b/contracts/integrations/test/framework/assertions/decreaseStakingPoolOperatorShare.ts @@ -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) => { + 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; + }, }, - }); + ); } diff --git a/contracts/integrations/test/framework/assertions/fillOrder.ts b/contracts/integrations/test/framework/assertions/fillOrder.ts new file mode 100644 index 0000000000..a2db06e2c0 --- /dev/null +++ b/contracts/integrations/test/framework/assertions/fillOrder.ts @@ -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( + 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( + 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, + ) => { + 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 */ diff --git a/contracts/integrations/test/framework/assertions/function_assertion.ts b/contracts/integrations/test/framework/assertions/function_assertion.ts index 541e57ce12..ff810da3f5 100644 --- a/contracts/integrations/test/framework/assertions/function_assertion.ts +++ b/contracts/integrations/test/framework/assertions/function_assertion.ts @@ -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 = (...args: any[]) => ContractFunctionObj; 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 { - before: (...args: any[]) => Promise; - after: (beforeInfo: TBefore, result: FunctionResult, ...args: any[]) => Promise; +export interface Condition { + before: (args: TArgs, txData: Partial) => Promise; + after: (beforeInfo: TBefore, result: FunctionResult, args: TArgs, txData: Partial) => Promise; } /** @@ -34,8 +33,8 @@ export interface Condition { * our `Assertion` implementations will do in practice). * @param runAsync The function to execute for the assertion. */ -export interface Assertion { - executeAsync: (...args: any[]) => Promise; +export interface Assertion { + executeAsync: (args: TArgs, txData: TxData) => Promise; } export interface AssertionResult { @@ -47,24 +46,28 @@ export interface AssertionResult { * This class implements `Assertion` and represents a "Hoare Triple" that can be * executed. */ -export class FunctionAssertion implements Assertion { +export class FunctionAssertion implements Assertion { // A condition that will be applied to `wrapperFunction`. - public condition: Condition; + public condition: Condition; // 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 | ContractFunctionObj; constructor( wrapperFunction: ( - ...args: any[] // tslint:disable-line:trailing-comma + ...args: TArgs // tslint:disable-line:trailing-comma ) => ContractTxFunctionObj | ContractFunctionObj, - condition: Partial> = {}, + condition: Partial> = {}, ) { this.condition = { - before: _.noop.bind(this), - after: _.noop.bind(this), + before: async (args: TArgs, txData: Partial) => { + return ({} as any) as TBefore; + }, + after: async (beforeInfo: TBefore, result: FunctionResult, args: TArgs, txData: Partial) => { + return ({} as any) as TBefore; + }, ...condition, }; this.wrapperFunction = wrapperFunction; @@ -74,9 +77,9 @@ export class FunctionAssertion 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> { + public async executeAsync(args: TArgs, txData: Partial): Promise> { // 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 implements Assertion { // result and receipt to the after condition. try { const functionWithArgs = this.wrapperFunction(...args) as ContractTxFunctionObj; - 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 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, diff --git a/contracts/integrations/test/framework/assertions/joinStakingPool.ts b/contracts/integrations/test/framework/assertions/joinStakingPool.ts new file mode 100644 index 0000000000..478268ca63 --- /dev/null +++ b/contracts/integrations/test/framework/assertions/joinStakingPool.ts @@ -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) => { + const [poolId] = args; + + expect(_result.success).to.be.true(); + + const logs = _result.receipt!.logs; + const logArgs = filterLogsToArguments( + 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 */ diff --git a/contracts/integrations/test/framework/assertions/moveStake.ts b/contracts/integrations/test/framework/assertions/moveStake.ts index 08d27627fe..f65d98ab41 100644 --- a/contracts/integrations/test/framework/assertions/moveStake.ts +++ b/contracts/integrations/test/framework/assertions/moveStake.ts @@ -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, - ) => { - 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, + ) => { + 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 */ diff --git a/contracts/integrations/test/framework/assertions/stake.ts b/contracts/integrations/test/framework/assertions/stake.ts index 2affd8a194..ac88003f0c 100644 --- a/contracts/integrations/test/framework/assertions/stake.ts +++ b/contracts/integrations/test/framework/assertions/stake.ts @@ -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 { +): FunctionAssertion<[BigNumber], LocalBalanceStore, void> { const { stakingWrapper, zrxVault } = deployment.staking; - return new FunctionAssertion(stakingWrapper.stake, { - before: async (amount: BigNumber, txData: Partial) => { + return new FunctionAssertion(stakingWrapper.stake.bind(stakingWrapper), { + before: async (args: [BigNumber], txData: Partial) => { + 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, ) => { + 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 */ diff --git a/contracts/integrations/test/framework/assertions/unstake.ts b/contracts/integrations/test/framework/assertions/unstake.ts index 5644ba5e39..6bfc6363fe 100644 --- a/contracts/integrations/test/framework/assertions/unstake.ts +++ b/contracts/integrations/test/framework/assertions/unstake.ts @@ -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 { +): FunctionAssertion<[BigNumber], LocalBalanceStore, void> { const { stakingWrapper, zrxVault } = deployment.staking; - return new FunctionAssertion(stakingWrapper.unstake, { - before: async (amount: BigNumber, txData: Partial) => { + return new FunctionAssertion(stakingWrapper.unstake.bind(stakingWrapper), { + before: async (args: [BigNumber], txData: Partial) => { + 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, ) => { + 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 */ diff --git a/contracts/integrations/test/framework/simulation.ts b/contracts/integrations/test/framework/simulation.ts index 5bc94a6625..1ae337cb7f 100644 --- a/contracts/integrations/test/framework/simulation.ts +++ b/contracts/integrations/test/framework/simulation.ts @@ -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 { diff --git a/contracts/integrations/test/framework/tests/function_assertion_test.ts b/contracts/integrations/test/framework/tests/function_assertion_test.ts index e585d17d1c..9e9e746b46 100644 --- a/contracts/integrations/test/framework/tests/function_assertion_test.ts +++ b/contracts/integrations/test/framework/tests/function_assertion_test.ts @@ -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( + const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>( exampleContract.returnInteger.bind(exampleContract), { - before: async (_input: BigNumber) => { + before: async (args: [BigNumber], txData: Partial) => { 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( + 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, + ) => { + [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( + const assertion = new FunctionAssertion<[BigNumber], BigNumber, BigNumber>( exampleContract.returnInteger.bind(exampleContract), { - before: async (_input: BigNumber) => { + before: async (_args: [BigNumber], _txData: Partial) => { return randomInput; }, - after: async (beforeInfo: any, _result: FunctionResult, _input: BigNumber) => { + after: async ( + beforeInfo: any, + _result: FunctionResult, + _args: [BigNumber], + _txData: Partial, + ) => { 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( + 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, + ) => { 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(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, + ) => { + 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( @@ -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(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, + ) => { + 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 diff --git a/contracts/integrations/test/fuzz_tests/pool_membership_test.ts b/contracts/integrations/test/fuzz_tests/pool_membership_test.ts new file mode 100644 index 0000000000..00670f18d9 --- /dev/null +++ b/contracts/integrations/test/fuzz_tests/pool_membership_test.ts @@ -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 { + 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(); + }); +}); diff --git a/yarn.lock b/yarn.lock index ad0359a900..699f0e744f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"