Move ExchangeTransferSimulator into contract-exchange tests since that's the only place it's still used and we no longer want to expose it to external developers
This commit is contained in:
		@@ -76,7 +76,7 @@
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@0x/base-contract": "^5.5.0-beta.1",
 | 
			
		||||
        "@0x/contracts-asset-proxy": "^2.3.0-beta.1",
 | 
			
		||||
        "@0x/contracts-dev-utils": "^0.1.0-beta.1",
 | 
			
		||||
        "@0x/contracts-dev-utils": "^2.4.0-beta.1",
 | 
			
		||||
        "@0x/contracts-erc1155": "^1.2.0-beta.1",
 | 
			
		||||
        "@0x/contracts-erc20": "^2.3.0-beta.1",
 | 
			
		||||
        "@0x/contracts-erc721": "^2.2.0-beta.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +1,21 @@
 | 
			
		||||
import {
 | 
			
		||||
    DevUtilsContract,
 | 
			
		||||
    DummyERC20TokenContract,
 | 
			
		||||
    ERC20ProxyContract,
 | 
			
		||||
    ERC20TokenContract,
 | 
			
		||||
} from '@0x/abi-gen-wrappers';
 | 
			
		||||
import * as artifacts from '@0x/contract-artifacts';
 | 
			
		||||
import { BlockchainLifecycle, devConstants } from '@0x/dev-utils';
 | 
			
		||||
import { artifacts as assetProxyArtifacts, ERC20ProxyContract } from '@0x/contracts-asset-proxy';
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract, ERC20TokenContract } from '@0x/contracts-erc20';
 | 
			
		||||
import { blockchainTests, chaiSetup, constants } from '@0x/contracts-test-utils';
 | 
			
		||||
import { TradeSide, TransferType } from '@0x/order-utils';
 | 
			
		||||
import { ExchangeContractErrs } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as chai from 'chai';
 | 
			
		||||
 | 
			
		||||
import { constants } from '../src/constants';
 | 
			
		||||
import { ExchangeTransferSimulator } from '../src/exchange_transfer_simulator';
 | 
			
		||||
import { BalanceAndProxyAllowanceLazyStore } from '../src/store/balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
import { TradeSide, TransferType } from '../src/types';
 | 
			
		||||
 | 
			
		||||
import { chaiSetup } from './utils/chai_setup';
 | 
			
		||||
import { ExchangeTransferSimulator } from './utils/exchange_transfer_simulator';
 | 
			
		||||
import { SimpleERC20BalanceAndProxyAllowanceFetcher } from './utils/simple_erc20_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
 | 
			
		||||
import { BalanceAndProxyAllowanceLazyStore } from './utils/store/balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
 | 
			
		||||
 | 
			
		||||
describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
const GAS_LIMIT = 9e6;
 | 
			
		||||
 | 
			
		||||
blockchainTests('ExchangeTransferSimulator', env => {
 | 
			
		||||
    const transferAmount = new BigNumber(5);
 | 
			
		||||
    let userAddresses: string[];
 | 
			
		||||
    let dummyERC20Token: DummyERC20TokenContract;
 | 
			
		||||
@@ -34,25 +26,25 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
    let exchangeTransferSimulator: ExchangeTransferSimulator;
 | 
			
		||||
    let txHash: string;
 | 
			
		||||
    let erc20ProxyAddress: string;
 | 
			
		||||
    const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
 | 
			
		||||
    const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, env.provider);
 | 
			
		||||
    before(async function(): Promise<void> {
 | 
			
		||||
        const mochaTestTimeoutMs = 20000;
 | 
			
		||||
        this.timeout(mochaTestTimeoutMs); // tslint:disable-line:no-invalid-this
 | 
			
		||||
 | 
			
		||||
        userAddresses = await web3Wrapper.getAvailableAddressesAsync();
 | 
			
		||||
        userAddresses = await env.web3Wrapper.getAvailableAddressesAsync();
 | 
			
		||||
        [coinbase, sender, recipient] = userAddresses;
 | 
			
		||||
 | 
			
		||||
        const txDefaults = {
 | 
			
		||||
            gas: devConstants.GAS_LIMIT,
 | 
			
		||||
            from: devConstants.TESTRPC_FIRST_ADDRESS,
 | 
			
		||||
            gas: GAS_LIMIT,
 | 
			
		||||
            from: userAddresses[0],
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await blockchainLifecycle.startAsync();
 | 
			
		||||
        await env.blockchainLifecycle.startAsync();
 | 
			
		||||
        const erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.ERC20Proxy,
 | 
			
		||||
            provider,
 | 
			
		||||
            assetProxyArtifacts.ERC20Proxy,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            txDefaults,
 | 
			
		||||
            artifacts,
 | 
			
		||||
            assetProxyArtifacts,
 | 
			
		||||
        );
 | 
			
		||||
        erc20ProxyAddress = erc20Proxy.address;
 | 
			
		||||
 | 
			
		||||
@@ -62,10 +54,10 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
        const decimals = new BigNumber(18);
 | 
			
		||||
        // tslint:disable-next-line:no-unused-variable
 | 
			
		||||
        dummyERC20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.DummyERC20Token,
 | 
			
		||||
            provider,
 | 
			
		||||
            erc20Artifacts.DummyERC20Token,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            txDefaults,
 | 
			
		||||
            artifacts,
 | 
			
		||||
            erc20Artifacts,
 | 
			
		||||
            name,
 | 
			
		||||
            symbol,
 | 
			
		||||
            decimals,
 | 
			
		||||
@@ -75,13 +67,13 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
        exampleAssetData = await devUtils.encodeERC20AssetData.callAsync(dummyERC20Token.address);
 | 
			
		||||
    });
 | 
			
		||||
    beforeEach(async () => {
 | 
			
		||||
        await blockchainLifecycle.startAsync();
 | 
			
		||||
        await env.blockchainLifecycle.startAsync();
 | 
			
		||||
    });
 | 
			
		||||
    afterEach(async () => {
 | 
			
		||||
        await blockchainLifecycle.revertAsync();
 | 
			
		||||
        await env.blockchainLifecycle.revertAsync();
 | 
			
		||||
    });
 | 
			
		||||
    after(async () => {
 | 
			
		||||
        await blockchainLifecycle.revertAsync();
 | 
			
		||||
        await env.blockchainLifecycle.revertAsync();
 | 
			
		||||
    });
 | 
			
		||||
    describe('#transferFromAsync', function(): void {
 | 
			
		||||
        // HACK: For some reason these tests need a slightly longer timeout
 | 
			
		||||
@@ -95,7 +87,7 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
            const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
 | 
			
		||||
                simpleERC20BalanceAndProxyAllowanceFetcher,
 | 
			
		||||
            );
 | 
			
		||||
            exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
 | 
			
		||||
            exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore, env.provider);
 | 
			
		||||
        });
 | 
			
		||||
        it("throws if the user doesn't have enough allowance", async () => {
 | 
			
		||||
            return expect(
 | 
			
		||||
@@ -113,7 +105,7 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
            txHash = await dummyERC20Token.approve.sendTransactionAsync(erc20ProxyAddress, transferAmount, {
 | 
			
		||||
                from: sender,
 | 
			
		||||
            });
 | 
			
		||||
            await web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            return expect(
 | 
			
		||||
                exchangeTransferSimulator.transferFromAsync(
 | 
			
		||||
                    exampleAssetData,
 | 
			
		||||
@@ -129,12 +121,12 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
            txHash = await dummyERC20Token.transfer.sendTransactionAsync(sender, transferAmount, {
 | 
			
		||||
                from: coinbase,
 | 
			
		||||
            });
 | 
			
		||||
            await web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
 | 
			
		||||
            txHash = await dummyERC20Token.approve.sendTransactionAsync(erc20ProxyAddress, transferAmount, {
 | 
			
		||||
                from: sender,
 | 
			
		||||
            });
 | 
			
		||||
            await web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
 | 
			
		||||
            await exchangeTransferSimulator.transferFromAsync(
 | 
			
		||||
                exampleAssetData,
 | 
			
		||||
@@ -156,7 +148,7 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
            txHash = await dummyERC20Token.transfer.sendTransactionAsync(sender, transferAmount, {
 | 
			
		||||
                from: coinbase,
 | 
			
		||||
            });
 | 
			
		||||
            await web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            txHash = await dummyERC20Token.approve.sendTransactionAsync(
 | 
			
		||||
                erc20ProxyAddress,
 | 
			
		||||
                constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
 | 
			
		||||
@@ -164,7 +156,7 @@ describe('ExchangeTransferSimulator', async () => {
 | 
			
		||||
                    from: sender,
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
            await web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash);
 | 
			
		||||
            await exchangeTransferSimulator.transferFromAsync(
 | 
			
		||||
                exampleAssetData,
 | 
			
		||||
                sender,
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
import { SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -13,10 +12,4 @@ export abstract class AbstractOrderFilledCancelledFetcher {
 | 
			
		||||
     * @return FilledTakerAmount
 | 
			
		||||
     */
 | 
			
		||||
    public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>;
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether an order is cancelled
 | 
			
		||||
     * @param orderHash OrderHash of order we are interested in
 | 
			
		||||
     * @return Whether or not the order is cancelled
 | 
			
		||||
     */
 | 
			
		||||
    public abstract async isOrderCancelledAsync(signedOrder: SignedOrder): Promise<boolean>;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
import { SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
export abstract class AbstractOrderFilledCancelledLazyStore {
 | 
			
		||||
    public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>;
 | 
			
		||||
    public abstract async getIsCancelledAsync(signedOrder: SignedOrder): Promise<boolean>;
 | 
			
		||||
    public abstract setFilledTakerAmount(orderHash: string, balance: BigNumber): void;
 | 
			
		||||
    public abstract deleteFilledTakerAmount(orderHash: string): void;
 | 
			
		||||
    public abstract setIsCancelled(orderHash: string, isCancelled: boolean): void;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { DevUtilsContract } from '@0x/abi-gen-wrappers';
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import { constants } from '@0x/contracts-test-utils';
 | 
			
		||||
import { SupportedProvider, TradeSide, TransferType } from '@0x/order-utils';
 | 
			
		||||
import { AssetProxyId, ExchangeContractErrs } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
import { assetDataUtils } from './asset_data_utils';
 | 
			
		||||
import { constants } from './constants';
 | 
			
		||||
import { TradeSide, TransferType } from './types';
 | 
			
		||||
 | 
			
		||||
enum FailureReason {
 | 
			
		||||
    Balance = 'balance',
 | 
			
		||||
@@ -40,6 +40,7 @@ const ERR_MSG_MAPPING = {
 | 
			
		||||
 */
 | 
			
		||||
export class ExchangeTransferSimulator {
 | 
			
		||||
    private readonly _store: AbstractBalanceAndProxyAllowanceLazyStore;
 | 
			
		||||
    private readonly _devUtils: DevUtilsContract;
 | 
			
		||||
    private static _throwValidationError(
 | 
			
		||||
        failureReason: FailureReason,
 | 
			
		||||
        tradeSide: TradeSide,
 | 
			
		||||
@@ -53,8 +54,9 @@ export class ExchangeTransferSimulator {
 | 
			
		||||
     * @param store A class that implements AbstractBalanceAndProxyAllowanceLazyStore
 | 
			
		||||
     * @return an instance of ExchangeTransferSimulator
 | 
			
		||||
     */
 | 
			
		||||
    constructor(store: AbstractBalanceAndProxyAllowanceLazyStore) {
 | 
			
		||||
    constructor(store: AbstractBalanceAndProxyAllowanceLazyStore, provider: SupportedProvider) {
 | 
			
		||||
        this._store = store;
 | 
			
		||||
        this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Simulates transferFrom call performed by a proxy
 | 
			
		||||
@@ -75,7 +77,7 @@ export class ExchangeTransferSimulator {
 | 
			
		||||
        tradeSide: TradeSide,
 | 
			
		||||
        transferType: TransferType,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
 | 
			
		||||
        const assetProxyId = await this._devUtils.decodeAssetProxyId.callAsync(assetData);
 | 
			
		||||
        switch (assetProxyId) {
 | 
			
		||||
            case AssetProxyId.ERC1155:
 | 
			
		||||
            case AssetProxyId.ERC20:
 | 
			
		||||
@@ -108,11 +110,11 @@ export class ExchangeTransferSimulator {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.MultiAsset: {
 | 
			
		||||
                const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData);
 | 
			
		||||
                const decodedAssetData = await this._devUtils.decodeMultiAssetData.callAsync(assetData);
 | 
			
		||||
                await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits);
 | 
			
		||||
                await this._increaseBalanceAsync(assetData, to, amountInBaseUnits);
 | 
			
		||||
                for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
 | 
			
		||||
                    const amountsElement = decodedAssetData.amounts[index];
 | 
			
		||||
                for (const [index, nestedAssetDataElement] of decodedAssetData[2].entries()) {
 | 
			
		||||
                    const amountsElement = decodedAssetData[1][index];
 | 
			
		||||
                    const totalAmount = amountInBaseUnits.times(amountsElement);
 | 
			
		||||
                    await this.transferFromAsync(
 | 
			
		||||
                        nestedAssetDataElement,
 | 
			
		||||
@@ -7,7 +7,7 @@ import {
 | 
			
		||||
} from '@0x/contracts-asset-proxy';
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import { constants, expect, LogDecoder, orderUtils, signingUtils } from '@0x/contracts-test-utils';
 | 
			
		||||
import { BalanceAndProxyAllowanceLazyStore, ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
 | 
			
		||||
import { ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
 | 
			
		||||
import { FillResults, Order, SignatureType, SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber, errorUtils, providerUtils, RevertError, StringRevertError } from '@0x/utils';
 | 
			
		||||
import { SupportedProvider, Web3Wrapper } from '@0x/web3-wrapper';
 | 
			
		||||
@@ -35,6 +35,7 @@ import {
 | 
			
		||||
import { FillOrderError, FillOrderSimulator } from './fill_order_simulator';
 | 
			
		||||
import { OrderFactoryFromScenario } from './order_factory_from_scenario';
 | 
			
		||||
import { SimpleAssetBalanceAndProxyAllowanceFetcher } from './simple_asset_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
 | 
			
		||||
const EMPTY_FILL_RESULTS = {
 | 
			
		||||
    takerAssetFilledAmount: constants.ZERO_AMOUNT,
 | 
			
		||||
@@ -503,7 +504,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        takerAssetFillAmount: BigNumber,
 | 
			
		||||
        lazyStore: BalanceAndProxyAllowanceLazyStore,
 | 
			
		||||
    ): Promise<FillResults> {
 | 
			
		||||
        const simulator = new FillOrderSimulator(lazyStore);
 | 
			
		||||
        const simulator = new FillOrderSimulator(lazyStore, this.provider);
 | 
			
		||||
        return simulator.simulateFillOrderAsync(signedOrder, this.takerAddress, takerAssetFillAmount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
import { constants, orderUtils } from '@0x/contracts-test-utils';
 | 
			
		||||
import {
 | 
			
		||||
    AbstractBalanceAndProxyAllowanceLazyStore as LazyStore,
 | 
			
		||||
    ExchangeTransferSimulator,
 | 
			
		||||
    Order,
 | 
			
		||||
    SupportedProvider,
 | 
			
		||||
    TradeSide,
 | 
			
		||||
    TransferType,
 | 
			
		||||
} from '@0x/order-utils';
 | 
			
		||||
@@ -10,6 +9,11 @@ import { FillResults } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    AbstractBalanceAndProxyAllowanceLazyStore as LazyStore,
 | 
			
		||||
} from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
import { ExchangeTransferSimulator } from './exchange_transfer_simulator';
 | 
			
		||||
 | 
			
		||||
export enum FillOrderError {
 | 
			
		||||
    OrderUnfillable = 'ORDER_UNFILLABLE',
 | 
			
		||||
    InvalidSender = 'INVALID_SENDER',
 | 
			
		||||
@@ -27,9 +31,9 @@ export class FillOrderSimulator {
 | 
			
		||||
    public readonly lazyStore: LazyStore;
 | 
			
		||||
    private readonly _transferSimulator: ExchangeTransferSimulator;
 | 
			
		||||
 | 
			
		||||
    constructor(lazyStore: LazyStore) {
 | 
			
		||||
    constructor(lazyStore: LazyStore, provider: SupportedProvider) {
 | 
			
		||||
        this.lazyStore = lazyStore;
 | 
			
		||||
        this._transferSimulator = new ExchangeTransferSimulator(lazyStore);
 | 
			
		||||
        this._transferSimulator = new ExchangeTransferSimulator(lazyStore, provider);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async simulateFillOrderAsync(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceFetcher } from '@0x/order-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { AssetWrapper } from './asset_wrapper';
 | 
			
		||||
 | 
			
		||||
export class SimpleAssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import { ERC20TokenContract } from '@0x/abi-gen-wrappers';
 | 
			
		||||
import { ERC20TokenContract } from '@0x/contracts-erc20';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceFetcher } from '../../src/abstract/abstract_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
 | 
			
		||||
export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
 | 
			
		||||
    private readonly _erc20TokenContract: ERC20TokenContract;
 | 
			
		||||
@@ -1,10 +1,8 @@
 | 
			
		||||
import { SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { AbstractOrderFilledCancelledFetcher } from '../abstract/abstract_order_filled_cancelled_fetcher';
 | 
			
		||||
import { AbstractOrderFilledCancelledLazyStore } from '../abstract/abstract_order_filled_cancelled_lazy_store';
 | 
			
		||||
import { orderHashUtils } from '../order_hash';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Copy on read store for balances/proxyAllowances of tokens/accounts
 | 
			
		||||
@@ -55,20 +53,6 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
 | 
			
		||||
    public deleteFilledTakerAmount(orderHash: string): void {
 | 
			
		||||
        delete this._filledTakerAmount[orderHash];
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if an order has been cancelled
 | 
			
		||||
     * @param orderHash OrderHash from order of interest
 | 
			
		||||
     * @return Whether the order has been cancelled
 | 
			
		||||
     */
 | 
			
		||||
    public async getIsCancelledAsync(signedOrder: SignedOrder): Promise<boolean> {
 | 
			
		||||
        const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
        if (this._isCancelled[orderHash] === undefined) {
 | 
			
		||||
            const isCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(signedOrder);
 | 
			
		||||
            this.setIsCancelled(orderHash, isCancelled);
 | 
			
		||||
        }
 | 
			
		||||
        const cachedIsCancelled = this._isCancelled[orderHash]; // tslint:disable-line:boolean-naming
 | 
			
		||||
        return cachedIsCancelled;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Set whether an order has been cancelled or not
 | 
			
		||||
     * @param orderHash OrderHash from order of interest
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -15,17 +15,6 @@ export { sortingUtils } from './sorting_utils';
 | 
			
		||||
export { orderParsingUtils } from './parsing_utils';
 | 
			
		||||
export { orderCalculationUtils } from './order_calculation_utils';
 | 
			
		||||
 | 
			
		||||
export { OrderStateUtils } from './order_state_utils';
 | 
			
		||||
export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
export { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
 | 
			
		||||
export { AbstractOrderFilledCancelledLazyStore } from './abstract/abstract_order_filled_cancelled_lazy_store';
 | 
			
		||||
 | 
			
		||||
export { OrderValidationUtils } from './order_validation_utils';
 | 
			
		||||
export { ExchangeTransferSimulator } from './exchange_transfer_simulator';
 | 
			
		||||
export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
 | 
			
		||||
export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store';
 | 
			
		||||
 | 
			
		||||
export { eip712Utils } from './eip712_utils';
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,363 +0,0 @@
 | 
			
		||||
import {
 | 
			
		||||
    AssetProxyId,
 | 
			
		||||
    ERC20AssetData,
 | 
			
		||||
    ERC721AssetData,
 | 
			
		||||
    ExchangeContractErrs,
 | 
			
		||||
    MultiAssetData,
 | 
			
		||||
    ObjectMap,
 | 
			
		||||
    OrderRelevantState,
 | 
			
		||||
    OrderState,
 | 
			
		||||
    OrderStateInvalid,
 | 
			
		||||
    OrderStateValid,
 | 
			
		||||
    SignedOrder,
 | 
			
		||||
    SingleAssetData,
 | 
			
		||||
} from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
 | 
			
		||||
import { assetDataUtils } from './asset_data_utils';
 | 
			
		||||
import { orderHashUtils } from './order_hash';
 | 
			
		||||
import { OrderValidationUtils } from './order_validation_utils';
 | 
			
		||||
import { RemainingFillableCalculator } from './remaining_fillable_calculator';
 | 
			
		||||
import { utils } from './utils';
 | 
			
		||||
 | 
			
		||||
interface SidedOrderRelevantState {
 | 
			
		||||
    isMakerSide: boolean;
 | 
			
		||||
    traderBalance: BigNumber;
 | 
			
		||||
    traderIndividualBalances: ObjectMap<BigNumber>;
 | 
			
		||||
    traderProxyAllowance: BigNumber;
 | 
			
		||||
    traderIndividualProxyAllowances: ObjectMap<BigNumber>;
 | 
			
		||||
    traderFeeBalance: BigNumber;
 | 
			
		||||
    traderFeeProxyAllowance: BigNumber;
 | 
			
		||||
    filledTakerAssetAmount: BigNumber;
 | 
			
		||||
    remainingFillableAssetAmount: BigNumber;
 | 
			
		||||
    isOrderCancelled: boolean;
 | 
			
		||||
}
 | 
			
		||||
interface OrderValidResult {
 | 
			
		||||
    isValid: true;
 | 
			
		||||
}
 | 
			
		||||
interface OrderInvalidResult {
 | 
			
		||||
    isValid: false;
 | 
			
		||||
    error: ExchangeContractErrs;
 | 
			
		||||
}
 | 
			
		||||
type OrderValidationResult = OrderValidResult | OrderInvalidResult;
 | 
			
		||||
 | 
			
		||||
export class OrderStateUtils {
 | 
			
		||||
    private readonly _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher;
 | 
			
		||||
    private readonly _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
 | 
			
		||||
    private static _validateIfOrderIsValid(
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        sidedOrderRelevantState: SidedOrderRelevantState,
 | 
			
		||||
    ): OrderValidationResult {
 | 
			
		||||
        const isMakerSide = sidedOrderRelevantState.isMakerSide;
 | 
			
		||||
        if (sidedOrderRelevantState.isOrderCancelled) {
 | 
			
		||||
            return { isValid: false, error: ExchangeContractErrs.OrderCancelled };
 | 
			
		||||
        }
 | 
			
		||||
        const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus(
 | 
			
		||||
            sidedOrderRelevantState.filledTakerAssetAmount,
 | 
			
		||||
        );
 | 
			
		||||
        if (availableTakerAssetAmount.eq(0)) {
 | 
			
		||||
            return { isValid: false, error: ExchangeContractErrs.OrderRemainingFillAmountZero };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sidedOrderRelevantState.traderBalance.eq(0)) {
 | 
			
		||||
            const error = isMakerSide
 | 
			
		||||
                ? ExchangeContractErrs.InsufficientMakerBalance
 | 
			
		||||
                : ExchangeContractErrs.InsufficientTakerBalance;
 | 
			
		||||
            return { isValid: false, error };
 | 
			
		||||
        }
 | 
			
		||||
        if (sidedOrderRelevantState.traderProxyAllowance.eq(0)) {
 | 
			
		||||
            const error = isMakerSide
 | 
			
		||||
                ? ExchangeContractErrs.InsufficientMakerAllowance
 | 
			
		||||
                : ExchangeContractErrs.InsufficientTakerAllowance;
 | 
			
		||||
            return { isValid: false, error };
 | 
			
		||||
        }
 | 
			
		||||
        if (!signedOrder.makerFee.eq(0)) {
 | 
			
		||||
            if (sidedOrderRelevantState.traderFeeBalance.eq(0)) {
 | 
			
		||||
                const error = isMakerSide
 | 
			
		||||
                    ? ExchangeContractErrs.InsufficientMakerFeeBalance
 | 
			
		||||
                    : ExchangeContractErrs.InsufficientTakerFeeBalance;
 | 
			
		||||
                return { isValid: false, error };
 | 
			
		||||
            }
 | 
			
		||||
            if (sidedOrderRelevantState.traderFeeProxyAllowance.eq(0)) {
 | 
			
		||||
                const error = isMakerSide
 | 
			
		||||
                    ? ExchangeContractErrs.InsufficientMakerFeeAllowance
 | 
			
		||||
                    : ExchangeContractErrs.InsufficientTakerFeeAllowance;
 | 
			
		||||
                return { isValid: false, error };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus(
 | 
			
		||||
            sidedOrderRelevantState.filledTakerAssetAmount,
 | 
			
		||||
        );
 | 
			
		||||
        const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(
 | 
			
		||||
            remainingTakerAssetAmount,
 | 
			
		||||
            signedOrder.takerAssetAmount,
 | 
			
		||||
            signedOrder.makerAssetAmount,
 | 
			
		||||
        );
 | 
			
		||||
        if (isRoundingError) {
 | 
			
		||||
            return { isValid: false, error: ExchangeContractErrs.OrderFillRoundingError };
 | 
			
		||||
        }
 | 
			
		||||
        return { isValid: true };
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Instantiate OrderStateUtils
 | 
			
		||||
     * @param balanceAndProxyAllowanceFetcher A class that is capable of fetching balances
 | 
			
		||||
     * and proxyAllowances for Ethereum addresses. It must implement AbstractBalanceAndProxyAllowanceFetcher
 | 
			
		||||
     * @param orderFilledCancelledFetcher A class that is capable of fetching whether an order
 | 
			
		||||
     * is cancelled and how much of it has been filled. It must implement AbstractOrderFilledCancelledFetcher
 | 
			
		||||
     * @return Instance of OrderStateUtils
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher,
 | 
			
		||||
        orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher,
 | 
			
		||||
    ) {
 | 
			
		||||
        this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
 | 
			
		||||
        this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the orderState for an "open" order (i.e where takerAddress=NULL_ADDRESS)
 | 
			
		||||
     * This method will only check the maker's balance/allowance to calculate the
 | 
			
		||||
     * OrderState.
 | 
			
		||||
     * @param signedOrder The order of interest
 | 
			
		||||
     * @return State relevant to the signedOrder, as well as whether the signedOrder is "valid".
 | 
			
		||||
     * Validity is defined as a non-zero amount of the order can still be filled.
 | 
			
		||||
     */
 | 
			
		||||
    public async getOpenOrderStateAsync(signedOrder: SignedOrder, transactionHash?: string): Promise<OrderState> {
 | 
			
		||||
        const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder);
 | 
			
		||||
        const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
        const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(signedOrder);
 | 
			
		||||
        const sidedOrderRelevantState = {
 | 
			
		||||
            isMakerSide: true,
 | 
			
		||||
            traderBalance: orderRelevantState.makerBalance,
 | 
			
		||||
            traderIndividualBalances: orderRelevantState.makerIndividualBalances,
 | 
			
		||||
            traderProxyAllowance: orderRelevantState.makerProxyAllowance,
 | 
			
		||||
            traderIndividualProxyAllowances: orderRelevantState.makerIndividualProxyAllowances,
 | 
			
		||||
            traderFeeBalance: orderRelevantState.makerFeeBalance,
 | 
			
		||||
            traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance,
 | 
			
		||||
            filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount,
 | 
			
		||||
            remainingFillableAssetAmount: orderRelevantState.remainingFillableMakerAssetAmount,
 | 
			
		||||
            isOrderCancelled,
 | 
			
		||||
        };
 | 
			
		||||
        const orderValidationResult = OrderStateUtils._validateIfOrderIsValid(signedOrder, sidedOrderRelevantState);
 | 
			
		||||
        if (orderValidationResult.isValid) {
 | 
			
		||||
            const orderState: OrderStateValid = {
 | 
			
		||||
                isValid: true,
 | 
			
		||||
                orderHash,
 | 
			
		||||
                orderRelevantState,
 | 
			
		||||
                transactionHash,
 | 
			
		||||
            };
 | 
			
		||||
            return orderState;
 | 
			
		||||
        } else {
 | 
			
		||||
            const orderState: OrderStateInvalid = {
 | 
			
		||||
                isValid: false,
 | 
			
		||||
                orderHash,
 | 
			
		||||
                error: orderValidationResult.error,
 | 
			
		||||
                transactionHash,
 | 
			
		||||
            };
 | 
			
		||||
            return orderState;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Get state relevant to an order (i.e makerBalance, makerAllowance, filledTakerAssetAmount, etc...
 | 
			
		||||
     * @param signedOrder Order of interest
 | 
			
		||||
     * @return An instance of OrderRelevantState
 | 
			
		||||
     */
 | 
			
		||||
    public async getOpenOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
 | 
			
		||||
        const isMaker = true;
 | 
			
		||||
        const sidedOrderRelevantState = await this._getSidedOrderRelevantStateAsync(
 | 
			
		||||
            isMaker,
 | 
			
		||||
            signedOrder,
 | 
			
		||||
            signedOrder.takerAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const remainingFillableTakerAssetAmount = sidedOrderRelevantState.remainingFillableAssetAmount
 | 
			
		||||
            .times(signedOrder.takerAssetAmount)
 | 
			
		||||
            .dividedToIntegerBy(signedOrder.makerAssetAmount);
 | 
			
		||||
 | 
			
		||||
        const orderRelevantState = {
 | 
			
		||||
            makerBalance: sidedOrderRelevantState.traderBalance,
 | 
			
		||||
            makerIndividualBalances: sidedOrderRelevantState.traderIndividualBalances,
 | 
			
		||||
            makerProxyAllowance: sidedOrderRelevantState.traderProxyAllowance,
 | 
			
		||||
            makerIndividualProxyAllowances: sidedOrderRelevantState.traderIndividualProxyAllowances,
 | 
			
		||||
            makerFeeBalance: sidedOrderRelevantState.traderFeeBalance,
 | 
			
		||||
            makerFeeProxyAllowance: sidedOrderRelevantState.traderFeeProxyAllowance,
 | 
			
		||||
            filledTakerAssetAmount: sidedOrderRelevantState.filledTakerAssetAmount,
 | 
			
		||||
            remainingFillableMakerAssetAmount: sidedOrderRelevantState.remainingFillableAssetAmount,
 | 
			
		||||
            remainingFillableTakerAssetAmount,
 | 
			
		||||
        };
 | 
			
		||||
        return orderRelevantState;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the max amount of the supplied order's takerAmount that could still be filled
 | 
			
		||||
     * @param signedOrder Order of interest
 | 
			
		||||
     * @param takerAddress Hypothetical taker of the order
 | 
			
		||||
     * @return fillableTakerAssetAmount
 | 
			
		||||
     */
 | 
			
		||||
    public async getMaxFillableTakerAssetAmountAsync(
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        takerAddress: string,
 | 
			
		||||
    ): Promise<BigNumber> {
 | 
			
		||||
        // Get max fillable amount for an order, considering the makers ability to fill
 | 
			
		||||
        let isMaker = true;
 | 
			
		||||
        const orderRelevantMakerState = await this._getSidedOrderRelevantStateAsync(
 | 
			
		||||
            isMaker,
 | 
			
		||||
            signedOrder,
 | 
			
		||||
            signedOrder.takerAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0)
 | 
			
		||||
            ? new BigNumber(0)
 | 
			
		||||
            : utils.getPartialAmountFloor(
 | 
			
		||||
                  orderRelevantMakerState.remainingFillableAssetAmount,
 | 
			
		||||
                  signedOrder.makerAssetAmount,
 | 
			
		||||
                  signedOrder.takerAssetAmount,
 | 
			
		||||
              );
 | 
			
		||||
 | 
			
		||||
        // Get max fillable amount for an order, considering the takers ability to fill
 | 
			
		||||
        isMaker = false;
 | 
			
		||||
        const orderRelevantTakerState = await this._getSidedOrderRelevantStateAsync(isMaker, signedOrder, takerAddress);
 | 
			
		||||
        const remainingFillableTakerAssetAmountGivenTakersStatus = orderRelevantTakerState.remainingFillableAssetAmount;
 | 
			
		||||
 | 
			
		||||
        // The min of these two in the actualy max fillable by either party
 | 
			
		||||
        const fillableTakerAssetAmount = BigNumber.min(
 | 
			
		||||
            remainingFillableTakerAssetAmountGivenMakersStatus,
 | 
			
		||||
            remainingFillableTakerAssetAmountGivenTakersStatus,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return fillableTakerAssetAmount;
 | 
			
		||||
    }
 | 
			
		||||
    private async _getSidedOrderRelevantStateAsync(
 | 
			
		||||
        isMakerSide: boolean,
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        takerAddress: string,
 | 
			
		||||
    ): Promise<SidedOrderRelevantState> {
 | 
			
		||||
        let traderAddress;
 | 
			
		||||
        let assetData;
 | 
			
		||||
        let assetAmount;
 | 
			
		||||
        let feeAssetData;
 | 
			
		||||
        let feeAmount;
 | 
			
		||||
        if (isMakerSide) {
 | 
			
		||||
            traderAddress = signedOrder.makerAddress;
 | 
			
		||||
            assetData = signedOrder.makerAssetData;
 | 
			
		||||
            assetAmount = signedOrder.makerAssetAmount;
 | 
			
		||||
            feeAssetData = signedOrder.makerFeeAssetData;
 | 
			
		||||
            feeAmount = signedOrder.makerFee;
 | 
			
		||||
        } else {
 | 
			
		||||
            traderAddress = takerAddress;
 | 
			
		||||
            assetData = signedOrder.takerAssetData;
 | 
			
		||||
            assetAmount = signedOrder.takerAssetAmount;
 | 
			
		||||
            feeAssetData = signedOrder.takerFeeAssetData;
 | 
			
		||||
            feeAmount = signedOrder.takerFee;
 | 
			
		||||
        }
 | 
			
		||||
        const isPercentageFee = assetData === feeAssetData;
 | 
			
		||||
 | 
			
		||||
        const traderBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress);
 | 
			
		||||
        const traderIndividualBalances = await this._getAssetBalancesAsync(assetData, traderAddress);
 | 
			
		||||
        const traderProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
 | 
			
		||||
            assetData,
 | 
			
		||||
            traderAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const traderIndividualProxyAllowances = await this._getAssetProxyAllowancesAsync(assetData, traderAddress);
 | 
			
		||||
        const traderFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
 | 
			
		||||
            feeAssetData,
 | 
			
		||||
            traderAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const traderFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
 | 
			
		||||
            feeAssetData,
 | 
			
		||||
            traderAddress,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const transferrableTraderAssetAmount = BigNumber.min(traderProxyAllowance, traderBalance);
 | 
			
		||||
        const transferrableFeeAssetAmount = BigNumber.min(traderFeeProxyAllowance, traderFeeBalance);
 | 
			
		||||
 | 
			
		||||
        const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
        const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
 | 
			
		||||
        const totalMakerAssetAmount = signedOrder.makerAssetAmount;
 | 
			
		||||
        const totalTakerAssetAmount = signedOrder.takerAssetAmount;
 | 
			
		||||
        const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(signedOrder);
 | 
			
		||||
        const remainingTakerAssetAmount = isOrderCancelled
 | 
			
		||||
            ? new BigNumber(0)
 | 
			
		||||
            : totalTakerAssetAmount.minus(filledTakerAssetAmount);
 | 
			
		||||
        const remainingMakerAssetAmount = remainingTakerAssetAmount.eq(0)
 | 
			
		||||
            ? new BigNumber(0)
 | 
			
		||||
            : remainingTakerAssetAmount.times(totalMakerAssetAmount).dividedToIntegerBy(totalTakerAssetAmount);
 | 
			
		||||
        const remainingAssetAmount = isMakerSide ? remainingMakerAssetAmount : remainingTakerAssetAmount;
 | 
			
		||||
 | 
			
		||||
        const remainingFillableCalculator = new RemainingFillableCalculator(
 | 
			
		||||
            feeAmount,
 | 
			
		||||
            assetAmount,
 | 
			
		||||
            isPercentageFee,
 | 
			
		||||
            transferrableTraderAssetAmount,
 | 
			
		||||
            transferrableFeeAssetAmount,
 | 
			
		||||
            remainingAssetAmount,
 | 
			
		||||
        );
 | 
			
		||||
        const remainingFillableAssetAmount = remainingFillableCalculator.computeRemainingFillable();
 | 
			
		||||
 | 
			
		||||
        const sidedOrderRelevantState = {
 | 
			
		||||
            isMakerSide,
 | 
			
		||||
            traderBalance,
 | 
			
		||||
            traderIndividualBalances,
 | 
			
		||||
            traderProxyAllowance,
 | 
			
		||||
            traderIndividualProxyAllowances,
 | 
			
		||||
            traderFeeBalance,
 | 
			
		||||
            traderFeeProxyAllowance,
 | 
			
		||||
            filledTakerAssetAmount,
 | 
			
		||||
            remainingFillableAssetAmount,
 | 
			
		||||
            isOrderCancelled,
 | 
			
		||||
        };
 | 
			
		||||
        return sidedOrderRelevantState;
 | 
			
		||||
    }
 | 
			
		||||
    private async _getAssetBalancesAsync(
 | 
			
		||||
        assetData: string,
 | 
			
		||||
        traderAddress: string,
 | 
			
		||||
        initialBalances: ObjectMap<BigNumber> = {},
 | 
			
		||||
    ): Promise<ObjectMap<BigNumber>> {
 | 
			
		||||
        const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
 | 
			
		||||
        let balances: ObjectMap<BigNumber> = { ...initialBalances };
 | 
			
		||||
        if (isERC20AssetData(decodedAssetData) || isERC721AssetData(decodedAssetData)) {
 | 
			
		||||
            const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress);
 | 
			
		||||
            // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
            const tokenAddress = (decodedAssetData as ERC20AssetData | ERC721AssetData).tokenAddress;
 | 
			
		||||
            balances[tokenAddress] =
 | 
			
		||||
                initialBalances[tokenAddress] === undefined ? balance : balances[tokenAddress].plus(balance);
 | 
			
		||||
        } else if (isMultiAssetData(decodedAssetData)) {
 | 
			
		||||
            for (const assetDataElement of (decodedAssetData as MultiAssetData).nestedAssetData) {
 | 
			
		||||
                balances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, balances);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return balances;
 | 
			
		||||
    }
 | 
			
		||||
    private async _getAssetProxyAllowancesAsync(
 | 
			
		||||
        assetData: string,
 | 
			
		||||
        traderAddress: string,
 | 
			
		||||
        initialAllowances: ObjectMap<BigNumber> = {},
 | 
			
		||||
    ): Promise<ObjectMap<BigNumber>> {
 | 
			
		||||
        const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
 | 
			
		||||
        let allowances: ObjectMap<BigNumber> = { ...initialAllowances };
 | 
			
		||||
        if (isERC20AssetData(decodedAssetData) || isERC721AssetData(decodedAssetData)) {
 | 
			
		||||
            const allowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
 | 
			
		||||
                assetData,
 | 
			
		||||
                traderAddress,
 | 
			
		||||
            );
 | 
			
		||||
            // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
            const tokenAddress = (decodedAssetData as ERC20AssetData | ERC721AssetData).tokenAddress;
 | 
			
		||||
            allowances[tokenAddress] =
 | 
			
		||||
                initialAllowances[tokenAddress] === undefined ? allowance : allowances[tokenAddress].plus(allowance);
 | 
			
		||||
        } else if (isMultiAssetData(decodedAssetData)) {
 | 
			
		||||
            for (const assetDataElement of (decodedAssetData as MultiAssetData).nestedAssetData) {
 | 
			
		||||
                allowances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, allowances);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return allowances;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isERC20AssetData(decodedAssetData: SingleAssetData | MultiAssetData): boolean {
 | 
			
		||||
    return decodedAssetData.assetProxyId === AssetProxyId.ERC20;
 | 
			
		||||
}
 | 
			
		||||
function isERC721AssetData(decodedAssetData: SingleAssetData | MultiAssetData): boolean {
 | 
			
		||||
    return decodedAssetData.assetProxyId === AssetProxyId.ERC721;
 | 
			
		||||
}
 | 
			
		||||
function isMultiAssetData(decodedAssetData: SingleAssetData | MultiAssetData): boolean {
 | 
			
		||||
    return decodedAssetData.assetProxyId === AssetProxyId.MultiAsset;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,204 +0,0 @@
 | 
			
		||||
import { ExchangeContractErrs, RevertReason, SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber, providerUtils } from '@0x/utils';
 | 
			
		||||
import { SupportedProvider, ZeroExProvider } from 'ethereum-types';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
 | 
			
		||||
import { constants } from './constants';
 | 
			
		||||
import { ExchangeTransferSimulator } from './exchange_transfer_simulator';
 | 
			
		||||
import { orderHashUtils } from './order_hash';
 | 
			
		||||
import { signatureUtils } from './signature_utils';
 | 
			
		||||
import { TradeSide, TransferType, TypedDataError } from './types';
 | 
			
		||||
import { utils } from './utils';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A utility class for validating orders
 | 
			
		||||
 */
 | 
			
		||||
export class OrderValidationUtils {
 | 
			
		||||
    private readonly _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
 | 
			
		||||
    private readonly _provider: ZeroExProvider;
 | 
			
		||||
    /**
 | 
			
		||||
     * A TypeScript implementation mirroring the implementation of isRoundingError in the
 | 
			
		||||
     * Exchange smart contract
 | 
			
		||||
     * @param numerator Numerator value. When used to check an order, pass in `takerAssetFilledAmount`
 | 
			
		||||
     * @param denominator Denominator value.  When used to check an order, pass in `order.takerAssetAmount`
 | 
			
		||||
     * @param target Target value. When used to check an order, pass in `order.makerAssetAmount`
 | 
			
		||||
     */
 | 
			
		||||
    public static isRoundingErrorFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean {
 | 
			
		||||
        // Solidity's mulmod() in JS
 | 
			
		||||
        // Source: https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#mathematical-and-cryptographic-functions
 | 
			
		||||
        if (denominator.eq(0)) {
 | 
			
		||||
            throw new Error('denominator cannot be 0');
 | 
			
		||||
        }
 | 
			
		||||
        const remainder = target.multipliedBy(numerator).mod(denominator);
 | 
			
		||||
        if (remainder.eq(0)) {
 | 
			
		||||
            return false; // no rounding error
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // tslint:disable-next-line:custom-no-magic-numbers
 | 
			
		||||
        const errPercentageTimes1000000 = remainder.multipliedBy(1000000).div(numerator.multipliedBy(target));
 | 
			
		||||
        // tslint:disable-next-line:custom-no-magic-numbers
 | 
			
		||||
        const isError = errPercentageTimes1000000.gt(1000);
 | 
			
		||||
        return isError;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate that the maker & taker have sufficient balances/allowances
 | 
			
		||||
     * to fill the supplied order to the fillTakerAssetAmount amount
 | 
			
		||||
     * @param exchangeTradeEmulator ExchangeTradeEmulator to use
 | 
			
		||||
     * @param signedOrder SignedOrder to test
 | 
			
		||||
     * @param fillTakerAssetAmount Amount of takerAsset to fill the signedOrder
 | 
			
		||||
     * @param senderAddress Sender of the fillOrder tx
 | 
			
		||||
     */
 | 
			
		||||
    public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
 | 
			
		||||
        exchangeTradeEmulator: ExchangeTransferSimulator,
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        fillTakerAssetAmount: BigNumber,
 | 
			
		||||
        senderAddress: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const fillMakerTokenAmount = utils.getPartialAmountFloor(
 | 
			
		||||
            fillTakerAssetAmount,
 | 
			
		||||
            signedOrder.takerAssetAmount,
 | 
			
		||||
            signedOrder.makerAssetAmount,
 | 
			
		||||
        );
 | 
			
		||||
        const makerFeeAmount = utils.getPartialAmountFloor(
 | 
			
		||||
            fillTakerAssetAmount,
 | 
			
		||||
            signedOrder.takerAssetAmount,
 | 
			
		||||
            signedOrder.makerFee,
 | 
			
		||||
        );
 | 
			
		||||
        const takerFeeAmount = utils.getPartialAmountFloor(
 | 
			
		||||
            fillTakerAssetAmount,
 | 
			
		||||
            signedOrder.takerAssetAmount,
 | 
			
		||||
            signedOrder.takerFee,
 | 
			
		||||
        );
 | 
			
		||||
        await exchangeTradeEmulator.transferFromAsync(
 | 
			
		||||
            signedOrder.makerAssetData,
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            senderAddress,
 | 
			
		||||
            fillMakerTokenAmount,
 | 
			
		||||
            TradeSide.Maker,
 | 
			
		||||
            TransferType.Trade,
 | 
			
		||||
        );
 | 
			
		||||
        await exchangeTradeEmulator.transferFromAsync(
 | 
			
		||||
            signedOrder.takerAssetData,
 | 
			
		||||
            senderAddress,
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            fillTakerAssetAmount,
 | 
			
		||||
            TradeSide.Taker,
 | 
			
		||||
            TransferType.Trade,
 | 
			
		||||
        );
 | 
			
		||||
        await exchangeTradeEmulator.transferFromAsync(
 | 
			
		||||
            signedOrder.makerFeeAssetData,
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            signedOrder.feeRecipientAddress,
 | 
			
		||||
            makerFeeAmount,
 | 
			
		||||
            TradeSide.Maker,
 | 
			
		||||
            TransferType.Fee,
 | 
			
		||||
        );
 | 
			
		||||
        await exchangeTradeEmulator.transferFromAsync(
 | 
			
		||||
            signedOrder.takerFeeAssetData,
 | 
			
		||||
            senderAddress,
 | 
			
		||||
            signedOrder.feeRecipientAddress,
 | 
			
		||||
            takerFeeAmount,
 | 
			
		||||
            TradeSide.Taker,
 | 
			
		||||
            TransferType.Fee,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static _validateOrderNotExpiredOrThrow(expirationTimeSeconds: BigNumber): void {
 | 
			
		||||
        const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
 | 
			
		||||
        if (expirationTimeSeconds.isLessThan(currentUnixTimestampSec)) {
 | 
			
		||||
            throw new Error(RevertReason.OrderUnfillable);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Instantiate OrderValidationUtils
 | 
			
		||||
     * @param orderFilledCancelledFetcher A module that implements the AbstractOrderFilledCancelledFetcher
 | 
			
		||||
     * @param supportedProvider Web3 provider to use for JSON RPC calls
 | 
			
		||||
     * @return An instance of OrderValidationUtils
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher,
 | 
			
		||||
        supportedProvider: SupportedProvider,
 | 
			
		||||
    ) {
 | 
			
		||||
        this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
 | 
			
		||||
        this._provider = providerUtils.standardizeOrThrow(supportedProvider);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate a call to FillOrder and throw if it wouldn't succeed
 | 
			
		||||
     * @param exchangeTradeEmulator ExchangeTradeEmulator to use
 | 
			
		||||
     * @param signedOrder SignedOrder of interest
 | 
			
		||||
     * @param fillTakerAssetAmount Amount we'd like to fill the order for
 | 
			
		||||
     * @param takerAddress The taker of the order
 | 
			
		||||
     */
 | 
			
		||||
    public async validateFillOrderThrowIfInvalidAsync(
 | 
			
		||||
        exchangeTradeEmulator: ExchangeTransferSimulator,
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        fillTakerAssetAmount: BigNumber,
 | 
			
		||||
        takerAddress: string,
 | 
			
		||||
    ): Promise<BigNumber> {
 | 
			
		||||
        OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationTimeSeconds);
 | 
			
		||||
        if (signedOrder.makerAssetAmount.eq(0) || signedOrder.takerAssetAmount.eq(0)) {
 | 
			
		||||
            throw new Error(RevertReason.OrderUnfillable);
 | 
			
		||||
        }
 | 
			
		||||
        if (fillTakerAssetAmount.eq(0)) {
 | 
			
		||||
            throw new Error(RevertReason.InvalidTakerAmount);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
        const isValid = await signatureUtils.isValidSignatureAsync(
 | 
			
		||||
            this._provider,
 | 
			
		||||
            orderHash,
 | 
			
		||||
            signedOrder.signature,
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
        );
 | 
			
		||||
        if (!isValid) {
 | 
			
		||||
            throw new Error(TypedDataError.InvalidSignature);
 | 
			
		||||
        }
 | 
			
		||||
        const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
 | 
			
		||||
        if (signedOrder.takerAssetAmount.eq(filledTakerTokenAmount)) {
 | 
			
		||||
            throw new Error(RevertReason.OrderUnfillable);
 | 
			
		||||
        }
 | 
			
		||||
        if (signedOrder.takerAddress !== constants.NULL_ADDRESS && signedOrder.takerAddress !== takerAddress) {
 | 
			
		||||
            throw new Error(RevertReason.InvalidTaker);
 | 
			
		||||
        }
 | 
			
		||||
        const remainingTakerTokenAmount = signedOrder.takerAssetAmount.minus(filledTakerTokenAmount);
 | 
			
		||||
        const desiredFillTakerTokenAmount = remainingTakerTokenAmount.isLessThan(fillTakerAssetAmount)
 | 
			
		||||
            ? remainingTakerTokenAmount
 | 
			
		||||
            : fillTakerAssetAmount;
 | 
			
		||||
        try {
 | 
			
		||||
            await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
 | 
			
		||||
                exchangeTradeEmulator,
 | 
			
		||||
                signedOrder,
 | 
			
		||||
                desiredFillTakerTokenAmount,
 | 
			
		||||
                takerAddress,
 | 
			
		||||
            );
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
            const transferFailedErrorMessages = [
 | 
			
		||||
                ExchangeContractErrs.InsufficientMakerBalance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientMakerFeeBalance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientTakerBalance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientTakerFeeBalance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientMakerAllowance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientMakerFeeAllowance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientTakerAllowance,
 | 
			
		||||
                ExchangeContractErrs.InsufficientTakerFeeAllowance,
 | 
			
		||||
            ];
 | 
			
		||||
            if (_.includes(transferFailedErrorMessages, err.message)) {
 | 
			
		||||
                throw new Error(RevertReason.TransferFailed);
 | 
			
		||||
            }
 | 
			
		||||
            throw err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingErrorFloor(
 | 
			
		||||
            desiredFillTakerTokenAmount,
 | 
			
		||||
            signedOrder.takerAssetAmount,
 | 
			
		||||
            signedOrder.makerAssetAmount,
 | 
			
		||||
        );
 | 
			
		||||
        if (wouldRoundingErrorOccur) {
 | 
			
		||||
            throw new Error(RevertReason.RoundingError);
 | 
			
		||||
        }
 | 
			
		||||
        return filledTakerTokenAmount;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,142 +0,0 @@
 | 
			
		||||
import { SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as chai from 'chai';
 | 
			
		||||
import 'mocha';
 | 
			
		||||
 | 
			
		||||
import { AbstractBalanceAndProxyAllowanceFetcher } from '../src/abstract/abstract_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { AbstractOrderFilledCancelledFetcher } from '../src/abstract/abstract_order_filled_cancelled_fetcher';
 | 
			
		||||
import { OrderStateUtils } from '../src/order_state_utils';
 | 
			
		||||
 | 
			
		||||
import { chaiSetup } from './utils/chai_setup';
 | 
			
		||||
import { testOrderFactory } from './utils/test_order_factory';
 | 
			
		||||
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
 | 
			
		||||
describe('OrderStateUtils', () => {
 | 
			
		||||
    describe('#getOpenOrderStateAsync', () => {
 | 
			
		||||
        const buildMockBalanceFetcher = (takerBalance: BigNumber): AbstractBalanceAndProxyAllowanceFetcher => {
 | 
			
		||||
            const balanceFetcher = {
 | 
			
		||||
                async getBalanceAsync(_assetData: string, _userAddress: string): Promise<BigNumber> {
 | 
			
		||||
                    return takerBalance;
 | 
			
		||||
                },
 | 
			
		||||
                async getProxyAllowanceAsync(_assetData: string, _userAddress: string): Promise<BigNumber> {
 | 
			
		||||
                    return takerBalance;
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            return balanceFetcher;
 | 
			
		||||
        };
 | 
			
		||||
        const buildMockOrderFilledFetcher = (
 | 
			
		||||
            filledAmount: BigNumber = new BigNumber(0),
 | 
			
		||||
            cancelled: boolean = false,
 | 
			
		||||
        ): AbstractOrderFilledCancelledFetcher => {
 | 
			
		||||
            const orderFetcher = {
 | 
			
		||||
                async getFilledTakerAmountAsync(_orderHash: string): Promise<BigNumber> {
 | 
			
		||||
                    return filledAmount;
 | 
			
		||||
                },
 | 
			
		||||
                async isOrderCancelledAsync(_signedOrder: SignedOrder): Promise<boolean> {
 | 
			
		||||
                    return cancelled;
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            return orderFetcher;
 | 
			
		||||
        };
 | 
			
		||||
        it('should have valid order state if order can be fully filled with small maker amount', async () => {
 | 
			
		||||
            const makerAssetAmount = new BigNumber(10);
 | 
			
		||||
            const takerAssetAmount = new BigNumber(10000000000000000);
 | 
			
		||||
            const takerBalance = takerAssetAmount;
 | 
			
		||||
            const orderFilledAmount = new BigNumber(0);
 | 
			
		||||
            const mockBalanceFetcher = buildMockBalanceFetcher(takerBalance);
 | 
			
		||||
            const mockOrderFilledFetcher = buildMockOrderFilledFetcher(orderFilledAmount);
 | 
			
		||||
            const [signedOrder] = testOrderFactory.generateTestSignedOrders(
 | 
			
		||||
                {
 | 
			
		||||
                    makerAssetAmount,
 | 
			
		||||
                    takerAssetAmount,
 | 
			
		||||
                },
 | 
			
		||||
                1,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const orderStateUtils = new OrderStateUtils(mockBalanceFetcher, mockOrderFilledFetcher);
 | 
			
		||||
            const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder);
 | 
			
		||||
            expect(orderState.isValid).to.eq(true);
 | 
			
		||||
        });
 | 
			
		||||
        it('should be invalid when an order is partially filled where only a rounding error remains', async () => {
 | 
			
		||||
            const makerAssetAmount = new BigNumber(1001);
 | 
			
		||||
            const takerAssetAmount = new BigNumber(3);
 | 
			
		||||
            const takerBalance = takerAssetAmount;
 | 
			
		||||
            const orderFilledAmount = new BigNumber(2);
 | 
			
		||||
            const mockBalanceFetcher = buildMockBalanceFetcher(takerBalance);
 | 
			
		||||
            const mockOrderFilledFetcher = buildMockOrderFilledFetcher(orderFilledAmount);
 | 
			
		||||
            const [signedOrder] = testOrderFactory.generateTestSignedOrders(
 | 
			
		||||
                {
 | 
			
		||||
                    makerAssetAmount,
 | 
			
		||||
                    takerAssetAmount,
 | 
			
		||||
                },
 | 
			
		||||
                1,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const orderStateUtils = new OrderStateUtils(mockBalanceFetcher, mockOrderFilledFetcher);
 | 
			
		||||
            const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder);
 | 
			
		||||
            expect(orderState.isValid).to.eq(false);
 | 
			
		||||
        });
 | 
			
		||||
        it('should be invalid when an order is cancelled', async () => {
 | 
			
		||||
            const makerAssetAmount = new BigNumber(1000);
 | 
			
		||||
            const takerAssetAmount = new BigNumber(2);
 | 
			
		||||
            const takerBalance = takerAssetAmount;
 | 
			
		||||
            const orderFilledAmount = new BigNumber(0);
 | 
			
		||||
            const isCancelled = true;
 | 
			
		||||
            const mockBalanceFetcher = buildMockBalanceFetcher(takerBalance);
 | 
			
		||||
            const mockOrderFilledFetcher = buildMockOrderFilledFetcher(orderFilledAmount, isCancelled);
 | 
			
		||||
            const [signedOrder] = testOrderFactory.generateTestSignedOrders(
 | 
			
		||||
                {
 | 
			
		||||
                    makerAssetAmount,
 | 
			
		||||
                    takerAssetAmount,
 | 
			
		||||
                },
 | 
			
		||||
                1,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const orderStateUtils = new OrderStateUtils(mockBalanceFetcher, mockOrderFilledFetcher);
 | 
			
		||||
            const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder);
 | 
			
		||||
            expect(orderState.isValid).to.eq(false);
 | 
			
		||||
        });
 | 
			
		||||
        it('should be invalid when an order is fully filled', async () => {
 | 
			
		||||
            const makerAssetAmount = new BigNumber(1000);
 | 
			
		||||
            const takerAssetAmount = new BigNumber(2);
 | 
			
		||||
            const takerBalance = takerAssetAmount;
 | 
			
		||||
            const orderFilledAmount = takerAssetAmount;
 | 
			
		||||
            const isCancelled = false;
 | 
			
		||||
            const mockBalanceFetcher = buildMockBalanceFetcher(takerBalance);
 | 
			
		||||
            const mockOrderFilledFetcher = buildMockOrderFilledFetcher(orderFilledAmount, isCancelled);
 | 
			
		||||
            const [signedOrder] = testOrderFactory.generateTestSignedOrders(
 | 
			
		||||
                {
 | 
			
		||||
                    makerAssetAmount,
 | 
			
		||||
                    takerAssetAmount,
 | 
			
		||||
                },
 | 
			
		||||
                1,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const orderStateUtils = new OrderStateUtils(mockBalanceFetcher, mockOrderFilledFetcher);
 | 
			
		||||
            const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder);
 | 
			
		||||
            expect(orderState.isValid).to.eq(false);
 | 
			
		||||
        });
 | 
			
		||||
        it('should include the transactionHash in orderState if supplied in method invocation', async () => {
 | 
			
		||||
            const makerAssetAmount = new BigNumber(10);
 | 
			
		||||
            const takerAssetAmount = new BigNumber(10000000000000000);
 | 
			
		||||
            const takerBalance = takerAssetAmount;
 | 
			
		||||
            const orderFilledAmount = new BigNumber(0);
 | 
			
		||||
            const mockBalanceFetcher = buildMockBalanceFetcher(takerBalance);
 | 
			
		||||
            const mockOrderFilledFetcher = buildMockOrderFilledFetcher(orderFilledAmount);
 | 
			
		||||
            const [signedOrder] = testOrderFactory.generateTestSignedOrders(
 | 
			
		||||
                {
 | 
			
		||||
                    makerAssetAmount,
 | 
			
		||||
                    takerAssetAmount,
 | 
			
		||||
                },
 | 
			
		||||
                1,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const orderStateUtils = new OrderStateUtils(mockBalanceFetcher, mockOrderFilledFetcher);
 | 
			
		||||
            const transactionHash = '0xdeadbeef';
 | 
			
		||||
            const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder, transactionHash);
 | 
			
		||||
            expect(orderState.transactionHash).to.eq(transactionHash);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as chai from 'chai';
 | 
			
		||||
import 'mocha';
 | 
			
		||||
 | 
			
		||||
import { OrderValidationUtils } from '../src/order_validation_utils';
 | 
			
		||||
 | 
			
		||||
import { chaiSetup } from './utils/chai_setup';
 | 
			
		||||
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
 | 
			
		||||
describe('OrderValidationUtils', () => {
 | 
			
		||||
    describe('#isRoundingError', () => {
 | 
			
		||||
        it('should return false if there is a rounding error of 0.1%', async () => {
 | 
			
		||||
            const numerator = new BigNumber(20);
 | 
			
		||||
            const denominator = new BigNumber(999);
 | 
			
		||||
            const target = new BigNumber(50);
 | 
			
		||||
            // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
 | 
			
		||||
            const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
 | 
			
		||||
            expect(isRoundingError).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should return false if there is a rounding of 0.09%', async () => {
 | 
			
		||||
            const numerator = new BigNumber(20);
 | 
			
		||||
            const denominator = new BigNumber(9991);
 | 
			
		||||
            const target = new BigNumber(500);
 | 
			
		||||
            // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
 | 
			
		||||
            const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
 | 
			
		||||
            expect(isRoundingError).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should return true if there is a rounding error of 0.11%', async () => {
 | 
			
		||||
            const numerator = new BigNumber(20);
 | 
			
		||||
            const denominator = new BigNumber(9989);
 | 
			
		||||
            const target = new BigNumber(500);
 | 
			
		||||
            // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
 | 
			
		||||
            const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
 | 
			
		||||
            expect(isRoundingError).to.be.true();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should return true if there is a rounding error > 0.1%', async () => {
 | 
			
		||||
            const numerator = new BigNumber(3);
 | 
			
		||||
            const denominator = new BigNumber(7);
 | 
			
		||||
            const target = new BigNumber(10);
 | 
			
		||||
            // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67%
 | 
			
		||||
            const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
 | 
			
		||||
            expect(isRoundingError).to.be.true();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should return false when there is no rounding error', async () => {
 | 
			
		||||
            const numerator = new BigNumber(1);
 | 
			
		||||
            const denominator = new BigNumber(2);
 | 
			
		||||
            const target = new BigNumber(10);
 | 
			
		||||
 | 
			
		||||
            const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
 | 
			
		||||
            expect(isRoundingError).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should return false when there is rounding error <= 0.1%', async () => {
 | 
			
		||||
            // randomly generated numbers
 | 
			
		||||
            const numerator = new BigNumber(76564);
 | 
			
		||||
            const denominator = new BigNumber(676373677);
 | 
			
		||||
            const target = new BigNumber(105762562);
 | 
			
		||||
            // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
 | 
			
		||||
            // (76564*105762562/676373677) = 0.0007%
 | 
			
		||||
            const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
 | 
			
		||||
            expect(isRoundingError).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
		Reference in New Issue
	
	Block a user