aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/test/utils
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2018-10-05 07:06:05 +0800
committerBrandon Millman <brandon.millman@gmail.com>2018-10-05 07:06:05 +0800
commite5153737d8386380675f28dd7cda70deeb1ea37c (patch)
tree81b061d2fa1af5952acc5abb41003f043ff8fce1 /packages/contracts/test/utils
parent88766a02c7e6688e72d5c4c69ce68028b322f154 (diff)
parentb04b649ec044b05f5c37bec214b7f992feb5998e (diff)
downloaddexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.tar
dexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.tar.gz
dexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.tar.bz2
dexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.tar.lz
dexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.tar.xz
dexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.tar.zst
dexon-sol-tools-e5153737d8386380675f28dd7cda70deeb1ea37c.zip
Merge branch 'development'
* development: (939 commits) Add asset-buyer to published packages section in README Publish Updated CHANGELOGS Update BuyQuote interface force re-build Add website build to instructions Revert format and re-add changes Build website in parallel with other tests since no other test relies on it being built to run Add back sourceMap support for both dev/prod Upgrade webpack Add missing default options Remove unused constants Add fee order with a takerFee Add additional order factory methods and refactor test to use them Add comments about buy quote calculation Update CHANGELOG Fix linter Add additional test for slippage Add buy_quote_calculator_test Add 0x Instant to bundle analysis ...
Diffstat (limited to 'packages/contracts/test/utils')
-rw-r--r--packages/contracts/test/utils/address_utils.ts3
-rw-r--r--packages/contracts/test/utils/artifacts.ts14
-rw-r--r--packages/contracts/test/utils/assertions.ts17
-rw-r--r--packages/contracts/test/utils/block_timestamp.ts7
-rw-r--r--packages/contracts/test/utils/constants.ts13
-rw-r--r--packages/contracts/test/utils/erc20_wrapper.ts7
-rw-r--r--packages/contracts/test/utils/erc721_wrapper.ts3
-rw-r--r--packages/contracts/test/utils/exchange_wrapper.ts5
-rw-r--r--packages/contracts/test/utils/fill_order_combinatorial_utils.ts25
-rw-r--r--packages/contracts/test/utils/forwarder_wrapper.ts2
-rw-r--r--packages/contracts/test/utils/log_decoder.ts7
-rw-r--r--packages/contracts/test/utils/match_order_tester.ts500
-rw-r--r--packages/contracts/test/utils/multi_sig_wrapper.ts17
-rw-r--r--packages/contracts/test/utils/order_factory_from_scenario.ts23
-rw-r--r--packages/contracts/test/utils/order_utils.ts2
-rw-r--r--packages/contracts/test/utils/test_with_reference.ts72
-rw-r--r--packages/contracts/test/utils/transaction_factory.ts6
-rw-r--r--packages/contracts/test/utils/types.ts18
-rw-r--r--packages/contracts/test/utils/web3_wrapper.ts4
19 files changed, 550 insertions, 195 deletions
diff --git a/packages/contracts/test/utils/address_utils.ts b/packages/contracts/test/utils/address_utils.ts
index a9fb6921a..3cf8ec786 100644
--- a/packages/contracts/test/utils/address_utils.ts
+++ b/packages/contracts/test/utils/address_utils.ts
@@ -1,4 +1,5 @@
-import { crypto, generatePseudoRandomSalt } from '@0xproject/order-utils';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
+import { crypto } from '@0xproject/order-utils/lib/src/crypto';
export const addressUtils = {
generatePseudoRandomAddress(): string {
diff --git a/packages/contracts/test/utils/artifacts.ts b/packages/contracts/test/utils/artifacts.ts
index e608ee174..53f2a4e4e 100644
--- a/packages/contracts/test/utils/artifacts.ts
+++ b/packages/contracts/test/utils/artifacts.ts
@@ -1,18 +1,23 @@
-import { ContractArtifact } from '@0xproject/sol-compiler';
+import { ContractArtifact } from 'ethereum-types';
import * as AssetProxyOwner from '../../artifacts/AssetProxyOwner.json';
import * as DummyERC20Token from '../../artifacts/DummyERC20Token.json';
import * as DummyERC721Receiver from '../../artifacts/DummyERC721Receiver.json';
import * as DummyERC721Token from '../../artifacts/DummyERC721Token.json';
+import * as DummyMultipleReturnERC20Token from '../../artifacts/DummyMultipleReturnERC20Token.json';
+import * as DummyNoReturnERC20Token from '../../artifacts/DummyNoReturnERC20Token.json';
import * as ERC20Proxy from '../../artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../../artifacts/ERC721Proxy.json';
import * as Exchange from '../../artifacts/Exchange.json';
import * as ExchangeWrapper from '../../artifacts/ExchangeWrapper.json';
import * as Forwarder from '../../artifacts/Forwarder.json';
import * as IAssetProxy from '../../artifacts/IAssetProxy.json';
+import * as InvalidERC721Receiver from '../../artifacts/InvalidERC721Receiver.json';
import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json';
+import * as OrderValidator from '../../artifacts/OrderValidator.json';
+import * as ReentrantERC20Token from '../../artifacts/ReentrantERC20Token.json';
import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json';
import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.json';
import * as TestConstants from '../../artifacts/TestConstants.json';
@@ -20,6 +25,7 @@ import * as TestExchangeInternals from '../../artifacts/TestExchangeInternals.js
import * as TestLibBytes from '../../artifacts/TestLibBytes.json';
import * as TestLibs from '../../artifacts/TestLibs.json';
import * as TestSignatureValidator from '../../artifacts/TestSignatureValidator.json';
+import * as TestStaticCallReceiver from '../../artifacts/TestStaticCallReceiver.json';
import * as TokenRegistry from '../../artifacts/TokenRegistry.json';
import * as Validator from '../../artifacts/Validator.json';
import * as Wallet from '../../artifacts/Wallet.json';
@@ -32,6 +38,8 @@ export const artifacts = {
DummyERC20Token: (DummyERC20Token as any) as ContractArtifact,
DummyERC721Receiver: (DummyERC721Receiver as any) as ContractArtifact,
DummyERC721Token: (DummyERC721Token as any) as ContractArtifact,
+ DummyMultipleReturnERC20Token: (DummyMultipleReturnERC20Token as any) as ContractArtifact,
+ DummyNoReturnERC20Token: (DummyNoReturnERC20Token as any) as ContractArtifact,
ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
ERC721Proxy: (ERC721Proxy as any) as ContractArtifact,
Exchange: (Exchange as any) as ContractArtifact,
@@ -39,9 +47,12 @@ export const artifacts = {
EtherToken: (EtherToken as any) as ContractArtifact,
Forwarder: (Forwarder as any) as ContractArtifact,
IAssetProxy: (IAssetProxy as any) as ContractArtifact,
+ InvalidERC721Receiver: (InvalidERC721Receiver as any) as ContractArtifact,
MixinAuthorizable: (MixinAuthorizable as any) as ContractArtifact,
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
+ OrderValidator: (OrderValidator as any) as ContractArtifact,
+ ReentrantERC20Token: (ReentrantERC20Token as any) as ContractArtifact,
TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact,
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
TestConstants: (TestConstants as any) as ContractArtifact,
@@ -49,6 +60,7 @@ export const artifacts = {
TestLibs: (TestLibs as any) as ContractArtifact,
TestExchangeInternals: (TestExchangeInternals as any) as ContractArtifact,
TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact,
+ TestStaticCallReceiver: (TestStaticCallReceiver as any) as ContractArtifact,
Validator: (Validator as any) as ContractArtifact,
Wallet: (Wallet as any) as ContractArtifact,
TokenRegistry: (TokenRegistry as any) as ContractArtifact,
diff --git a/packages/contracts/test/utils/assertions.ts b/packages/contracts/test/utils/assertions.ts
index 61df800c8..3361a751a 100644
--- a/packages/contracts/test/utils/assertions.ts
+++ b/packages/contracts/test/utils/assertions.ts
@@ -159,7 +159,7 @@ export async function expectTransactionFailedWithoutReasonAsync(p: sendTransacti
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
-export async function expectContractCallFailed<T>(p: Promise<T>, reason: RevertReason): Promise<void> {
+export async function expectContractCallFailedAsync<T>(p: Promise<T>, reason: RevertReason): Promise<void> {
return expect(p).to.be.rejectedWith(reason);
}
@@ -180,7 +180,20 @@ export async function expectContractCallFailedWithoutReasonAsync<T>(p: Promise<T
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
-export async function expectContractCreationFailedWithoutReason<T>(p: Promise<T>): Promise<void> {
+export async function expectContractCreationFailedAsync<T>(
+ p: sendTransactionResult,
+ reason: RevertReason,
+): Promise<void> {
+ return expectTransactionFailedAsync(p, reason);
+}
+
+/**
+ * Resolves if the contract creation/deployment fails without a revert reason.
+ * @param p a Promise resulting from a contract creation/deployment
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export async function expectContractCreationFailedWithoutReasonAsync<T>(p: Promise<T>): Promise<void> {
const errMessage = await _getTransactionFailedErrorMessageAsync();
return expect(p).to.be.rejectedWith(errMessage);
}
diff --git a/packages/contracts/test/utils/block_timestamp.ts b/packages/contracts/test/utils/block_timestamp.ts
index 1159792c4..66c13eed1 100644
--- a/packages/contracts/test/utils/block_timestamp.ts
+++ b/packages/contracts/test/utils/block_timestamp.ts
@@ -35,6 +35,9 @@ export async function increaseTimeAndMineBlockAsync(seconds: number): Promise<nu
* @returns a new Promise which will resolve with the timestamp in seconds.
*/
export async function getLatestBlockTimestampAsync(): Promise<number> {
- const currentBlock = await web3Wrapper.getBlockAsync('latest');
- return currentBlock.timestamp;
+ const currentBlockIfExists = await web3Wrapper.getBlockIfExistsAsync('latest');
+ if (_.isUndefined(currentBlockIfExists)) {
+ throw new Error(`Unable to fetch latest block.`);
+ }
+ return currentBlockIfExists.timestamp;
}
diff --git a/packages/contracts/test/utils/constants.ts b/packages/contracts/test/utils/constants.ts
index 65eaee398..b9ba8ccb9 100644
--- a/packages/contracts/test/utils/constants.ts
+++ b/packages/contracts/test/utils/constants.ts
@@ -51,4 +51,17 @@ export const constants = {
WORD_LENGTH: 32,
ZERO_AMOUNT: new BigNumber(0),
PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18),
+ FUNCTIONS_WITH_MUTEX: [
+ 'FILL_ORDER',
+ 'FILL_OR_KILL_ORDER',
+ 'BATCH_FILL_ORDERS',
+ 'BATCH_FILL_OR_KILL_ORDERS',
+ 'MARKET_BUY_ORDERS',
+ 'MARKET_SELL_ORDERS',
+ 'MATCH_ORDERS',
+ 'CANCEL_ORDER',
+ 'BATCH_CANCEL_ORDERS',
+ 'CANCEL_ORDERS_UP_TO',
+ 'SET_SIGNATURE_VALIDATOR_APPROVAL',
+ ],
};
diff --git a/packages/contracts/test/utils/erc20_wrapper.ts b/packages/contracts/test/utils/erc20_wrapper.ts
index 424aae579..526ffe9f8 100644
--- a/packages/contracts/test/utils/erc20_wrapper.ts
+++ b/packages/contracts/test/utils/erc20_wrapper.ts
@@ -20,6 +20,13 @@ export class ERC20Wrapper {
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
private _proxyContract?: ERC20ProxyContract;
private _proxyIdIfExists?: string;
+ /**
+ * Instanitates an ERC20Wrapper
+ * @param provider Web3 provider to use for all JSON RPC requests
+ * @param tokenOwnerAddresses Addresses that we want to endow as owners for dummy ERC20 tokens
+ * @param contractOwnerAddress Desired owner of the contract
+ * Instance of ERC20Wrapper
+ */
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
this._dummyTokenContracts = [];
this._web3Wrapper = new Web3Wrapper(provider);
diff --git a/packages/contracts/test/utils/erc721_wrapper.ts b/packages/contracts/test/utils/erc721_wrapper.ts
index d023b4d02..743d10706 100644
--- a/packages/contracts/test/utils/erc721_wrapper.ts
+++ b/packages/contracts/test/utils/erc721_wrapper.ts
@@ -81,7 +81,8 @@ export class ERC721Wrapper {
}
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
- const doesExist = await tokenContract.exists.callAsync(tokenId);
+ const owner = await tokenContract.ownerOf.callAsync(tokenId);
+ const doesExist = owner !== constants.NULL_ADDRESS;
return doesExist;
}
public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise<void> {
diff --git a/packages/contracts/test/utils/exchange_wrapper.ts b/packages/contracts/test/utils/exchange_wrapper.ts
index d57592d6d..619d43994 100644
--- a/packages/contracts/test/utils/exchange_wrapper.ts
+++ b/packages/contracts/test/utils/exchange_wrapper.ts
@@ -17,7 +17,7 @@ export class ExchangeWrapper {
constructor(exchangeContract: ExchangeContract, provider: Provider) {
this._exchange = exchangeContract;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, this._exchange.address);
+ this._logDecoder = new LogDecoder(this._web3Wrapper);
}
public async fillOrderAsync(
signedOrder: SignedOrder,
@@ -266,4 +266,7 @@ export class ExchangeWrapper {
);
return data;
}
+ public getExchangeAddress(): string {
+ return this._exchange.address;
+ }
}
diff --git a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts b/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
index 284c4a2db..92d0f4003 100644
--- a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
+++ b/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
@@ -81,6 +81,12 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
erc20FiveDecimalTokenCount,
fiveDecimals,
);
+ const zeroDecimals = new BigNumber(0);
+ const erc20ZeroDecimalTokenCount = 2;
+ const [erc20ZeroDecimalTokenA, erc20ZeroDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync(
+ erc20ZeroDecimalTokenCount,
+ zeroDecimals,
+ );
const erc20Proxy = await erc20Wrapper.deployProxyAsync();
await erc20Wrapper.setBalancesAndAllowancesAsync();
@@ -119,6 +125,7 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
zrxToken.address,
[erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address],
[erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address],
+ [erc20ZeroDecimalTokenA.address, erc20ZeroDecimalTokenB.address],
erc721Token,
erc721Balances,
exchangeContract.address,
@@ -460,17 +467,17 @@ export class FillOrderCombinatorialUtils {
? remainingTakerAmountToFill
: alreadyFilledTakerAmount.add(takerAssetFillAmount);
- const expFilledMakerAmount = orderUtils.getPartialAmount(
+ const expFilledMakerAmount = orderUtils.getPartialAmountFloor(
expFilledTakerAmount,
signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount,
);
- const expMakerFeePaid = orderUtils.getPartialAmount(
+ const expMakerFeePaid = orderUtils.getPartialAmountFloor(
expFilledTakerAmount,
signedOrder.takerAssetAmount,
signedOrder.makerFee,
);
- const expTakerFeePaid = orderUtils.getPartialAmount(
+ const expTakerFeePaid = orderUtils.getPartialAmountFloor(
expFilledTakerAmount,
signedOrder.takerAssetAmount,
signedOrder.takerFee,
@@ -497,7 +504,11 @@ export class FillOrderCombinatorialUtils {
const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash);
expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount');
- expect(txReceipt.logs.length).to.be.equal(1, 'logs length');
+ const exchangeLogs = _.filter(
+ txReceipt.logs,
+ txLog => txLog.address === this.exchangeWrapper.getExchangeAddress(),
+ );
+ expect(exchangeLogs.length).to.be.equal(1, 'logs length');
// tslint:disable-next-line:no-unnecessary-type-assertion
const log = txReceipt.logs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>;
expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress');
@@ -657,7 +668,7 @@ export class FillOrderCombinatorialUtils {
signedOrder: SignedOrder,
takerAssetFillAmount: BigNumber,
): Promise<void> {
- const makerAssetFillAmount = orderUtils.getPartialAmount(
+ const makerAssetFillAmount = orderUtils.getPartialAmountFloor(
takerAssetFillAmount,
signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount,
@@ -694,7 +705,7 @@ export class FillOrderCombinatorialUtils {
);
}
- const makerFee = orderUtils.getPartialAmount(
+ const makerFee = orderUtils.getPartialAmountFloor(
takerAssetFillAmount,
signedOrder.takerAssetAmount,
signedOrder.makerFee,
@@ -818,7 +829,7 @@ export class FillOrderCombinatorialUtils {
);
}
- const takerFee = orderUtils.getPartialAmount(
+ const takerFee = orderUtils.getPartialAmountFloor(
takerAssetFillAmount,
signedOrder.takerAssetAmount,
signedOrder.takerFee,
diff --git a/packages/contracts/test/utils/forwarder_wrapper.ts b/packages/contracts/test/utils/forwarder_wrapper.ts
index 5b9a63ddf..de247a878 100644
--- a/packages/contracts/test/utils/forwarder_wrapper.ts
+++ b/packages/contracts/test/utils/forwarder_wrapper.ts
@@ -58,7 +58,7 @@ export class ForwarderWrapper {
constructor(contractInstance: ForwarderContract, provider: Provider) {
this._forwarderContract = contractInstance;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, this._forwarderContract.address);
+ this._logDecoder = new LogDecoder(this._web3Wrapper);
}
public async marketSellOrdersWithEthAsync(
orders: SignedOrder[],
diff --git a/packages/contracts/test/utils/log_decoder.ts b/packages/contracts/test/utils/log_decoder.ts
index 5a4801319..144a18dd1 100644
--- a/packages/contracts/test/utils/log_decoder.ts
+++ b/packages/contracts/test/utils/log_decoder.ts
@@ -1,8 +1,8 @@
-import { ContractArtifact } from '@0xproject/sol-compiler';
import { AbiDecoder, BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import {
AbiDefinition,
+ ContractArtifact,
DecodedLogArgs,
LogEntry,
LogWithDecodedArgs,
@@ -16,7 +16,6 @@ import { constants } from './constants';
export class LogDecoder {
private readonly _web3Wrapper: Web3Wrapper;
- private readonly _contractAddress: string;
private readonly _abiDecoder: AbiDecoder;
public static wrapLogBigNumbers(log: any): any {
const argNames = _.keys(log.args);
@@ -27,9 +26,8 @@ export class LogDecoder {
}
}
}
- constructor(web3Wrapper: Web3Wrapper, contractAddress: string) {
+ constructor(web3Wrapper: Web3Wrapper) {
this._web3Wrapper = web3Wrapper;
- this._contractAddress = contractAddress;
const abiArrays: AbiDefinition[][] = [];
_.forEach(artifacts, (artifact: ContractArtifact) => {
const compilerOutput = artifact.compilerOutput;
@@ -48,7 +46,6 @@ export class LogDecoder {
}
public async getTxWithDecodedLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
const tx = await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
- tx.logs = _.filter(tx.logs, log => log.address === this._contractAddress);
tx.logs = _.map(tx.logs, log => this.decodeLogOrThrow(log));
return tx;
}
diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts
index fa2eabc12..e0c55b834 100644
--- a/packages/contracts/test/utils/match_order_tester.ts
+++ b/packages/contracts/test/utils/match_order_tester.ts
@@ -4,11 +4,20 @@ import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
+import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types';
+
import { chaiSetup } from './chai_setup';
import { ERC20Wrapper } from './erc20_wrapper';
import { ERC721Wrapper } from './erc721_wrapper';
import { ExchangeWrapper } from './exchange_wrapper';
-import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts } from './types';
+import {
+ ERC20BalancesByOwner,
+ ERC721TokenIdsByOwner,
+ OrderInfo,
+ OrderStatus,
+ TransferAmountsByMatchOrders as TransferAmounts,
+ TransferAmountsLoggedByMatchOrders as LoggedTransferAmounts,
+} from './types';
chaiSetup.configure();
const expect = chai.expect;
@@ -18,43 +27,107 @@ export class MatchOrderTester {
private readonly _erc20Wrapper: ERC20Wrapper;
private readonly _erc721Wrapper: ERC721Wrapper;
private readonly _feeTokenAddress: string;
-
- /// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners.
- /// @param expectedNewERC20BalancesByOwner Expected ERC20 balances.
- /// @param realERC20BalancesByOwner Actual ERC20 balances.
- /// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners.
- /// @param realERC721TokenIdsByOwner Actual ERC20 token owners.
- /// @return True only if ERC20 balances match and ERC721 token owners match.
- private static _compareExpectedAndRealBalances(
- expectedNewERC20BalancesByOwner: ERC20BalancesByOwner,
+ /// @dev Checks values from the logs produced by Exchange.matchOrders against the expected transfer amounts.
+ /// Values include the amounts transferred from the left/right makers and taker, along with
+ /// the fees paid on each matched order. These are also the return values of MatchOrders.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param transactionReceipt Transaction receipt and logs produced by Exchange.matchOrders.
+ /// @param takerAddress Address of taker (account that called Exchange.matchOrders)
+ /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
+ private static async _assertLogsAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ transactionReceipt: TransactionReceiptWithDecodedLogs,
+ takerAddress: string,
+ expectedTransferAmounts: TransferAmounts,
+ ): Promise<void> {
+ // Should have two fill event logs -- one for each order.
+ const transactionFillLogs = _.filter(transactionReceipt.logs, ['event', 'Fill']);
+ expect(transactionFillLogs.length, 'Checking number of logs').to.be.equal(2);
+ // First log is for left fill
+ const leftLog = (transactionFillLogs[0] as any).args as LoggedTransferAmounts;
+ expect(leftLog.makerAddress, 'Checking logged maker address of left order').to.be.equal(
+ signedOrderLeft.makerAddress,
+ );
+ expect(leftLog.takerAddress, 'Checking logged taker address of right order').to.be.equal(takerAddress);
+ const amountBoughtByLeftMaker = new BigNumber(leftLog.takerAssetFilledAmount);
+ const amountSoldByLeftMaker = new BigNumber(leftLog.makerAssetFilledAmount);
+ const feePaidByLeftMaker = new BigNumber(leftLog.makerFeePaid);
+ const feePaidByTakerLeft = new BigNumber(leftLog.takerFeePaid);
+ // Second log is for right fill
+ const rightLog = (transactionFillLogs[1] as any).args as LoggedTransferAmounts;
+ expect(rightLog.makerAddress, 'Checking logged maker address of right order').to.be.equal(
+ signedOrderRight.makerAddress,
+ );
+ expect(rightLog.takerAddress, 'Checking loggerd taker address of right order').to.be.equal(takerAddress);
+ const amountBoughtByRightMaker = new BigNumber(rightLog.takerAssetFilledAmount);
+ const amountSoldByRightMaker = new BigNumber(rightLog.makerAssetFilledAmount);
+ const feePaidByRightMaker = new BigNumber(rightLog.makerFeePaid);
+ const feePaidByTakerRight = new BigNumber(rightLog.takerFeePaid);
+ // Derive amount received by taker
+ const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker);
+ // Assert log values - left order
+ expect(amountBoughtByLeftMaker, 'Checking logged amount bought by left maker').to.be.bignumber.equal(
+ expectedTransferAmounts.amountBoughtByLeftMaker,
+ );
+ expect(amountSoldByLeftMaker, 'Checking logged amount sold by left maker').to.be.bignumber.equal(
+ expectedTransferAmounts.amountSoldByLeftMaker,
+ );
+ expect(feePaidByLeftMaker, 'Checking logged fee paid by left maker').to.be.bignumber.equal(
+ expectedTransferAmounts.feePaidByLeftMaker,
+ );
+ expect(feePaidByTakerLeft, 'Checking logged fee paid on left order by taker').to.be.bignumber.equal(
+ expectedTransferAmounts.feePaidByTakerLeft,
+ );
+ // Assert log values - right order
+ expect(amountBoughtByRightMaker, 'Checking logged amount bought by right maker').to.be.bignumber.equal(
+ expectedTransferAmounts.amountBoughtByRightMaker,
+ );
+ expect(amountSoldByRightMaker, 'Checking logged amount sold by right maker').to.be.bignumber.equal(
+ expectedTransferAmounts.amountSoldByRightMaker,
+ );
+ expect(feePaidByRightMaker, 'Checking logged fee paid by right maker').to.be.bignumber.equal(
+ expectedTransferAmounts.feePaidByRightMaker,
+ );
+ expect(feePaidByTakerRight, 'Checking logged fee paid on right order by taker').to.be.bignumber.equal(
+ expectedTransferAmounts.feePaidByTakerRight,
+ );
+ // Assert derived amount received by taker
+ expect(amountReceivedByTaker, 'Checking logged amount received by taker').to.be.bignumber.equal(
+ expectedTransferAmounts.amountReceivedByTaker,
+ );
+ }
+ /// @dev Asserts all expected ERC20 and ERC721 account holdings match the real holdings.
+ /// @param expectedERC20BalancesByOwner Expected ERC20 balances.
+ /// @param realERC20BalancesByOwner Real ERC20 balances.
+ /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners.
+ /// @param realERC721TokenIdsByOwner Real ERC20 token owners.
+ private static async _assertAllKnownBalancesAsync(
+ expectedERC20BalancesByOwner: ERC20BalancesByOwner,
realERC20BalancesByOwner: ERC20BalancesByOwner,
- expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
- ): boolean {
+ ): Promise<void> {
// ERC20 Balances
- const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner);
- if (!doesErc20BalancesMatch) {
- return false;
- }
+ const areERC20BalancesEqual = _.isEqual(expectedERC20BalancesByOwner, realERC20BalancesByOwner);
+ expect(areERC20BalancesEqual, 'Checking all known ERC20 account balances').to.be.true();
// ERC721 Token Ids
- const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(
- expectedNewERC721TokenIdsByOwner,
- tokenIdsByOwner => {
- _.mapValues(tokenIdsByOwner, tokenIds => {
- _.sortBy(tokenIds);
- });
- },
- );
+ const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(expectedERC721TokenIdsByOwner, tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ });
const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => {
_.mapValues(tokenIdsByOwner, tokenIds => {
_.sortBy(tokenIds);
});
});
- const doesErc721TokenIdsMatch = _.isEqual(
+ const areERC721TokenIdsEqual = _.isEqual(
sortedExpectedNewERC721TokenIdsByOwner,
sortedNewERC721TokenIdsByOwner,
);
- return doesErc721TokenIdsMatch;
+ expect(areERC721TokenIdsEqual, 'Checking all known ERC721 account balances').to.be.true();
}
/// @dev Constructs new MatchOrderTester.
/// @param exchangeWrapper Used to call to the Exchange.
@@ -72,150 +145,199 @@ export class MatchOrderTester {
this._erc721Wrapper = erc721Wrapper;
this._feeTokenAddress = feeTokenAddress;
}
- /// @dev Matches two complementary orders and validates results.
- /// Validation either succeeds or throws.
+ /// @dev Matches two complementary orders and asserts results.
/// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order.
/// @param takerAddress Address of taker (the address who matched the two orders)
/// @param erc20BalancesByOwner Current ERC20 balances.
/// @param erc721TokenIdsByOwner Current ERC721 token owners.
- /// @param initialTakerAssetFilledAmountLeft Current amount the left order has been filled.
- /// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled.
+ /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
+ /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders.
+ /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders.
/// @return New ERC20 balances & ERC721 token owners.
- public async matchOrdersAndVerifyBalancesAsync(
+ public async matchOrdersAndAssertEffectsAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
takerAddress: string,
erc20BalancesByOwner: ERC20BalancesByOwner,
erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
- initialTakerAssetFilledAmountLeft?: BigNumber,
- initialTakerAssetFilledAmountRight?: BigNumber,
+ expectedTransferAmounts: TransferAmounts,
+ initialLeftOrderFilledAmount: BigNumber = new BigNumber(0),
+ initialRightOrderFilledAmount: BigNumber = new BigNumber(0),
): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
- // Verify Left order preconditions
- const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
- orderHashUtils.getOrderHashHex(signedOrderLeft),
- );
- const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft
- ? initialTakerAssetFilledAmountLeft
- : new BigNumber(0);
- expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft);
- // Verify Right order preconditions
- const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
- orderHashUtils.getOrderHashHex(signedOrderRight),
+ // Assert initial order states
+ await this._assertInitialOrderStatesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ initialLeftOrderFilledAmount,
+ initialRightOrderFilledAmount,
);
- const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight
- ? initialTakerAssetFilledAmountRight
- : new BigNumber(0);
- expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight);
// Match left & right orders
- await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
+ const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ );
const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
- // Calculate expected balance changes
- const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync(
+ // Assert logs
+ await MatchOrderTester._assertLogsAsync(
signedOrderLeft,
signedOrderRight,
- orderTakerAssetFilledAmountLeft,
- orderTakerAssetFilledAmountRight,
+ transactionReceipt,
+ takerAddress,
+ expectedTransferAmounts,
);
- let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
- let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
- [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
+ // Assert exchange state
+ await this._assertExchangeStateAsync(
signedOrderLeft,
signedOrderRight,
- takerAddress,
- erc20BalancesByOwner,
- erc721TokenIdsByOwner,
+ initialLeftOrderFilledAmount,
+ initialRightOrderFilledAmount,
expectedTransferAmounts,
);
- // Assert our expected balances are equal to the actual balances
- const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances(
- expectedERC20BalancesByOwner,
+ // Assert balances of makers, taker, and fee recipients
+ await this._assertBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
newERC20BalancesByOwner,
- expectedERC721TokenIdsByOwner,
newERC721TokenIdsByOwner,
+ expectedTransferAmounts,
+ takerAddress,
);
- expect(didExpectedBalancesMatchRealBalances).to.be.true();
return [newERC20BalancesByOwner, newERC721TokenIdsByOwner];
}
- /// @dev Calculates expected transfer amounts between order makers, fee recipients, and
- /// the taker when two orders are matched.
+ /// @dev Asserts initial exchange state for the left and right orders.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param expectedOrderFilledAmountLeft How much left order has been filled, prior to matching orders.
+ /// @param expectedOrderFilledAmountRight How much the right order has been filled, prior to matching orders.
+ private async _assertInitialOrderStatesAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ expectedOrderFilledAmountLeft: BigNumber,
+ expectedOrderFilledAmountRight: BigNumber,
+ ): Promise<void> {
+ // Assert left order initial state
+ const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderHashUtils.getOrderHashHex(signedOrderLeft),
+ );
+ expect(orderTakerAssetFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal(
+ expectedOrderFilledAmountLeft,
+ );
+ // Assert right order initial state
+ const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderHashUtils.getOrderHashHex(signedOrderRight),
+ );
+ expect(orderTakerAssetFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal(
+ expectedOrderFilledAmountRight,
+ );
+ }
+ /// @dev Asserts the exchange state against the expected amounts transferred by from matching orders.
/// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order.
- /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders.
- /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders.
+ /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders.
+ /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders.
/// @return TransferAmounts A struct containing the expected transfer amounts.
- private async _calculateExpectedTransferAmountsAsync(
+ private async _assertExchangeStateAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
- orderTakerAssetFilledAmountLeft: BigNumber,
- orderTakerAssetFilledAmountRight: BigNumber,
- ): Promise<TransferAmounts> {
+ initialLeftOrderFilledAmount: BigNumber,
+ initialRightOrderFilledAmount: BigNumber,
+ expectedTransferAmounts: TransferAmounts,
+ ): Promise<void> {
+ // Assert state for left order: amount bought by left maker
let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderLeft),
);
- amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft);
- const amountSoldByLeftMaker = amountBoughtByLeftMaker
- .times(signedOrderLeft.makerAssetAmount)
- .dividedToIntegerBy(signedOrderLeft.takerAssetAmount);
- const amountReceivedByRightMaker = amountBoughtByLeftMaker
- .times(signedOrderRight.takerAssetAmount)
- .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
- const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker);
+ amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(initialLeftOrderFilledAmount);
+ expect(amountBoughtByLeftMaker, 'Checking exchange state for left order').to.be.bignumber.equal(
+ expectedTransferAmounts.amountBoughtByLeftMaker,
+ );
+ // Assert state for right order: amount bought by right maker
let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderRight),
);
- amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight);
- const amountSoldByRightMaker = amountBoughtByRightMaker
- .times(signedOrderRight.makerAssetAmount)
- .dividedToIntegerBy(signedOrderRight.takerAssetAmount);
- const amountReceivedByLeftMaker = amountSoldByRightMaker;
- const feePaidByLeftMaker = signedOrderLeft.makerFee
- .times(amountSoldByLeftMaker)
- .dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
- const feePaidByRightMaker = signedOrderRight.makerFee
- .times(amountSoldByRightMaker)
- .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
- const feePaidByTakerLeft = signedOrderLeft.takerFee
- .times(amountSoldByLeftMaker)
- .dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
- const feePaidByTakerRight = signedOrderRight.takerFee
- .times(amountSoldByRightMaker)
- .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
- const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight);
- const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft);
- const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight);
- // Return values
- const expectedTransferAmounts = {
- // Left Maker
- amountBoughtByLeftMaker,
- amountSoldByLeftMaker,
- amountReceivedByLeftMaker,
- feePaidByLeftMaker,
- // Right Maker
- amountBoughtByRightMaker,
- amountSoldByRightMaker,
- amountReceivedByRightMaker,
- feePaidByRightMaker,
- // Taker
- amountReceivedByTaker,
- feePaidByTakerLeft,
- feePaidByTakerRight,
- totalFeePaidByTaker,
- // Fee Recipients
- feeReceivedLeft,
- feeReceivedRight,
- };
- return expectedTransferAmounts;
+ amountBoughtByRightMaker = amountBoughtByRightMaker.minus(initialRightOrderFilledAmount);
+ expect(amountBoughtByRightMaker, 'Checking exchange state for right order').to.be.bignumber.equal(
+ expectedTransferAmounts.amountBoughtByRightMaker,
+ );
+ // Assert left order status
+ const maxAmountBoughtByLeftMaker = signedOrderLeft.takerAssetAmount.minus(initialLeftOrderFilledAmount);
+ const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker)
+ ? OrderStatus.FULLY_FILLED
+ : OrderStatus.FILLABLE;
+ expect(leftOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for left order').to.be.equal(
+ leftExpectedStatus,
+ );
+ // Assert right order status
+ const maxAmountBoughtByRightMaker = signedOrderRight.takerAssetAmount.minus(initialRightOrderFilledAmount);
+ const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker)
+ ? OrderStatus.FULLY_FILLED
+ : OrderStatus.FILLABLE;
+ expect(rightOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for right order').to.be.equal(
+ rightExpectedStatus,
+ );
+ }
+ /// @dev Asserts account balances after matching orders.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param initialERC20BalancesByOwner ERC20 balances prior to order matching.
+ /// @param initialERC721TokenIdsByOwner ERC721 token owners prior to order matching.
+ /// @param finalERC20BalancesByOwner ERC20 balances after order matching.
+ /// @param finalERC721TokenIdsByOwner ERC721 token owners after order matching.
+ /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
+ /// @param takerAddress Address of taker (account that called Exchange.matchOrders).
+ private async _assertBalancesAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ initialERC20BalancesByOwner: ERC20BalancesByOwner,
+ initialERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ finalERC20BalancesByOwner: ERC20BalancesByOwner,
+ finalERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ expectedTransferAmounts: TransferAmounts,
+ takerAddress: string,
+ ): Promise<void> {
+ let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
+ let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ initialERC20BalancesByOwner,
+ initialERC721TokenIdsByOwner,
+ expectedTransferAmounts,
+ );
+ // Assert balances of makers, taker, and fee recipients
+ await this._assertMakerTakerAndFeeRecipientBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ expectedERC20BalancesByOwner,
+ finalERC20BalancesByOwner,
+ expectedERC721TokenIdsByOwner,
+ finalERC721TokenIdsByOwner,
+ takerAddress,
+ );
+ // Assert balances for all known accounts
+ await MatchOrderTester._assertAllKnownBalancesAsync(
+ expectedERC20BalancesByOwner,
+ finalERC20BalancesByOwner,
+ expectedERC721TokenIdsByOwner,
+ finalERC721TokenIdsByOwner,
+ );
}
/// @dev Calculates the expected balances of order makers, fee recipients, and the taker,
/// as a result of matching two orders.
- /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight First matched order.
/// @param signedOrderRight Second matched order.
/// @param takerAddress Address of taker (the address who matched the two orders)
/// @param erc20BalancesByOwner Current ERC20 balances.
/// @param erc721TokenIdsByOwner Current ERC721 token owners.
- /// @param expectedTransferAmounts A struct containing the expected transfer amounts.
+ /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
/// @return Expected ERC20 balances & ERC721 token owners after orders have been matched.
private _calculateExpectedBalances(
signedOrderLeft: SignedOrder,
@@ -247,7 +369,7 @@ export class MatchOrderTester {
expectedNewERC20BalancesByOwner[makerAddressRight][
takerAssetAddressRight
] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add(
- expectedTransferAmounts.amountReceivedByRightMaker,
+ expectedTransferAmounts.amountBoughtByRightMaker,
);
// Taker
expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
@@ -277,7 +399,7 @@ export class MatchOrderTester {
// Left Maker
expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
makerAddressLeft
- ][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker);
+ ][takerAssetAddressLeft].add(expectedTransferAmounts.amountBoughtByLeftMaker);
// Right Maker
expectedNewERC20BalancesByOwner[makerAddressRight][
makerAssetAddressRight
@@ -307,20 +429,138 @@ export class MatchOrderTester {
// Taker Fees
expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
takerAddress
- ][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker);
+ ][this._feeTokenAddress].minus(
+ expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight),
+ );
// Left Fee Recipient Fees
expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
this._feeTokenAddress
] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
- expectedTransferAmounts.feeReceivedLeft,
+ expectedTransferAmounts.feePaidByLeftMaker.add(expectedTransferAmounts.feePaidByTakerLeft),
);
// Right Fee Recipient Fees
expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
this._feeTokenAddress
] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
- expectedTransferAmounts.feeReceivedRight,
+ expectedTransferAmounts.feePaidByRightMaker.add(expectedTransferAmounts.feePaidByTakerRight),
);
return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner];
}
-}
+ /// @dev Asserts ERC20 account balances and ERC721 token holdings that result from order matching.
+ /// Specifically checks balances of makers, taker and fee recipients.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param expectedERC20BalancesByOwner Expected ERC20 balances.
+ /// @param realERC20BalancesByOwner Real ERC20 balances.
+ /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners.
+ /// @param realERC721TokenIdsByOwner Real ERC20 token owners.
+ /// @param takerAddress Address of taker (account that called Exchange.matchOrders).
+ private async _assertMakerTakerAndFeeRecipientBalancesAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ expectedERC20BalancesByOwner: ERC20BalancesByOwner,
+ realERC20BalancesByOwner: ERC20BalancesByOwner,
+ expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ takerAddress: string,
+ ): Promise<void> {
+ // Individual balance comparisons
+ const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData);
+ const makerERC20AssetDataLeft =
+ makerAssetProxyIdLeft === AssetProxyId.ERC20
+ ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData)
+ : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress;
+ const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData);
+ const makerERC20AssetDataRight =
+ makerAssetProxyIdRight === AssetProxyId.ERC20
+ ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData)
+ : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData);
+ const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress;
+ if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ expect(
+ realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft],
+ 'Checking left maker egress ERC20 account balance',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]);
+ expect(
+ realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft],
+ 'Checking right maker ingress ERC20 account balance',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]);
+ expect(
+ realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft],
+ 'Checking taker ingress ERC20 account balance',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]);
+ } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ expect(
+ realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(),
+ 'Checking left maker egress ERC721 account holdings',
+ ).to.be.deep.equal(
+ expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(),
+ );
+ expect(
+ realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(),
+ 'Checking right maker ERC721 account holdings',
+ ).to.be.deep.equal(
+ expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(),
+ );
+ expect(
+ realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(),
+ 'Checking taker ingress ERC721 account holdings',
+ ).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort());
+ } else {
+ throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`);
+ }
+ if (makerAssetProxyIdRight === AssetProxyId.ERC20) {
+ expect(
+ realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight],
+ 'Checking left maker ingress ERC20 account balance',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]);
+ expect(
+ realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight],
+ 'Checking right maker egress ERC20 account balance',
+ ).to.be.bignumber.equal(
+ expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight],
+ );
+ } else if (makerAssetProxyIdRight === AssetProxyId.ERC721) {
+ expect(
+ realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(),
+ 'Checking left maker ingress ERC721 account holdings',
+ ).to.be.deep.equal(
+ expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(),
+ );
+ expect(
+ realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight],
+ 'Checking right maker agress ERC721 account holdings',
+ ).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]);
+ } else {
+ throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`);
+ }
+ // Paid fees
+ expect(
+ realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress],
+ 'Checking left maker egress ERC20 account fees',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]);
+ expect(
+ realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress],
+ 'Checking right maker egress ERC20 account fees',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]);
+ expect(
+ realERC20BalancesByOwner[takerAddress][this._feeTokenAddress],
+ 'Checking taker egress ERC20 account fees',
+ ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]);
+ // Received fees
+ expect(
+ realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress],
+ 'Checking left fee recipient ingress ERC20 account fees',
+ ).to.be.bignumber.equal(
+ expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress],
+ );
+ expect(
+ realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress],
+ 'Checking right fee receipient ingress ERC20 account fees',
+ ).to.be.bignumber.equal(
+ expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress],
+ );
+ }
+} // tslint:disable-line:max-file-line-count
diff --git a/packages/contracts/test/utils/multi_sig_wrapper.ts b/packages/contracts/test/utils/multi_sig_wrapper.ts
index 8c8055d4a..e12a58695 100644
--- a/packages/contracts/test/utils/multi_sig_wrapper.ts
+++ b/packages/contracts/test/utils/multi_sig_wrapper.ts
@@ -6,7 +6,6 @@ import * as _ from 'lodash';
import { AssetProxyOwnerContract } from '../../generated_contract_wrappers/asset_proxy_owner';
import { MultiSigWalletContract } from '../../generated_contract_wrappers/multi_sig_wallet';
-import { constants } from './constants';
import { LogDecoder } from './log_decoder';
export class MultiSigWrapper {
@@ -16,7 +15,7 @@ export class MultiSigWrapper {
constructor(multiSigContract: MultiSigWalletContract, provider: Provider) {
this._multiSig = multiSigContract;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, this._multiSig.address);
+ this._logDecoder = new LogDecoder(this._web3Wrapper);
}
public async submitTransactionAsync(
destination: string,
@@ -36,10 +35,19 @@ export class MultiSigWrapper {
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
- public async executeTransactionAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
+ public async revokeConfirmationAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
+ const txHash = await this._multiSig.revokeConfirmation.sendTransactionAsync(txId, { from });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async executeTransactionAsync(
+ txId: BigNumber,
+ from: string,
+ opts: { gas?: number } = {},
+ ): Promise<TransactionReceiptWithDecodedLogs> {
const txHash = await this._multiSig.executeTransaction.sendTransactionAsync(txId, {
from,
- gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
+ gas: opts.gas,
});
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -52,7 +60,6 @@ export class MultiSigWrapper {
const txHash = await (this
._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
from,
- gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
});
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
diff --git a/packages/contracts/test/utils/order_factory_from_scenario.ts b/packages/contracts/test/utils/order_factory_from_scenario.ts
index a908140b9..8e04db588 100644
--- a/packages/contracts/test/utils/order_factory_from_scenario.ts
+++ b/packages/contracts/test/utils/order_factory_from_scenario.ts
@@ -21,6 +21,8 @@ const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100_000_000_000_000_000)
const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50_000_000_000_000_000);
const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1_000_000);
const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500_000);
+const TEN_UNITS_ZERO_DECIMALS = new BigNumber(10);
+const ONE_THOUSAND_UNITS_ZERO_DECIMALS = new BigNumber(1000);
const ONE_NFT_UNIT = new BigNumber(1);
export class OrderFactoryFromScenario {
@@ -28,6 +30,7 @@ export class OrderFactoryFromScenario {
private readonly _zrxAddress: string;
private readonly _nonZrxERC20EighteenDecimalTokenAddresses: string[];
private readonly _erc20FiveDecimalTokenAddresses: string[];
+ private readonly _erc20ZeroDecimalTokenAddresses: string[];
private readonly _erc721Token: DummyERC721TokenContract;
private readonly _erc721Balances: ERC721TokenIdsByOwner;
private readonly _exchangeAddress: string;
@@ -36,6 +39,7 @@ export class OrderFactoryFromScenario {
zrxAddress: string,
nonZrxERC20EighteenDecimalTokenAddresses: string[],
erc20FiveDecimalTokenAddresses: string[],
+ erc20ZeroDecimalTokenAddresses: string[],
erc721Token: DummyERC721TokenContract,
erc721Balances: ERC721TokenIdsByOwner,
exchangeAddress: string,
@@ -44,6 +48,7 @@ export class OrderFactoryFromScenario {
this._zrxAddress = zrxAddress;
this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses;
this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses;
+ this._erc20ZeroDecimalTokenAddresses = erc20ZeroDecimalTokenAddresses;
this._erc721Token = erc721Token;
this._erc721Balances = erc721Balances;
this._exchangeAddress = exchangeAddress;
@@ -89,6 +94,9 @@ export class OrderFactoryFromScenario {
erc721MakerAssetIds[0],
);
break;
+ case AssetDataScenario.ERC20ZeroDecimals:
+ makerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20ZeroDecimalTokenAddresses[0]);
+ break;
default:
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
}
@@ -109,6 +117,9 @@ export class OrderFactoryFromScenario {
erc721TakerAssetIds[0],
);
break;
+ case AssetDataScenario.ERC20ZeroDecimals:
+ takerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20ZeroDecimalTokenAddresses[1]);
+ break;
default:
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
}
@@ -126,6 +137,9 @@ export class OrderFactoryFromScenario {
case AssetDataScenario.ERC721:
makerAssetAmount = ONE_NFT_UNIT;
break;
+ case AssetDataScenario.ERC20ZeroDecimals:
+ makerAssetAmount = ONE_THOUSAND_UNITS_ZERO_DECIMALS;
+ break;
default:
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
}
@@ -142,6 +156,9 @@ export class OrderFactoryFromScenario {
case AssetDataScenario.ERC721:
makerAssetAmount = ONE_NFT_UNIT;
break;
+ case AssetDataScenario.ERC20ZeroDecimals:
+ makerAssetAmount = TEN_UNITS_ZERO_DECIMALS;
+ break;
default:
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
}
@@ -166,6 +183,9 @@ export class OrderFactoryFromScenario {
case AssetDataScenario.ERC721:
takerAssetAmount = ONE_NFT_UNIT;
break;
+ case AssetDataScenario.ERC20ZeroDecimals:
+ takerAssetAmount = ONE_THOUSAND_UNITS_ZERO_DECIMALS;
+ break;
default:
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
}
@@ -182,6 +202,9 @@ export class OrderFactoryFromScenario {
case AssetDataScenario.ERC721:
takerAssetAmount = ONE_NFT_UNIT;
break;
+ case AssetDataScenario.ERC20ZeroDecimals:
+ takerAssetAmount = TEN_UNITS_ZERO_DECIMALS;
+ break;
default:
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
}
diff --git a/packages/contracts/test/utils/order_utils.ts b/packages/contracts/test/utils/order_utils.ts
index 019f6e74b..444e27c44 100644
--- a/packages/contracts/test/utils/order_utils.ts
+++ b/packages/contracts/test/utils/order_utils.ts
@@ -5,7 +5,7 @@ import { constants } from './constants';
import { CancelOrder, MatchOrder } from './types';
export const orderUtils = {
- getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
+ getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
const partialAmount = numerator
.mul(target)
.div(denominator)
diff --git a/packages/contracts/test/utils/test_with_reference.ts b/packages/contracts/test/utils/test_with_reference.ts
index 599b1eed4..b80be4a6c 100644
--- a/packages/contracts/test/utils/test_with_reference.ts
+++ b/packages/contracts/test/utils/test_with_reference.ts
@@ -6,6 +6,34 @@ import { chaiSetup } from './chai_setup';
chaiSetup.configure();
const expect = chai.expect;
+class Value<T> {
+ public value: T;
+ constructor(value: T) {
+ this.value = value;
+ }
+}
+
+// tslint:disable-next-line: max-classes-per-file
+class ErrorMessage {
+ public error: string;
+ constructor(message: string) {
+ this.error = message;
+ }
+}
+
+type PromiseResult<T> = Value<T> | ErrorMessage;
+
+// TODO(albrow): This seems like a generic utility function that could exist in
+// lodash. We should replace it by a library implementation, or move it to our
+// own.
+async function evaluatePromise<T>(promise: Promise<T>): Promise<PromiseResult<T>> {
+ try {
+ return new Value<T>(await promise);
+ } catch (e) {
+ return new ErrorMessage(e.message);
+ }
+}
+
export async function testWithReferenceFuncAsync<P0, R>(
referenceFunc: (p0: P0) => Promise<R>,
testFunc: (p0: P0) => Promise<R>,
@@ -64,39 +92,31 @@ export async function testWithReferenceFuncAsync(
testFuncAsync: (...args: any[]) => Promise<any>,
values: any[],
): Promise<void> {
- let expectedResult: any;
- let expectedErr: string | undefined;
- try {
- expectedResult = await referenceFuncAsync(...values);
- } catch (e) {
- expectedErr = e.message;
- }
- let actualResult: any | undefined;
- try {
- actualResult = await testFuncAsync(...values);
- if (!_.isUndefined(expectedErr)) {
+ // Measure correct behaviour
+ const expected = await evaluatePromise(referenceFuncAsync(...values));
+
+ // Measure actual behaviour
+ const actual = await evaluatePromise(testFuncAsync(...values));
+
+ // Compare behaviour
+ if (expected instanceof ErrorMessage) {
+ // If we expected an error, check if the actual error message contains the
+ // expected error message.
+ if (!(actual instanceof ErrorMessage)) {
throw new Error(
- `Expected error containing ${expectedErr} but got no error\n\tTest case: ${_getTestCaseString(
+ `Expected error containing ${expected.error} but got no error\n\tTest case: ${_getTestCaseString(
referenceFuncAsync,
values,
)}`,
);
}
- } catch (e) {
- if (_.isUndefined(expectedErr)) {
- throw new Error(`${e.message}\n\tTest case: ${_getTestCaseString(referenceFuncAsync, values)}`);
- } else {
- expect(e.message).to.contain(
- expectedErr,
- `${e.message}\n\tTest case: ${_getTestCaseString(referenceFuncAsync, values)}`,
- );
- }
- }
- if (!_.isUndefined(actualResult) && !_.isUndefined(expectedResult)) {
- expect(actualResult).to.deep.equal(
- expectedResult,
- `Test case: ${_getTestCaseString(referenceFuncAsync, values)}`,
+ expect(actual.error).to.contain(
+ expected.error,
+ `${actual.error}\n\tTest case: ${_getTestCaseString(referenceFuncAsync, values)}`,
);
+ } else {
+ // If we do not expect an error, compare actual and expected directly.
+ expect(actual).to.deep.equal(expected, `Test case ${_getTestCaseString(referenceFuncAsync, values)}`);
}
}
diff --git a/packages/contracts/test/utils/transaction_factory.ts b/packages/contracts/test/utils/transaction_factory.ts
index 281c1e30d..8465a6a30 100644
--- a/packages/contracts/test/utils/transaction_factory.ts
+++ b/packages/contracts/test/utils/transaction_factory.ts
@@ -1,4 +1,4 @@
-import { EIP712Schema, EIP712Types, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils';
+import { EIP712Schema, EIP712Types, eip712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils';
import { SignatureType } from '@0xproject/types';
import * as ethUtil from 'ethereumjs-util';
@@ -31,11 +31,11 @@ export class TransactionFactory {
signerAddress,
data,
};
- const executeTransactionHashBuff = EIP712Utils.structHash(
+ const executeTransactionHashBuff = eip712Utils.structHash(
EIP712_ZEROEX_TRANSACTION_SCHEMA,
executeTransactionData,
);
- const txHash = EIP712Utils.createEIP712Message(executeTransactionHashBuff, this._exchangeAddress);
+ const txHash = eip712Utils.createEIP712Message(executeTransactionHashBuff, this._exchangeAddress);
const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType);
const signedTx = {
exchangeAddress: this._exchangeAddress,
diff --git a/packages/contracts/test/utils/types.ts b/packages/contracts/test/utils/types.ts
index 67313b647..598fb5393 100644
--- a/packages/contracts/test/utils/types.ts
+++ b/packages/contracts/test/utils/types.ts
@@ -117,21 +117,24 @@ export interface TransferAmountsByMatchOrders {
// Left Maker
amountBoughtByLeftMaker: BigNumber;
amountSoldByLeftMaker: BigNumber;
- amountReceivedByLeftMaker: BigNumber;
feePaidByLeftMaker: BigNumber;
// Right Maker
amountBoughtByRightMaker: BigNumber;
amountSoldByRightMaker: BigNumber;
- amountReceivedByRightMaker: BigNumber;
feePaidByRightMaker: BigNumber;
// Taker
amountReceivedByTaker: BigNumber;
feePaidByTakerLeft: BigNumber;
feePaidByTakerRight: BigNumber;
- totalFeePaidByTaker: BigNumber;
- // Fee Recipients
- feeReceivedLeft: BigNumber;
- feeReceivedRight: BigNumber;
+}
+
+export interface TransferAmountsLoggedByMatchOrders {
+ makerAddress: string;
+ takerAddress: string;
+ makerAssetFilledAmount: string;
+ takerAssetFilledAmount: string;
+ makerFeePaid: string;
+ takerFeePaid: string;
}
export interface OrderInfo {
@@ -177,10 +180,11 @@ export enum ExpirationTimeSecondsScenario {
}
export enum AssetDataScenario {
- ERC721 = 'ERC721',
+ ERC20ZeroDecimals = 'ERC20_ZERO_DECIMALS',
ZRXFeeToken = 'ZRX_FEE_TOKEN',
ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS',
ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS',
+ ERC721 = 'ERC721',
}
export enum TakerAssetFillAmountScenario {
diff --git a/packages/contracts/test/utils/web3_wrapper.ts b/packages/contracts/test/utils/web3_wrapper.ts
index acb3103b7..d1cd3d387 100644
--- a/packages/contracts/test/utils/web3_wrapper.ts
+++ b/packages/contracts/test/utils/web3_wrapper.ts
@@ -1,5 +1,5 @@
import { devConstants, env, EnvVars, web3Factory } from '@0xproject/dev-utils';
-import { prependSubprovider } from '@0xproject/subproviders';
+import { prependSubprovider, Web3ProviderEngine } from '@0xproject/subproviders';
import { logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
@@ -47,7 +47,7 @@ const ganacheConfigs = {
};
const providerConfigs = testProvider === ProviderType.Ganache ? ganacheConfigs : gethConfigs;
-export const provider = web3Factory.getRpcProvider(providerConfigs);
+export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
const isProfilerEnabled = env.parseBoolean(EnvVars.SolidityProfiler);
const isRevertTraceEnabled = env.parseBoolean(EnvVars.SolidityRevertTrace);