aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/contracts/src/utils/erc20_wrapper.ts28
-rw-r--r--packages/contracts/src/utils/new_order_factory.ts264
-rw-r--r--packages/contracts/src/utils/order_info_utils.ts44
-rw-r--r--packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts20
-rw-r--r--packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts32
-rw-r--r--packages/contracts/src/utils/types.ts26
-rw-r--r--packages/contracts/test/combinatorial_tests.ts266
7 files changed, 677 insertions, 3 deletions
diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts
index 91c9d50b7..fc42fbff2 100644
--- a/packages/contracts/src/utils/erc20_wrapper.ts
+++ b/packages/contracts/src/utils/erc20_wrapper.ts
@@ -25,8 +25,11 @@ export class ERC20Wrapper {
this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress;
}
- public async deployDummyTokensAsync(): Promise<DummyERC20TokenContract[]> {
- for (let i = 0; i < constants.NUM_DUMMY_ERC20_TO_DEPLOY; i++) {
+ public async deployDummyTokensAsync(num?: number, decimals?: BigNumber): Promise<DummyERC20TokenContract[]> {
+ // TODO(fabio): Remove and refactor all tests
+ const finalNum = _.isUndefined(num) ? constants.NUM_DUMMY_ERC20_TO_DEPLOY : num;
+ const finalDecimals = _.isUndefined(decimals) ? constants.DUMMY_TOKEN_DECIMALS : decimals;
+ for (let i = 0; i < finalNum; i++) {
this._dummyTokenContracts.push(
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC20Token,
@@ -34,7 +37,7 @@ export class ERC20Wrapper {
txDefaults,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
- constants.DUMMY_TOKEN_DECIMALS,
+ finalDecimals,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
),
);
@@ -73,6 +76,25 @@ export class ERC20Wrapper {
}
}
}
+ public async getBalanceAsync(owner: string, token: string): Promise<BigNumber> {
+ const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token);
+ if (_.isUndefined(tokenContractIfExists)) {
+ throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`);
+ }
+ const balance = new BigNumber(await tokenContractIfExists.balanceOf.callAsync(owner));
+ return balance;
+ }
+ public async getProxyAllowanceAsync(owner: string, token: string): Promise<BigNumber> {
+ this._validateProxyContractExistsOrThrow();
+ const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token);
+ if (_.isUndefined(tokenContractIfExists)) {
+ throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`);
+ }
+ const balance = new BigNumber(
+ await tokenContractIfExists.allowance.callAsync(owner, (this._proxyContract as ERC20ProxyContract).address),
+ );
+ return balance;
+ }
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
this._validateDummyTokenContractsExistOrThrow();
const balancesByOwner: ERC20BalancesByOwner = {};
diff --git a/packages/contracts/src/utils/new_order_factory.ts b/packages/contracts/src/utils/new_order_factory.ts
new file mode 100644
index 000000000..a4ded4230
--- /dev/null
+++ b/packages/contracts/src/utils/new_order_factory.ts
@@ -0,0 +1,264 @@
+import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils';
+import { Order } from '@0xproject/types';
+import { BigNumber, errorUtils } from '@0xproject/utils';
+
+import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
+
+import { constants } from './constants';
+import {
+ AssetDataScenario,
+ ERC721TokenIdsByOwner,
+ ExpirationTimeSecondsScenario,
+ FeeRecipientAddressScenario,
+ OrderAmountScenario,
+} from './types';
+
+const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000);
+const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000);
+const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000);
+const ONE_NFT_UNIT = new BigNumber(1);
+const TEN_MINUTES_MS = 1000 * 60 * 10;
+
+/*
+ * TODO:
+ * - Write function that given an order, fillAmount, retrieves orderRelevantState and maps it to expected test outcome.
+ * - Write function that generates order permutations.
+ * - Write functions for other steps that must be permutated
+ */
+
+export class NewOrderFactory {
+ private _userAddresses: string[];
+ private _zrxAddress: string;
+ private _nonZrxERC20EighteenDecimalTokenAddresses: string[];
+ private _erc20FiveDecimalTokenAddresses: string[];
+ private _erc721Token: DummyERC721TokenContract;
+ private _erc721Balances: ERC721TokenIdsByOwner;
+ private _exchangeAddress: string;
+ constructor(
+ userAddresses: string[],
+ zrxAddress: string,
+ nonZrxERC20EighteenDecimalTokenAddresses: string[],
+ erc20FiveDecimalTokenAddresses: string[],
+ erc721Token: DummyERC721TokenContract,
+ erc721Balances: ERC721TokenIdsByOwner,
+ exchangeAddress: string,
+ ) {
+ this._userAddresses = userAddresses;
+ this._zrxAddress = zrxAddress;
+ this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses;
+ this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses;
+ this._erc721Token = erc721Token;
+ this._erc721Balances = erc721Balances;
+ this._exchangeAddress = exchangeAddress;
+ }
+ public generateOrder(
+ feeRecipientScenario: FeeRecipientAddressScenario,
+ makerAssetAmountScenario: OrderAmountScenario,
+ takerAssetAmountScenario: OrderAmountScenario,
+ makerFeeScenario: OrderAmountScenario,
+ takerFeeScenario: OrderAmountScenario,
+ expirationTimeSecondsScenario: ExpirationTimeSecondsScenario,
+ makerAssetDataScenario: AssetDataScenario,
+ takerAssetDataScenario: AssetDataScenario,
+ ): Order {
+ const makerAddress = this._userAddresses[1];
+ const takerAddress = this._userAddresses[2];
+ const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address];
+ const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address];
+ let feeRecipientAddress;
+ let makerAssetAmount;
+ let takerAssetAmount;
+ let makerFee;
+ let takerFee;
+ let expirationTimeSeconds;
+ let makerAssetData;
+ let takerAssetData;
+
+ switch (feeRecipientScenario) {
+ case FeeRecipientAddressScenario.BurnAddress:
+ feeRecipientAddress = constants.NULL_ADDRESS;
+ break;
+ case FeeRecipientAddressScenario.EthUserAddress:
+ feeRecipientAddress = this._userAddresses[4];
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario);
+ }
+
+ const invalidAssetProxyIdHex = '0A';
+ switch (makerAssetDataScenario) {
+ case AssetDataScenario.ZRXFeeToken:
+ makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress);
+ break;
+ case AssetDataScenario.ERC20NonZRXEighteenDecimals:
+ makerAssetData = assetProxyUtils.encodeERC20ProxyData(
+ this._nonZrxERC20EighteenDecimalTokenAddresses[0],
+ );
+ break;
+ case AssetDataScenario.ERC20FiveDecimals:
+ makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]);
+ break;
+ case AssetDataScenario.ERC20InvalidAssetProxyId: {
+ const validAssetData = assetProxyUtils.encodeERC20ProxyData(
+ this._nonZrxERC20EighteenDecimalTokenAddresses[0],
+ );
+ makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
+ break;
+ }
+ case AssetDataScenario.ERC721ValidAssetProxyId:
+ makerAssetData = assetProxyUtils.encodeERC721ProxyData(
+ this._erc721Token.address,
+ erc721MakerAssetIds[0],
+ );
+ break;
+ case AssetDataScenario.ERC721InvalidAssetProxyId: {
+ const validAssetData = assetProxyUtils.encodeERC721ProxyData(
+ this._erc721Token.address,
+ erc721MakerAssetIds[0],
+ );
+ makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
+ break;
+ }
+ default:
+ throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario);
+ }
+
+ switch (takerAssetDataScenario) {
+ case AssetDataScenario.ZRXFeeToken:
+ takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress);
+ break;
+ case AssetDataScenario.ERC20NonZRXEighteenDecimals:
+ takerAssetData = assetProxyUtils.encodeERC20ProxyData(
+ this._nonZrxERC20EighteenDecimalTokenAddresses[1],
+ );
+ break;
+ case AssetDataScenario.ERC20FiveDecimals:
+ takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]);
+ break;
+ case AssetDataScenario.ERC20InvalidAssetProxyId: {
+ const validAssetData = assetProxyUtils.encodeERC20ProxyData(
+ this._nonZrxERC20EighteenDecimalTokenAddresses[1],
+ );
+ takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
+ break;
+ }
+ case AssetDataScenario.ERC721ValidAssetProxyId:
+ takerAssetData = assetProxyUtils.encodeERC721ProxyData(
+ this._erc721Token.address,
+ erc721TakerAssetIds[0],
+ );
+ break;
+ case AssetDataScenario.ERC721InvalidAssetProxyId: {
+ const validAssetData = assetProxyUtils.encodeERC721ProxyData(
+ this._erc721Token.address,
+ erc721TakerAssetIds[0],
+ );
+ takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
+ break;
+ }
+ default:
+ throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario);
+ }
+
+ switch (makerAssetAmountScenario) {
+ case OrderAmountScenario.NonZero:
+ switch (makerAssetDataScenario) {
+ case AssetDataScenario.ERC20NonZRXEighteenDecimals:
+ case AssetDataScenario.ERC20InvalidAssetProxyId:
+ makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
+ break;
+ case AssetDataScenario.ERC20FiveDecimals:
+ makerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
+ break;
+ case AssetDataScenario.ERC721ValidAssetProxyId:
+ case AssetDataScenario.ERC721InvalidAssetProxyId:
+ makerAssetAmount = ONE_NFT_UNIT;
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario);
+ }
+ break;
+ case OrderAmountScenario.Zero:
+ makerAssetAmount = new BigNumber(0);
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario);
+ }
+
+ switch (takerAssetAmountScenario) {
+ case OrderAmountScenario.NonZero:
+ switch (takerAssetDataScenario) {
+ case AssetDataScenario.ERC20NonZRXEighteenDecimals:
+ case AssetDataScenario.ERC20InvalidAssetProxyId:
+ takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
+ break;
+ case AssetDataScenario.ERC20FiveDecimals:
+ takerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
+ break;
+ case AssetDataScenario.ERC721ValidAssetProxyId:
+ case AssetDataScenario.ERC721InvalidAssetProxyId:
+ takerAssetAmount = ONE_NFT_UNIT;
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario);
+ }
+ break;
+ case OrderAmountScenario.Zero:
+ takerAssetAmount = new BigNumber(0);
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario);
+ }
+
+ switch (makerFeeScenario) {
+ case OrderAmountScenario.NonZero:
+ makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS;
+ break;
+ case OrderAmountScenario.Zero:
+ makerFee = new BigNumber(0);
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario);
+ }
+
+ switch (takerFeeScenario) {
+ case OrderAmountScenario.NonZero:
+ takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS;
+ break;
+ case OrderAmountScenario.Zero:
+ takerFee = new BigNumber(0);
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario);
+ }
+
+ switch (expirationTimeSecondsScenario) {
+ case ExpirationTimeSecondsScenario.InFuture:
+ expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS);
+ break;
+ case ExpirationTimeSecondsScenario.InPast:
+ expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS);
+ break;
+ default:
+ throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario);
+ }
+
+ const order: Order = {
+ senderAddress: constants.NULL_ADDRESS,
+ makerAddress,
+ takerAddress,
+ makerFee,
+ takerFee,
+ makerAssetAmount,
+ takerAssetAmount,
+ makerAssetData,
+ takerAssetData,
+ salt: generatePseudoRandomSalt(),
+ exchangeAddress: this._exchangeAddress,
+ feeRecipientAddress,
+ expirationTimeSeconds,
+ };
+
+ return order;
+ }
+}
diff --git a/packages/contracts/src/utils/order_info_utils.ts b/packages/contracts/src/utils/order_info_utils.ts
new file mode 100644
index 000000000..9df627da3
--- /dev/null
+++ b/packages/contracts/src/utils/order_info_utils.ts
@@ -0,0 +1,44 @@
+import { assetProxyUtils, OrderStateUtils } from '@0xproject/order-utils';
+import { SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+
+import { ExchangeContract } from '../contract_wrappers/generated/exchange';
+
+import { constants } from './constants';
+import { ERC20Wrapper } from './erc20_wrapper';
+import { SimpleERC20BalanceAndProxyAllowanceFetcher } from './simple_erc20_balance_and_allowance_fetcher';
+import { SimpleOrderFilledCancelledFetcher } from './simple_filled_cancelled_fetcher';
+
+export class OrderInfoUtils {
+ private _orderStateUtils: OrderStateUtils;
+ private _erc20Wrapper: ERC20Wrapper;
+ constructor(exchangeContract: ExchangeContract, erc20Wrapper: ERC20Wrapper, zrxAddress: string) {
+ this._erc20Wrapper = erc20Wrapper;
+ const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(exchangeContract, zrxAddress);
+ const simpleERC20BalanceAndProxyAllowanceFetcher = new SimpleERC20BalanceAndProxyAllowanceFetcher(erc20Wrapper);
+ this._orderStateUtils = new OrderStateUtils(
+ simpleERC20BalanceAndProxyAllowanceFetcher,
+ simpleOrderFilledCancelledFetcher,
+ );
+ }
+ public async getFillableTakerAssetAmountAsync(signedOrder: SignedOrder, takerAddress: string): Promise<BigNumber> {
+ const orderRelevantState = await this._orderStateUtils.getOrderRelevantStateAsync(signedOrder);
+ console.log('orderRelevantState', orderRelevantState);
+ if (takerAddress === constants.NULL_ADDRESS) {
+ return orderRelevantState.remainingFillableTakerAssetAmount;
+ }
+ const takerAssetData = assetProxyUtils.decodeERC20ProxyData(signedOrder.takerAssetData);
+ const takerBalance = await this._erc20Wrapper.getBalanceAsync(takerAddress, takerAssetData.tokenAddress);
+ const takerAllowance = await this._erc20Wrapper.getProxyAllowanceAsync(
+ takerAddress,
+ takerAssetData.tokenAddress,
+ );
+ // TODO: We also need to make sure taker has sufficient ZRX for fees...
+ const fillableTakerAssetAmount = BigNumber.min([
+ takerBalance,
+ takerAllowance,
+ orderRelevantState.remainingFillableTakerAssetAmount,
+ ]);
+ return fillableTakerAssetAmount;
+ }
+}
diff --git a/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts
new file mode 100644
index 000000000..6eed9227a
--- /dev/null
+++ b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts
@@ -0,0 +1,20 @@
+import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils';
+import { BigNumber } from '@0xproject/utils';
+
+import { ERC20Wrapper } from './erc20_wrapper';
+
+// TODO(fabio): Refactor this to also work for ERC721!
+export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
+ private _erc20TokenWrapper: ERC20Wrapper;
+ constructor(erc20TokenWrapper: ERC20Wrapper) {
+ this._erc20TokenWrapper = erc20TokenWrapper;
+ }
+ public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ const balance = await this._erc20TokenWrapper.getBalanceAsync(userAddress, tokenAddress);
+ return balance;
+ }
+ public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ const proxyAllowance = await this._erc20TokenWrapper.getProxyAllowanceAsync(userAddress, tokenAddress);
+ return proxyAllowance;
+ }
+}
diff --git a/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts
new file mode 100644
index 000000000..0a80637cc
--- /dev/null
+++ b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts
@@ -0,0 +1,32 @@
+import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils';
+import { BigNumber } from '@0xproject/utils';
+import { BlockParamLiteral } from 'ethereum-types';
+
+import {
+ CancelContractEventArgs,
+ ExchangeContract,
+ FillContractEventArgs,
+} from '../contract_wrappers/generated/exchange';
+
+export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
+ private _exchangeContract: ExchangeContract;
+ private _zrxAddress: string;
+ constructor(exchange: ExchangeContract, zrxAddress: string) {
+ this._exchangeContract = exchange;
+ this._zrxAddress = zrxAddress;
+ }
+ public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ const filledTakerAmount = new BigNumber(await this._exchangeContract.filled.callAsync(orderHash));
+ return filledTakerAmount;
+ }
+ public async isOrderCancelledAsync(orderHash: string): Promise<boolean> {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Latest,
+ };
+ const isCancelled = await this._exchangeContract.cancelled.callAsync(orderHash);
+ return isCancelled;
+ }
+ public getZRXTokenAddress(): string {
+ return this._zrxAddress;
+ }
+}
diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts
index bb8c12088..a6e1cd6d0 100644
--- a/packages/contracts/src/utils/types.ts
+++ b/packages/contracts/src/utils/types.ts
@@ -150,3 +150,29 @@ export interface MatchOrder {
leftSignature: string;
rightSignature: string;
}
+
+// Combinatorial testing types
+
+export enum FeeRecipientAddressScenario {
+ BurnAddress = 'BURN_ADDRESS',
+ EthUserAddress = 'ETH_USER_ADDRESS',
+}
+
+export enum OrderAmountScenario {
+ Zero = 'ZERO',
+ NonZero = 'NON_ZERO',
+}
+
+export enum ExpirationTimeSecondsScenario {
+ InPast = 'IN_PAST',
+ InFuture = 'IN_FUTURE',
+}
+
+export enum AssetDataScenario {
+ ERC721ValidAssetProxyId = 'ERC721_VALID_ASSET_PROXY_ID',
+ ERC721InvalidAssetProxyId = 'ERC721_INVALID_ASSET_PROXY_ID',
+ ZRXFeeToken = 'ZRX_FEE_TOKEN',
+ ERC20InvalidAssetProxyId = 'ERC20_INVALID_ASSET_PROXY_ID',
+ ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS',
+ ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS',
+}
diff --git a/packages/contracts/test/combinatorial_tests.ts b/packages/contracts/test/combinatorial_tests.ts
new file mode 100644
index 000000000..7a118f6ac
--- /dev/null
+++ b/packages/contracts/test/combinatorial_tests.ts
@@ -0,0 +1,266 @@
+import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { assetProxyUtils, crypto, orderHashUtils, OrderStateUtils } from '@0xproject/order-utils';
+import { AssetProxyId, SignatureType, SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import * as chai from 'chai';
+import { LogWithDecodedArgs } from 'ethereum-types';
+import ethUtil = require('ethereumjs-util');
+import 'make-promises-safe';
+
+import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token';
+import { DummyERC721TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c721_token';
+import { ERC20ProxyContract } from '../src/contract_wrappers/generated/e_r_c20_proxy';
+import { ERC721ProxyContract } from '../src/contract_wrappers/generated/e_r_c721_proxy';
+import {
+ CancelContractEventArgs,
+ ExchangeContract,
+ FillContractEventArgs,
+} from '../src/contract_wrappers/generated/exchange';
+import { artifacts } from '../src/utils/artifacts';
+import { chaiSetup } from '../src/utils/chai_setup';
+import { constants } from '../src/utils/constants';
+import { ERC20Wrapper } from '../src/utils/erc20_wrapper';
+import { ERC721Wrapper } from '../src/utils/erc721_wrapper';
+import { ExchangeWrapper } from '../src/utils/exchange_wrapper';
+import { NewOrderFactory } from '../src/utils/new_order_factory';
+import { OrderInfoUtils } from '../src/utils/order_info_utils';
+import { orderUtils } from '../src/utils/order_utils';
+import { signingUtils } from '../src/utils/signing_utils';
+import {
+ AssetDataScenario,
+ ContractName,
+ ERC20BalancesByOwner,
+ ExpirationTimeSecondsScenario,
+ FeeRecipientAddressScenario,
+ OrderAmountScenario,
+ OrderStatus,
+} from '../src/utils/types';
+
+import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+
+describe('Combinatorial tests', () => {
+ let newOrderFactory: NewOrderFactory;
+ let usedAddresses: string[];
+
+ let makerAddress: string;
+ let owner: string;
+ let takerAddress: string;
+ let feeRecipientAddress: string;
+
+ let erc20EighteenDecimalTokenA: DummyERC20TokenContract;
+ let erc20EighteenDecimalTokenB: DummyERC20TokenContract;
+ let erc20FiveDecimalTokenA: DummyERC20TokenContract;
+ let erc20FiveDecimalTokenB: DummyERC20TokenContract;
+ let zrxToken: DummyERC20TokenContract;
+ let erc721Token: DummyERC721TokenContract;
+ let exchange: ExchangeContract;
+ let erc20Proxy: ERC20ProxyContract;
+ let erc721Proxy: ERC721ProxyContract;
+
+ let erc20Balances: ERC20BalancesByOwner;
+ let exchangeWrapper: ExchangeWrapper;
+ let erc20Wrapper: ERC20Wrapper;
+ let erc721Wrapper: ERC721Wrapper;
+
+ let erc721MakerAssetIds: BigNumber[];
+ let erc721TakerAssetIds: BigNumber[];
+
+ let defaultMakerAssetAddress: string;
+ let defaultTakerAssetAddress: string;
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ usedAddresses = [owner, makerAddress, takerAddress, feeRecipientAddress] = accounts;
+
+ erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
+ erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
+
+ const erc20EighteenDecimalTokenCount = 3;
+ const eighteenDecimals = new BigNumber(18);
+ [erc20EighteenDecimalTokenA, erc20EighteenDecimalTokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
+ erc20EighteenDecimalTokenCount,
+ eighteenDecimals,
+ );
+
+ const erc20FiveDecimalTokenCount = 2;
+ const fiveDecimals = new BigNumber(18);
+ [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync(
+ erc20FiveDecimalTokenCount,
+ fiveDecimals,
+ );
+ erc20Proxy = await erc20Wrapper.deployProxyAsync();
+ await erc20Wrapper.setBalancesAndAllowancesAsync();
+
+ [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
+ erc721Proxy = await erc721Wrapper.deployProxyAsync();
+ await erc721Wrapper.setBalancesAndAllowancesAsync();
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
+ erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
+
+ exchange = await ExchangeContract.deployFrom0xArtifactAsync(
+ artifacts.Exchange,
+ provider,
+ txDefaults,
+ assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
+ );
+ exchangeWrapper = new ExchangeWrapper(exchange, provider);
+ await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
+ await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner);
+
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ defaultMakerAssetAddress = erc20EighteenDecimalTokenA.address;
+ defaultTakerAssetAddress = erc20EighteenDecimalTokenB.address;
+
+ newOrderFactory = new NewOrderFactory(
+ usedAddresses,
+ zrxToken.address,
+ [erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address],
+ [erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address],
+ erc721Token,
+ erc721Balances,
+ exchange.address,
+ );
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe.only('Fill order', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ });
+ it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => {
+ const order = newOrderFactory.generateOrder(
+ FeeRecipientAddressScenario.EthUserAddress,
+ OrderAmountScenario.NonZero,
+ OrderAmountScenario.NonZero,
+ OrderAmountScenario.Zero,
+ OrderAmountScenario.Zero,
+ ExpirationTimeSecondsScenario.InFuture,
+ AssetDataScenario.ERC20NonZRXEighteenDecimals,
+ AssetDataScenario.ERC20NonZRXEighteenDecimals,
+ );
+
+ // TODO: Permute signature types
+
+ // TODO: Sign order (for now simply ECSign)
+ const orderHashBuff = orderHashUtils.getOrderHashBuff(order);
+ const privateKey = constants.TESTRPC_PRIVATE_KEYS[usedAddresses.indexOf(makerAddress)];
+ const signature = signingUtils.signMessage(orderHashBuff, privateKey, SignatureType.EthSign);
+ const signedOrder = {
+ ...order,
+ signature: `0x${signature.toString('hex')}`,
+ };
+ console.log('signedOrder', signedOrder);
+
+ // TODO: Get orderRelevantState
+ const orderInfoUtils = new OrderInfoUtils(exchange, erc20Wrapper, zrxToken.address);
+ // 1. How much of this order can I fill?
+ const fillableTakerAssetAmount = await orderInfoUtils.getFillableTakerAssetAmountAsync(
+ signedOrder,
+ takerAddress,
+ );
+ console.log('fillableTakerAssetAmount', fillableTakerAssetAmount);
+
+ // TODO: Decide how much to fill (all, some)
+ const takerFillAmount = fillableTakerAssetAmount.div(2); // some for now
+
+ // 2. If I fill it by X, what are the resulting balances/allowances/filled amounts expected?
+ // NOTE: we can't use orderStateUtils for this :( We need to do this ourselves.
+
+ // This doesn't include taker balance/allowance checks...
+ /*
+ Inputs:
+ - signedOrder
+ - takerAddress
+ Outputs:
+ - Check fillable amount
+ - maker token balance & allowance
+ - maker ZRX balance & allowance
+ - taker token balance & allowance
+ - taker ZRX balance & allowance
+ Test:
+ - If fillable >= fillAmount:
+ - check that filled by fillAmount
+ - check makerBalance
+ */
+
+ // signedOrder = orderFactory.newSignedOrder({
+ // makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ // takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ // }); );
+ //
+ // const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync(
+ // orderHashUtils.getOrderHashHex(signedOrder),
+ // );
+ // expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0);
+ //
+ // const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
+ // await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount });
+ //
+ // const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync(
+ // orderHashUtils.getOrderHashHex(signedOrder),
+ // );
+ // expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount);
+ //
+ // const newBalances = await erc20Wrapper.getBalancesAsync();
+ //
+ // const makerAssetFilledAmount = takerAssetFillAmount
+ // .times(signedOrder.makerAssetAmount)
+ // .dividedToIntegerBy(signedOrder.takerAssetAmount);
+ // const makerFeePaid = signedOrder.makerFee
+ // .times(makerAssetFilledAmount)
+ // .dividedToIntegerBy(signedOrder.makerAssetAmount);
+ // const takerFeePaid = signedOrder.takerFee
+ // .times(makerAssetFilledAmount)
+ // .dividedToIntegerBy(signedOrder.makerAssetAmount);
+ // expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ // erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount),
+ // );
+ // expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ // erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount),
+ // );
+ // expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
+ // erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid),
+ // );
+ // expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ // erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount),
+ // );
+ // expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ // erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount),
+ // );
+ // expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
+ // erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid),
+ // );
+ // expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ // erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)),
+ // );
+ });
+ });
+});