aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src/utils')
-rw-r--r--packages/contracts/src/utils/address_utils.ts4
-rw-r--r--packages/contracts/src/utils/constants.ts16
-rw-r--r--packages/contracts/src/utils/coverage.ts21
-rw-r--r--packages/contracts/src/utils/erc20_wrapper.ts8
-rw-r--r--packages/contracts/src/utils/erc721_wrapper.ts14
-rw-r--r--packages/contracts/src/utils/exchange_wrapper.ts15
-rw-r--r--packages/contracts/src/utils/formatters.ts8
-rw-r--r--packages/contracts/src/utils/match_order_tester.ts356
-rw-r--r--packages/contracts/src/utils/order_factory.ts9
-rw-r--r--packages/contracts/src/utils/order_utils.ts6
-rw-r--r--packages/contracts/src/utils/transaction_factory.ts4
-rw-r--r--packages/contracts/src/utils/types.ts12
-rw-r--r--packages/contracts/src/utils/web3_wrapper.ts13
13 files changed, 443 insertions, 43 deletions
diff --git a/packages/contracts/src/utils/address_utils.ts b/packages/contracts/src/utils/address_utils.ts
index 01a7a6fd4..dc63459f9 100644
--- a/packages/contracts/src/utils/address_utils.ts
+++ b/packages/contracts/src/utils/address_utils.ts
@@ -1,10 +1,10 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { crypto } from './crypto';
export const addressUtils = {
generatePseudoRandomAddress(): string {
- const randomBigNum = ZeroEx.generatePseudoRandomSalt();
+ const randomBigNum = generatePseudoRandomSalt();
const randomBuff = crypto.solSHA3([randomBigNum]);
const randomAddress = `0x${randomBuff.slice(0, 20).toString('hex')}`;
return randomAddress;
diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts
index 3fd2a0a3b..483340186 100644
--- a/packages/contracts/src/utils/constants.ts
+++ b/packages/contracts/src/utils/constants.ts
@@ -1,5 +1,5 @@
-import { ZeroEx } from '0x.js';
import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -20,6 +20,7 @@ export const constants = {
INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
TESTRPC_NETWORK_ID: 50,
+ AWAIT_TRANSACTION_MINED_MS: 100,
MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
MAX_TOKEN_APPROVE_GAS: 60000,
@@ -30,13 +31,14 @@ export const constants = {
NUM_DUMMY_ERC20_TO_DEPLOY: 3,
NUM_DUMMY_ERC721_TO_DEPLOY: 1,
NUM_ERC721_TOKENS_TO_MINT: 2,
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
- INITIAL_ERC20_BALANCE: ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18),
- INITIAL_ERC20_ALLOWANCE: ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18),
+ INITIAL_ERC20_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
+ INITIAL_ERC20_ALLOWANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
STATIC_ORDER_PARAMS: {
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
+ makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
},
};
diff --git a/packages/contracts/src/utils/coverage.ts b/packages/contracts/src/utils/coverage.ts
new file mode 100644
index 000000000..a37939464
--- /dev/null
+++ b/packages/contracts/src/utils/coverage.ts
@@ -0,0 +1,21 @@
+import { devConstants } from '@0xproject/dev-utils';
+import { CoverageSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
+import * as fs from 'fs';
+import * as _ from 'lodash';
+
+let coverageSubprovider: CoverageSubprovider;
+
+export const coverage = {
+ getCoverageSubproviderSingleton(): CoverageSubprovider {
+ if (_.isUndefined(coverageSubprovider)) {
+ coverageSubprovider = coverage._getCoverageSubprovider();
+ }
+ return coverageSubprovider;
+ },
+ _getCoverageSubprovider(): CoverageSubprovider {
+ const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
+ const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
+ const subprovider = new CoverageSubprovider(solCompilerArtifactAdapter, defaultFromAddress);
+ return subprovider;
+ },
+};
diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts
index f12571b48..0303649a5 100644
--- a/packages/contracts/src/utils/erc20_wrapper.ts
+++ b/packages/contracts/src/utils/erc20_wrapper.ts
@@ -23,7 +23,7 @@ export class ERC20Wrapper {
}
public async deployDummyTokensAsync(): Promise<DummyERC20TokenContract[]> {
this._dummyTokenContracts = await Promise.all(
- _.times(constants.NUM_DUMMY_ERC20_TO_DEPLOY, () =>
+ _.times(constants.NUM_DUMMY_ERC20_TO_DEPLOY, async () =>
DummyERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC20Token,
this._provider,
@@ -45,7 +45,7 @@ export class ERC20Wrapper {
);
return this._proxyContract;
}
- public async setBalancesAndAllowancesAsync() {
+ public async setBalancesAndAllowancesAsync(): Promise<void> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
const setBalancePromises: Array<Promise<string>> = [];
@@ -103,12 +103,12 @@ export class ERC20Wrapper {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
- private _validateDummyTokenContractsExistOrThrow() {
+ private _validateDummyTokenContractsExistOrThrow(): void {
if (_.isUndefined(this._dummyTokenContracts)) {
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
- private _validateProxyContractExistsOrThrow() {
+ private _validateProxyContractExistsOrThrow(): void {
if (_.isUndefined(this._proxyContract)) {
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
}
diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts
index 5b5de262f..aee796e4b 100644
--- a/packages/contracts/src/utils/erc721_wrapper.ts
+++ b/packages/contracts/src/utils/erc721_wrapper.ts
@@ -1,4 +1,4 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { Provider } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
@@ -25,7 +25,7 @@ export class ERC721Wrapper {
}
public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> {
this._dummyTokenContracts = await Promise.all(
- _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY, () =>
+ _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY, async () =>
DummyERC721TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC721Token,
this._provider,
@@ -45,7 +45,7 @@ export class ERC721Wrapper {
);
return this._proxyContract;
}
- public async setBalancesAndAllowancesAsync() {
+ public async setBalancesAndAllowancesAsync(): Promise<void> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
const setBalancePromises: Array<Promise<string>> = [];
@@ -54,7 +54,7 @@ export class ERC721Wrapper {
_.forEach(this._dummyTokenContracts, dummyTokenContract => {
_.forEach(this._tokenOwnerAddresses, tokenOwnerAddress => {
_.forEach(_.range(constants.NUM_ERC721_TOKENS_TO_MINT), () => {
- const tokenId = ZeroEx.generatePseudoRandomSalt();
+ const tokenId = generatePseudoRandomSalt();
setBalancePromises.push(
dummyTokenContract.mint.sendTransactionAsync(tokenOwnerAddress, tokenId, {
from: this._contractOwnerAddress,
@@ -125,17 +125,17 @@ export class ERC721Wrapper {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
- private _validateDummyTokenContractsExistOrThrow() {
+ private _validateDummyTokenContractsExistOrThrow(): void {
if (_.isUndefined(this._dummyTokenContracts)) {
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
- private _validateProxyContractExistsOrThrow() {
+ private _validateProxyContractExistsOrThrow(): void {
if (_.isUndefined(this._proxyContract)) {
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
- private _validateBalancesAndAllowancesSetOrThrow() {
+ private _validateBalancesAndAllowancesSetOrThrow(): void {
if (_.keys(this._initialTokenIdsByOwner).length === 0) {
throw new Error(
'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts
index 46531fa3f..0d0977ab8 100644
--- a/packages/contracts/src/utils/exchange_wrapper.ts
+++ b/packages/contracts/src/utils/exchange_wrapper.ts
@@ -1,5 +1,6 @@
-import { TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
+import { Provider, TransactionReceiptWithDecodedLogs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import * as Web3 from 'web3';
@@ -13,11 +14,11 @@ import { AssetProxyId, OrderInfo, SignedOrder, SignedTransaction } from './types
export class ExchangeWrapper {
private _exchange: ExchangeContract;
+ private _web3Wrapper: Web3Wrapper;
private _logDecoder: LogDecoder = new LogDecoder(constants.TESTRPC_NETWORK_ID);
- private _zeroEx: ZeroEx;
- constructor(exchangeContract: ExchangeContract, zeroEx: ZeroEx) {
+ constructor(exchangeContract: ExchangeContract, provider: Provider) {
this._exchange = exchangeContract;
- this._zeroEx = zeroEx;
+ this._web3Wrapper = new Web3Wrapper(provider);
}
public async fillOrderAsync(
signedOrder: SignedOrder,
@@ -196,7 +197,7 @@ export class ExchangeWrapper {
opts: { oldAssetProxyAddressIfExists?: string } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const oldAssetProxyAddress = _.isUndefined(opts.oldAssetProxyAddressIfExists)
- ? ZeroEx.NULL_ADDRESS
+ ? constants.NULL_ADDRESS
: opts.oldAssetProxyAddressIfExists;
const txHash = await this._exchange.registerAssetProxy.sendTransactionAsync(
assetProxyId,
@@ -245,8 +246,8 @@ export class ExchangeWrapper {
const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash);
return tx;
}
- private async _getTxWithDecodedExchangeLogsAsync(txHash: string) {
- const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash);
+ private async _getTxWithDecodedExchangeLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
+ const tx = await this._web3Wrapper.awaitTransactionMinedAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
tx.logs = _.filter(tx.logs, log => log.address === this._exchange.address);
tx.logs = _.map(tx.logs, log => this._logDecoder.decodeLogOrThrow(log));
return tx;
diff --git a/packages/contracts/src/utils/formatters.ts b/packages/contracts/src/utils/formatters.ts
index e706c15b5..bfa48d6f1 100644
--- a/packages/contracts/src/utils/formatters.ts
+++ b/packages/contracts/src/utils/formatters.ts
@@ -5,7 +5,7 @@ import { orderUtils } from './order_utils';
import { BatchCancelOrders, BatchFillOrders, MarketBuyOrders, MarketSellOrders, SignedOrder } from './types';
export const formatters = {
- createBatchFill(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[] = []) {
+ createBatchFill(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[] = []): BatchFillOrders {
const batchFill: BatchFillOrders = {
orders: [],
signatures: [],
@@ -21,7 +21,7 @@ export const formatters = {
});
return batchFill;
},
- createMarketSellOrders(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber) {
+ createMarketSellOrders(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber): MarketSellOrders {
const marketSellOrders: MarketSellOrders = {
orders: [],
signatures: [],
@@ -34,7 +34,7 @@ export const formatters = {
});
return marketSellOrders;
},
- createMarketBuyOrders(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber) {
+ createMarketBuyOrders(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber): MarketBuyOrders {
const marketBuyOrders: MarketBuyOrders = {
orders: [],
signatures: [],
@@ -47,7 +47,7 @@ export const formatters = {
});
return marketBuyOrders;
},
- createBatchCancel(signedOrders: SignedOrder[]) {
+ createBatchCancel(signedOrders: SignedOrder[]): BatchCancelOrders {
const batchCancel: BatchCancelOrders = {
orders: [],
};
diff --git a/packages/contracts/src/utils/match_order_tester.ts b/packages/contracts/src/utils/match_order_tester.ts
new file mode 100644
index 000000000..30039937f
--- /dev/null
+++ b/packages/contracts/src/utils/match_order_tester.ts
@@ -0,0 +1,356 @@
+import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { LogWithDecodedArgs } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import * as chai from 'chai';
+import ethUtil = require('ethereumjs-util');
+import * as _ from 'lodash';
+
+import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
+import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
+import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy';
+import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy';
+import {
+ CancelContractEventArgs,
+ ExchangeContract,
+ FillContractEventArgs,
+} from '../contract_wrappers/generated/exchange';
+import { assetProxyUtils } from '../utils/asset_proxy_utils';
+import { chaiSetup } from '../utils/chai_setup';
+import { constants } from '../utils/constants';
+import { crypto } from '../utils/crypto';
+import { ERC20Wrapper } from '../utils/erc20_wrapper';
+import { ERC721Wrapper } from '../utils/erc721_wrapper';
+import { ExchangeWrapper } from '../utils/exchange_wrapper';
+import { OrderFactory } from '../utils/order_factory';
+import { orderUtils } from '../utils/order_utils';
+import {
+ AssetProxyId,
+ ContractName,
+ ERC20BalancesByOwner,
+ ERC721TokenIdsByOwner,
+ ExchangeStatus,
+ SignedOrder,
+ TransferAmountsByMatchOrders as TransferAmounts,
+} from '../utils/types';
+import { provider, web3Wrapper } from '../utils/web3_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+
+export class MatchOrderTester {
+ private _exchangeWrapper: ExchangeWrapper;
+ private _erc20Wrapper: ERC20Wrapper;
+ private _erc721Wrapper: ERC721Wrapper;
+ private _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,
+ realERC20BalancesByOwner: ERC20BalancesByOwner,
+ expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ ): boolean {
+ // ERC20 Balances
+ const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner);
+ if (!doesErc20BalancesMatch) {
+ return false;
+ }
+ // ERC721 Token Ids
+ const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(
+ expectedNewERC721TokenIdsByOwner,
+ tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ },
+ );
+ const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ });
+ const doesErc721TokenIdsMatch = _.isEqual(
+ sortedExpectedNewERC721TokenIdsByOwner,
+ sortedNewERC721TokenIdsByOwner,
+ );
+ return doesErc721TokenIdsMatch;
+ }
+ /// @dev Constructs new MatchOrderTester.
+ /// @param exchangeWrapper Used to call to the Exchange.
+ /// @param erc20Wrapper Used to fetch ERC20 balances.
+ /// @param erc721Wrapper Used to fetch ERC721 token owners.
+ /// @param feeTokenAddress Address of ERC20 fee token.
+ constructor(
+ exchangeWrapper: ExchangeWrapper,
+ erc20Wrapper: ERC20Wrapper,
+ erc721Wrapper: ERC721Wrapper,
+ feeTokenAddress: string,
+ ) {
+ this._exchangeWrapper = exchangeWrapper;
+ this._erc20Wrapper = erc20Wrapper;
+ this._erc721Wrapper = erc721Wrapper;
+ this._feeTokenAddress = feeTokenAddress;
+ }
+ /// @dev Matches two complementary orders and validates results.
+ /// Validation either succeeds or throws.
+ /// @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.
+ /// @return New ERC20 balances & ERC721 token owners.
+ public async matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ takerAddress: string,
+ erc20BalancesByOwner: ERC20BalancesByOwner,
+ erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ initialTakerAssetFilledAmountLeft?: BigNumber,
+ initialTakerAssetFilledAmountRight?: BigNumber,
+ ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
+ // Test setup & verify preconditions
+ const makerAddressLeft = signedOrderLeft.makerAddress;
+ const makerAddressRight = signedOrderRight.makerAddress;
+ const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
+ const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
+ // Verify Left order preconditions
+ const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.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(
+ orderUtils.getOrderHashHex(signedOrderRight),
+ );
+ 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 newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
+ const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
+ // Calculate expected balance changes
+ const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ orderTakerAssetFilledAmountLeft,
+ orderTakerAssetFilledAmountRight,
+ );
+ let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
+ let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ expectedTransferAmounts,
+ );
+ // Assert our expected balances are equal to the actual balances
+ const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances(
+ expectedERC20BalancesByOwner,
+ newERC20BalancesByOwner,
+ expectedERC721TokenIdsByOwner,
+ newERC721TokenIdsByOwner,
+ );
+ 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.
+ /// @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.
+ /// @return TransferAmounts A struct containing the expected transfer amounts.
+ private async _calculateExpectedTransferAmountsAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ orderTakerAssetFilledAmountLeft: BigNumber,
+ orderTakerAssetFilledAmountRight: BigNumber,
+ ): Promise<TransferAmounts> {
+ let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.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);
+ let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.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;
+ }
+ /// @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 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.
+ /// @return Expected ERC20 balances & ERC721 token owners after orders have been matched.
+ private _calculateExpectedBalances(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ takerAddress: string,
+ erc20BalancesByOwner: ERC20BalancesByOwner,
+ erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ expectedTransferAmounts: TransferAmounts,
+ ): [ERC20BalancesByOwner, ERC721TokenIdsByOwner] {
+ const makerAddressLeft = signedOrderLeft.makerAddress;
+ const makerAddressRight = signedOrderRight.makerAddress;
+ const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
+ const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
+ // Operations are performed on copies of the balances
+ const expectedNewERC20BalancesByOwner = _.cloneDeep(erc20BalancesByOwner);
+ const expectedNewERC721TokenIdsByOwner = _.cloneDeep(erc721TokenIdsByOwner);
+ // Left Maker Asset (Right Taker Asset)
+ const makerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.makerAssetData);
+ if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ // Decode asset data
+ const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc20ProxyData.tokenAddress;
+ const takerAssetAddressRight = makerAssetAddressLeft;
+ // Left Maker
+ expectedNewERC20BalancesByOwner[makerAddressLeft][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][makerAssetAddressLeft].minus(expectedTransferAmounts.amountSoldByLeftMaker);
+ // Right Maker
+ expectedNewERC20BalancesByOwner[makerAddressRight][
+ takerAssetAddressRight
+ ] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add(
+ expectedTransferAmounts.amountReceivedByRightMaker,
+ );
+ // Taker
+ expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ takerAddress
+ ][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker);
+ } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ // Decode asset data
+ const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc721ProxyData.tokenAddress;
+ const makerAssetIdLeft = erc721ProxyData.tokenId;
+ const takerAssetAddressRight = makerAssetAddressLeft;
+ const takerAssetIdRight = makerAssetIdLeft;
+ // Left Maker
+ _.remove(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft], makerAssetIdLeft);
+ // Right Maker
+ expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight].push(takerAssetIdRight);
+ // Taker: Since there is only 1 asset transferred, the taker does not receive any of the left maker asset.
+ }
+ // Left Taker Asset (Right Maker Asset)
+ // Note: This exchange is only between the order makers: the Taker does not receive any of the left taker asset.
+ const takerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.takerAssetData);
+ if (takerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ // Decode asset data
+ const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.takerAssetData);
+ const takerAssetAddressLeft = erc20ProxyData.tokenAddress;
+ const makerAssetAddressRight = takerAssetAddressLeft;
+ // Left Maker
+ expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker);
+ // Right Maker
+ expectedNewERC20BalancesByOwner[makerAddressRight][
+ makerAssetAddressRight
+ ] = expectedNewERC20BalancesByOwner[makerAddressRight][makerAssetAddressRight].minus(
+ expectedTransferAmounts.amountSoldByRightMaker,
+ );
+ } else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ // Decode asset data
+ const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderRight.makerAssetData);
+ const makerAssetAddressRight = erc721ProxyData.tokenAddress;
+ const makerAssetIdRight = erc721ProxyData.tokenId;
+ const takerAssetAddressLeft = makerAssetAddressRight;
+ const takerAssetIdLeft = makerAssetIdRight;
+ // Right Maker
+ _.remove(expectedNewERC721TokenIdsByOwner[makerAddressRight][makerAssetAddressRight], makerAssetIdRight);
+ // Left Maker
+ expectedNewERC721TokenIdsByOwner[makerAddressLeft][takerAssetAddressLeft].push(takerAssetIdLeft);
+ }
+ // Left Maker Fees
+ expectedNewERC20BalancesByOwner[makerAddressLeft][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByLeftMaker);
+ // Right Maker Fees
+ expectedNewERC20BalancesByOwner[makerAddressRight][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
+ makerAddressRight
+ ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByRightMaker);
+ // Taker Fees
+ expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
+ takerAddress
+ ][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker);
+ // Left Fee Recipient Fees
+ expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
+ this._feeTokenAddress
+ ] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
+ expectedTransferAmounts.feeReceivedLeft,
+ );
+ // Right Fee Recipient Fees
+ expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
+ this._feeTokenAddress
+ ] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
+ expectedTransferAmounts.feeReceivedRight,
+ );
+
+ return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner];
+ }
+}
diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts
index 044e9b865..86b3d5ac7 100644
--- a/packages/contracts/src/utils/order_factory.ts
+++ b/packages/contracts/src/utils/order_factory.ts
@@ -1,7 +1,8 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
+import { constants } from './constants';
import { orderUtils } from './order_utils';
import { signingUtils } from './signing_utils';
import { SignatureType, SignedOrder, UnsignedOrder } from './types';
@@ -19,10 +20,10 @@ export class OrderFactory {
): SignedOrder {
const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000));
const order = ({
- senderAddress: ZeroEx.NULL_ADDRESS,
+ senderAddress: constants.NULL_ADDRESS,
expirationTimeSeconds: randomExpiration,
- salt: ZeroEx.generatePseudoRandomSalt(),
- takerAddress: ZeroEx.NULL_ADDRESS,
+ salt: generatePseudoRandomSalt(),
+ takerAddress: constants.NULL_ADDRESS,
...this._defaultOrderParams,
...customOrderParams,
} as any) as UnsignedOrder;
diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts
index 7a482ad9e..8adc6b735 100644
--- a/packages/contracts/src/utils/order_utils.ts
+++ b/packages/contracts/src/utils/order_utils.ts
@@ -4,7 +4,7 @@ import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import { crypto } from './crypto';
-import { OrderStruct, SignatureType, SignedOrder, UnsignedOrder } from './types';
+import { CancelOrder, MatchOrder, OrderStruct, SignatureType, SignedOrder, UnsignedOrder } from './types';
export const orderUtils = {
createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => {
@@ -15,7 +15,7 @@ export const orderUtils = {
};
return fill;
},
- createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber) {
+ createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber): CancelOrder {
const cancel = {
order: orderUtils.getOrderStruct(signedOrder),
takerAssetCancelAmount: takerAssetCancelAmount || signedOrder.takerAssetAmount,
@@ -80,7 +80,7 @@ export const orderUtils = {
const orderHashHex = `0x${orderHashBuff.toString('hex')}`;
return orderHashHex;
},
- createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder) {
+ createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder {
const fill = {
left: orderUtils.getOrderStruct(signedOrderLeft),
right: orderUtils.getOrderStruct(signedOrderRight),
diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts
index 3a4f48330..941bff96d 100644
--- a/packages/contracts/src/utils/transaction_factory.ts
+++ b/packages/contracts/src/utils/transaction_factory.ts
@@ -1,4 +1,4 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as ethUtil from 'ethereumjs-util';
@@ -20,7 +20,7 @@ export class TransactionFactory {
data: string,
signatureType: SignatureType = SignatureType.Ecrecover,
): SignedTransaction {
- const salt = ZeroEx.generatePseudoRandomSalt();
+ const salt = generatePseudoRandomSalt();
const txHash = crypto.solSHA3([this._exchangeAddress, salt, ethUtil.toBuffer(data)]);
const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType);
const signedTx = {
diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts
index 8d81adece..ef86b4f38 100644
--- a/packages/contracts/src/utils/types.ts
+++ b/packages/contracts/src/utils/types.ts
@@ -197,3 +197,15 @@ export interface ProxyData {
tokenAddress?: string;
data?: any;
}
+
+export interface CancelOrder {
+ order: OrderStruct;
+ takerAssetCancelAmount: BigNumber;
+}
+
+export interface MatchOrder {
+ left: OrderStruct;
+ right: OrderStruct;
+ leftSignature: string;
+ rightSignature: string;
+}
diff --git a/packages/contracts/src/utils/web3_wrapper.ts b/packages/contracts/src/utils/web3_wrapper.ts
index ed1c488a2..02595506b 100644
--- a/packages/contracts/src/utils/web3_wrapper.ts
+++ b/packages/contracts/src/utils/web3_wrapper.ts
@@ -1,12 +1,19 @@
-import { devConstants, web3Factory } from '@0xproject/dev-utils';
+import { devConstants, env, EnvVars, web3Factory } from '@0xproject/dev-utils';
+import { prependSubprovider } from '@0xproject/subproviders';
import { Provider } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { coverage } from './coverage';
+
export const txDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
gas: devConstants.GAS_ESTIMATE,
};
const providerConfigs = { shouldUseInProcessGanache: true };
-export const web3 = web3Factory.create(providerConfigs);
-export const provider = web3.currentProvider;
+export const provider = web3Factory.getRpcProvider(providerConfigs);
+const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
+if (isCoverageEnabled) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ prependSubprovider(provider, coverageSubprovider);
+}
export const web3Wrapper = new Web3Wrapper(provider);