aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts988
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts133
-rw-r--r--packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts28
-rw-r--r--packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts36
-rw-r--r--packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts91
-rw-r--r--packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts75
-rw-r--r--packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts112
-rw-r--r--packages/contract-wrappers/src/utils/order_validation_utils.ts202
-rw-r--r--packages/contract-wrappers/test/exchange_transfer_simulator_test.ts124
-rw-r--r--packages/contract-wrappers/test/exchange_wrapper_test.ts1227
-rw-r--r--packages/contract-wrappers/test/order_validation_test.ts539
-rw-r--r--packages/contract-wrappers/test/token_registry_wrapper_test.ts136
12 files changed, 0 insertions, 3691 deletions
diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
deleted file mode 100644
index 8548a06b6..000000000
--- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
+++ /dev/null
@@ -1,988 +0,0 @@
-import { schemas } from '@0xproject/json-schemas';
-import { formatters, getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils';
-import {
- BlockParamLiteral,
- ContractAbi,
- DecodedLogArgs,
- ECSignature,
- ExchangeContractErrs,
- LogEntry,
- LogWithDecodedArgs,
- Order,
- OrderState,
- SignedOrder,
-} from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import * as _ from 'lodash';
-
-import { artifacts } from '../artifacts';
-import { SimpleBalanceAndProxyAllowanceFetcher } from '../fetchers/simple_balance_and_proxy_allowance_fetcher';
-import { SimpleOrderFilledCancelledFetcher } from '../fetchers/simple_order_filled_cancelled_fetcher';
-import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
-import {
- BlockRange,
- EventCallback,
- ExchangeContractErrCodes,
- IndexedFilterValues,
- MethodOpts,
- OrderCancellationRequest,
- OrderFillRequest,
- OrderTransactionOpts,
- ValidateOrderFillableOpts,
-} from '../types';
-import { assert } from '../utils/assert';
-import { decorators } from '../utils/decorators';
-import { ExchangeTransferSimulator } from '../utils/exchange_transfer_simulator';
-import { OrderValidationUtils } from '../utils/order_validation_utils';
-
-import { ContractWrapper } from './contract_wrapper';
-import {
- ExchangeContract,
- ExchangeContractEventArgs,
- ExchangeEvents,
- LogErrorContractEventArgs,
-} from './generated/exchange';
-import { TokenWrapper } from './token_wrapper';
-const SHOULD_VALIDATE_BY_DEFAULT = true;
-
-interface ExchangeContractErrCodesToMsgs {
- [exchangeContractErrCodes: number]: string;
-}
-
-/**
- * This class includes all the functionality related to calling methods and subscribing to
- * events of the 0x Exchange smart contract.
- */
-export class ExchangeWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.Exchange.abi;
- private _exchangeContractIfExists?: ExchangeContract;
- private _orderValidationUtilsIfExists?: OrderValidationUtils;
- private _tokenWrapper: TokenWrapper;
- private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = {
- [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
- [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
- };
- private _contractAddressIfExists?: string;
- private _zrxContractAddressIfExists?: string;
- constructor(
- web3Wrapper: Web3Wrapper,
- networkId: number,
- tokenWrapper: TokenWrapper,
- contractAddressIfExists?: string,
- zrxContractAddressIfExists?: string,
- ) {
- super(web3Wrapper, networkId);
- this._tokenWrapper = tokenWrapper;
- this._contractAddressIfExists = contractAddressIfExists;
- this._zrxContractAddressIfExists = zrxContractAddressIfExists;
- }
- /**
- * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
- * amount that has been filled or cancelled. The remaining takerAmount can be calculated by
- * subtracting the unavailable amount from the total order takerAmount.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * unavailable takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
- */
- public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- const txData = {};
- let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash,
- txData,
- defaultBlock,
- );
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
- return unavailableTakerTokenAmount;
- }
- /**
- * Retrieve the takerAmount of an order that has already been filled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has already been filled.
- */
- public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- const txData = {};
- let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, txData, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits);
- return fillAmountInBaseUnits;
- }
- /**
- * Retrieve the takerAmount of an order that has been cancelled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * cancelled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has been cancelled.
- */
- public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- const txData = {};
- let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, txData, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
- return cancelledAmountInBaseUnits;
- }
- /**
- * Fills a signed order with an amount denominated in baseUnits of the taker token.
- * Since the order in which transactions are included in the next block is indeterminate, race-conditions
- * could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
- * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
- * If false, the smart contract will not throw if the parties
- * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
- * and causes the smart contract to throw (using all the gas supplied) instead.
- * @param signedOrder An object that conforms to the SignedOrder interface.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
- * you wish to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution the tokens cannot be transferred.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
-
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder);
-
- const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
- * If the fill amount is reached - it succeeds and does not fill the rest of the orders.
- * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
- * @param signedOrders The array of signedOrders that you would like to fill until
- * takerTokenFillAmount is reached.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
- * upon execution any of the tokens cannot be transferred.
- * If set to false, the call will continue to fill subsequent
- * signedOrders even when some cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill these
- * orders. Must be available via the supplied Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrdersUpToAsync(
- signedOrders: SignedOrder[],
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
- const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
- assert.hasAtMostOneUniqueValue(
- takerTokenAddresses,
- ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
- );
- const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- let filledTakerTokenAmount = new BigNumber(0);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- for (const signedOrder of signedOrders) {
- const singleFilledTakerTokenAmount = await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount.minus(filledTakerTokenAmount),
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
- if (filledTakerTokenAmount.eq(fillTakerTokenAmount)) {
- break;
- }
- }
- }
-
- if (_.isEmpty(signedOrders)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
-
- const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
- return [
- ...formatters.getOrderAddressesAndValues(signedOrder),
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAndSignatureArray,
- );
-
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrderAsync.
- * Executes multiple fills atomically in a single transaction.
- * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even
- * when earlier ones fail.
- * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails.
- * @param orderFillRequests An array of objects that conform to the
- * OrderFillRequest interface.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution any of the tokens cannot be
- * transferred. If set to false, the call will continue to
- * fill subsequent signedOrders even when some
- * cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill
- * these orders. Must be available via the supplied
- * Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrdersAsync(
- orderFillRequests: OrderFillRequest[],
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- for (const orderFillRequest of orderFillRequests) {
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- }
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
-
- const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
- return [
- ...formatters.getOrderAddressesAndValues(orderFillRequest.signedOrder),
- orderFillRequest.takerTokenFillAmount,
- orderFillRequest.signedOrder.ecSignature.v,
- orderFillRequest.signedOrder.ecSignature.r,
- orderFillRequest.signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAmountsAndSignatureArray,
- );
-
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
- * the fill order is abandoned.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrKillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
-
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder);
- const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
- * filled (each to the specified fillAmount) or aborted.
- * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface.
- * @param takerAddress The user Ethereum address who would like to fill there orders.
- * Must be available via the supplied Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrKillAsync(
- orderFillRequests: OrderFillRequest[],
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- for (const orderFillRequest of orderFillRequests) {
- await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- }
-
- const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
- return [
- ...formatters.getOrderAddressesAndValues(request.signedOrder),
- request.takerTokenFillAmount,
- request.signedOrder.ecSignature.v,
- request.signedOrder.ecSignature.r,
- request.signedOrder.ecSignature.s,
- ];
- });
-
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenFillAmounts,
- );
- const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmounts,
- vParams,
- rParams,
- sParams,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Cancel a given fill amount of an order. Cancellations are cumulative.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async cancelOrderAsync(
- order: Order | SignedOrder,
- cancelTakerTokenAmount: BigNumber,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
- await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
- const normalizedMakerAddress = order.maker.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const orderHash = getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
-
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order);
- const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmount,
- {
- from: normalizedMakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
- * All orders must be from the same maker.
- * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
- * interface.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchCancelOrdersAsync(
- orderCancellationRequests: OrderCancellationRequest[],
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema(
- 'orderCancellationRequests',
- orderCancellationRequests,
- schemas.orderCancellationRequestsSchema,
- );
- const exchangeContractAddresses = _.map(
- orderCancellationRequests,
- orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
- assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- const maker = makers[0];
- await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
- const normalizedMakerAddress = maker.toLowerCase();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- for (const orderCancellationRequest of orderCancellationRequests) {
- const orderHash = getOrderHashHex(orderCancellationRequest.order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- orderCancellationRequest.order,
- orderCancellationRequest.takerTokenCancelAmount,
- unavailableTakerTokenAmount,
- );
- }
- }
- if (_.isEmpty(orderCancellationRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
- const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
- return [
- ...formatters.getOrderAddressesAndValues(cancellationRequest.order),
- cancellationRequest.takerTokenCancelAmount,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenCancelAmounts,
- );
- const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmounts,
- {
- from: normalizedMakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Subscribe to an event type emitted by the Exchange contract.
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const exchangeContractAddress = this.getContractAddress();
- const subscriptionToken = this._subscribe<ArgsType>(
- exchangeContractAddress,
- eventName,
- indexFilterValues,
- artifacts.Exchange.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super._unsubscribeAll();
- }
- /**
- * Gets historical logs without creating a subscription
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const exchangeContractAddress = this.getContractAddress();
- const logs = await this._getLogsAsync<ArgsType>(
- exchangeContractAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.Exchange.abi,
- );
- return logs;
- }
- /**
- * Retrieves the Ethereum address of the Exchange contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the Exchange contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.Exchange, this._contractAddressIfExists);
- return contractAddress;
- }
- /**
- * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
- * pruning where you want to remove stale orders without knowing who the taker will be.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to validate.
- * @param opts An object that conforms to the ValidateOrderFillableOpts
- * interface. Allows specifying a specific fillTakerTokenAmount
- * to validate for.
- */
- public async validateOrderFillableOrThrowAsync(
- signedOrder: SignedOrder,
- opts?: ValidateOrderFillableOpts,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator,
- signedOrder,
- zrxTokenAddress,
- expectedFillTakerTokenAmount,
- );
- }
- /**
- * Checks if order fill will succeed and throws an error otherwise.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider passed to 0x.js.
- */
- public async validateFillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if cancelling a given order will succeed and throws an informative error if it won't.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- */
- public async validateCancelOrderThrowIfInvalidAsync(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- ): Promise<void> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
- const orderHash = getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
- /**
- * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider passed to 0x.js.
- */
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
- * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
- * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
- * no decimals can only be filled in quantities and ratios that avoid large rounding errors.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
- * @param takerTokenAmount The order size on the taker side
- * @param makerTokenAmount The order size on the maker side
- */
- public async isRoundingErrorAsync(
- fillTakerTokenAmount: BigNumber,
- takerTokenAmount: BigNumber,
- makerTokenAmount: BigNumber,
- ): Promise<boolean> {
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
- assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
- const exchangeInstance = await this._getExchangeContractAsync();
- const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
- fillTakerTokenAmount,
- takerTokenAmount,
- makerTokenAmount,
- );
- return isRoundingError;
- }
- /**
- * Checks if logs contain LogError, which is emitted by Exchange contract on transaction failure.
- * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
- */
- public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | LogEntry>): void {
- const errLog = _.find(logs, {
- event: ExchangeEvents.LogError,
- });
- if (!_.isUndefined(errLog)) {
- const logArgs = (errLog as LogWithDecodedArgs<LogErrorContractEventArgs>).args;
- const errCode = logArgs.errorId;
- const errMessage = this._exchangeContractErrCodesToMsg[errCode];
- throw new Error(errMessage);
- }
- }
- /**
- * Gets the latest OrderState of a signedOrder
- * @param signedOrder The signedOrder
- * @param stateLayer Optional, desired blockchain state layer (defaults to latest).
- * @return OrderState of the signedOrder
- */
- public async getOrderStateAsync(
- signedOrder: SignedOrder,
- stateLayer: BlockParamLiteral = BlockParamLiteral.Latest,
- ): Promise<OrderState> {
- const simpleBalanceAndProxyAllowanceFetcher = new SimpleBalanceAndProxyAllowanceFetcher(
- this._tokenWrapper,
- stateLayer,
- );
- const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(this, stateLayer);
- const orderStateUtils = new OrderStateUtils(
- simpleBalanceAndProxyAllowanceFetcher,
- simpleOrderFilledCancelledFetcher,
- );
- const orderState = orderStateUtils.getOrderStateAsync(signedOrder);
- return orderState;
- }
- /**
- * Returns the ZRX token address used by the exchange contract.
- * @return Address of ZRX token
- */
- public getZRXTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ZRX, this._zrxContractAddressIfExists);
- return contractAddress;
- }
- // tslint:disable:no-unused-variable
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- delete this._exchangeContractIfExists;
- }
- private async _isValidSignatureUsingContractCallAsync(
- dataHex: string,
- ecSignature: ECSignature,
- signerAddressHex: string,
- ): Promise<boolean> {
- assert.isHexString('dataHex', dataHex);
- assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddressHex', signerAddressHex);
- const normalizedSignerAddress = signerAddressHex.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
- normalizedSignerAddress,
- dataHex,
- ecSignature.v,
- ecSignature.r,
- ecSignature.s,
- );
- return isValidSignature;
- }
- private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order);
- const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
- return orderHashHex;
- }
- private async _getOrderValidationUtilsAsync(): Promise<OrderValidationUtils> {
- if (!_.isUndefined(this._orderValidationUtilsIfExists)) {
- return this._orderValidationUtilsIfExists;
- }
- const exchangeContract = await this._getExchangeContractAsync();
- this._orderValidationUtilsIfExists = new OrderValidationUtils(exchangeContract);
- return this._orderValidationUtilsIfExists;
- }
- // tslint:enable:no-unused-variable
- private async _getExchangeContractAsync(): Promise<ExchangeContract> {
- if (!_.isUndefined(this._exchangeContractIfExists)) {
- return this._exchangeContractIfExists;
- }
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.Exchange,
- this._contractAddressIfExists,
- );
- const contractInstance = new ExchangeContract(
- abi,
- address,
- this._web3Wrapper.getProvider(),
- this._web3Wrapper.getContractDefaults(),
- );
- this._exchangeContractIfExists = contractInstance;
- return this._exchangeContractIfExists;
- }
-} // tslint:disable:max-file-line-count
diff --git a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts
deleted file mode 100644
index 7b558ed69..000000000
--- a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { ContractAbi, Token } from '@0xproject/types';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import * as _ from 'lodash';
-
-import { artifacts } from '../artifacts';
-import { TokenMetadata } from '../types';
-import { assert } from '../utils/assert';
-import { constants } from '../utils/constants';
-
-import { ContractWrapper } from './contract_wrapper';
-import { TokenRegistryContract } from './generated/token_registry';
-
-/**
- * This class includes all the functionality related to interacting with the 0x Token Registry smart contract.
- */
-export class TokenRegistryWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.TokenRegistry.abi;
- private _tokenRegistryContractIfExists?: TokenRegistryContract;
- private _contractAddressIfExists?: string;
- private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined {
- if (metadata[0] === constants.NULL_ADDRESS) {
- return undefined;
- }
- const token = {
- address: metadata[0],
- name: metadata[1],
- symbol: metadata[2],
- decimals: metadata[3],
- };
- return token;
- }
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
- super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * Retrieves all the tokens currently listed in the Token Registry smart contract
- * @return An array of objects that conform to the Token interface.
- */
- public async getTokensAsync(): Promise<Token[]> {
- const addresses = await this.getTokenAddressesAsync();
- const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
- this.getTokenIfExistsAsync(address),
- );
- const tokens = await Promise.all(tokenPromises);
- return tokens as Token[];
- }
- /**
- * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
- * @return An array of token addresses.
- */
- public async getTokenAddressesAsync(): Promise<string[]> {
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addresses = await tokenRegistryContract.getTokenAddresses.callAsync();
- const lowerCaseAddresses = _.map(addresses, address => address.toLowerCase());
- return lowerCaseAddresses;
- }
- /**
- * Retrieves a token by address currently listed in the Token Registry smart contract
- * @return An object that conforms to the Token interface or undefined if token not found.
- */
- public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
- assert.isETHAddressHex('address', address);
- const normalizedAddress = address.toLowerCase();
-
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(normalizedAddress);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- /**
- * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the TokenRegistry contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.TokenRegistry, this._contractAddressIfExists);
- return contractAddress;
- }
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstance(): void {
- delete this._tokenRegistryContractIfExists;
- }
- private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
- if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
- return this._tokenRegistryContractIfExists;
- }
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.TokenRegistry,
- this._contractAddressIfExists,
- );
- const contractInstance = new TokenRegistryContract(
- abi,
- address,
- this._web3Wrapper.getProvider(),
- this._web3Wrapper.getContractDefaults(),
- );
- this._tokenRegistryContractIfExists = contractInstance;
- return this._tokenRegistryContractIfExists;
- }
-}
diff --git a/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts
deleted file mode 100644
index 5017d879a..000000000
--- a/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils';
-import { BlockParamLiteral } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-
-import { TokenWrapper } from '../contract_wrappers/token_wrapper';
-
-export class SimpleBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
- private _tokenWrapper: TokenWrapper;
- private _defaultBlock: BlockParamLiteral;
- constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
- this._tokenWrapper = token;
- this._defaultBlock = defaultBlock;
- }
- public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const balance = this._tokenWrapper.getBalanceAsync(tokenAddress, userAddress, methodOpts);
- return balance;
- }
- public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const proxyAllowance = this._tokenWrapper.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts);
- return proxyAllowance;
- }
-}
diff --git a/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts b/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts
deleted file mode 100644
index 52d0a100c..000000000
--- a/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils';
-import { BlockParamLiteral } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-
-import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper';
-
-export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
- private _exchangeWrapper: ExchangeWrapper;
- private _defaultBlock: BlockParamLiteral;
- constructor(exchange: ExchangeWrapper, defaultBlock: BlockParamLiteral) {
- this._exchangeWrapper = exchange;
- this._defaultBlock = defaultBlock;
- }
- public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const filledTakerAmount = this._exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts);
- return filledTakerAmount;
- }
- public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const cancelledTakerAmount = this._exchangeWrapper.getCancelledTakerAmountAsync(orderHash, methodOpts);
- return cancelledTakerAmount;
- }
- public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- const unavailableTakerAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- return unavailableTakerAmount;
- }
- public getZRXTokenAddress(): string {
- const zrxToken = this._exchangeWrapper.getZRXTokenAddress();
- return zrxToken;
- }
-}
diff --git a/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts
deleted file mode 100644
index c0250ce7c..000000000
--- a/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import { BlockParamLiteral } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
-
-import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store';
-import { TokenWrapper } from '../contract_wrappers/token_wrapper';
-
-/**
- * Copy on read store for balances/proxyAllowances of tokens/accounts
- */
-export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProxyAllowanceLazyStore {
- private _tokenWrapper: TokenWrapper;
- private _defaultBlock: BlockParamLiteral;
- private _balance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber;
- };
- };
- private _proxyAllowance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber;
- };
- };
- constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
- this._tokenWrapper = token;
- this._defaultBlock = defaultBlock;
- this._balance = {};
- this._proxyAllowance = {};
- }
- public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const balance = await this._tokenWrapper.getBalanceAsync(tokenAddress, userAddress, methodOpts);
- this.setBalance(tokenAddress, userAddress, balance);
- }
- const cachedBalance = this._balance[tokenAddress][userAddress];
- return cachedBalance;
- }
- public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
- if (_.isUndefined(this._balance[tokenAddress])) {
- this._balance[tokenAddress] = {};
- }
- this._balance[tokenAddress][userAddress] = balance;
- }
- public deleteBalance(tokenAddress: string, userAddress: string): void {
- if (!_.isUndefined(this._balance[tokenAddress])) {
- delete this._balance[tokenAddress][userAddress];
- if (_.isEmpty(this._balance[tokenAddress])) {
- delete this._balance[tokenAddress];
- }
- }
- }
- public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (
- _.isUndefined(this._proxyAllowance[tokenAddress]) ||
- _.isUndefined(this._proxyAllowance[tokenAddress][userAddress])
- ) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const proxyAllowance = await this._tokenWrapper.getProxyAllowanceAsync(
- tokenAddress,
- userAddress,
- methodOpts,
- );
- this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
- }
- const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress];
- return cachedProxyAllowance;
- }
- public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
- if (_.isUndefined(this._proxyAllowance[tokenAddress])) {
- this._proxyAllowance[tokenAddress] = {};
- }
- this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
- }
- public deleteProxyAllowance(tokenAddress: string, userAddress: string): void {
- if (!_.isUndefined(this._proxyAllowance[tokenAddress])) {
- delete this._proxyAllowance[tokenAddress][userAddress];
- if (_.isEmpty(this._proxyAllowance[tokenAddress])) {
- delete this._proxyAllowance[tokenAddress];
- }
- }
- }
- public deleteAll(): void {
- this._balance = {};
- this._proxyAllowance = {};
- }
-}
diff --git a/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts b/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts
deleted file mode 100644
index 3d96a95f3..000000000
--- a/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils';
-import { BlockParamLiteral } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
-
-import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper';
-
-/**
- * Copy on read store for filled/cancelled taker amounts
- */
-export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancelledFetcher {
- private _exchangeWrapper: ExchangeWrapper;
- private _defaultBlock: BlockParamLiteral;
- private _filledTakerAmount: {
- [orderHash: string]: BigNumber;
- };
- private _cancelledTakerAmount: {
- [orderHash: string]: BigNumber;
- };
- constructor(exchange: ExchangeWrapper, defaultBlock: BlockParamLiteral) {
- this._exchangeWrapper = exchange;
- this._defaultBlock = defaultBlock;
- this._filledTakerAmount = {};
- this._cancelledTakerAmount = {};
- }
- public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- if (_.isUndefined(this._filledTakerAmount[orderHash])) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const filledTakerAmount = await this._exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts);
- this.setFilledTakerAmount(orderHash, filledTakerAmount);
- }
- const cachedFilled = this._filledTakerAmount[orderHash];
- return cachedFilled;
- }
- public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
- this._filledTakerAmount[orderHash] = filledTakerAmount;
- }
- public deleteFilledTakerAmount(orderHash: string): void {
- delete this._filledTakerAmount[orderHash];
- }
- public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- if (_.isUndefined(this._cancelledTakerAmount[orderHash])) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const cancelledTakerAmount = await this._exchangeWrapper.getCancelledTakerAmountAsync(
- orderHash,
- methodOpts,
- );
- this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
- }
- const cachedCancelled = this._cancelledTakerAmount[orderHash];
- return cachedCancelled;
- }
- public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void {
- this._cancelledTakerAmount[orderHash] = cancelledTakerAmount;
- }
- public deleteCancelledTakerAmount(orderHash: string): void {
- delete this._cancelledTakerAmount[orderHash];
- }
- public deleteAll(): void {
- this._filledTakerAmount = {};
- this._cancelledTakerAmount = {};
- }
- public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- const unavailableTakerAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- return unavailableTakerAmount;
- }
- public getZRXTokenAddress(): string {
- const zrxToken = this._exchangeWrapper.getZRXTokenAddress();
- return zrxToken;
- }
-}
diff --git a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts
deleted file mode 100644
index 527b8575d..000000000
--- a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import { BlockParamLiteral, ExchangeContractErrs } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-
-import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store';
-import { TokenWrapper } from '../contract_wrappers/token_wrapper';
-import { TradeSide, TransferType } from '../types';
-import { constants } from '../utils/constants';
-
-enum FailureReason {
- Balance = 'balance',
- ProxyAllowance = 'proxyAllowance',
-}
-
-const ERR_MSG_MAPPING = {
- [FailureReason.Balance]: {
- [TradeSide.Maker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance,
- },
- [TradeSide.Taker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance,
- },
- },
- [FailureReason.ProxyAllowance]: {
- [TradeSide.Maker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance,
- },
- [TradeSide.Taker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance,
- },
- },
-};
-
-export class ExchangeTransferSimulator {
- private _store: AbstractBalanceAndProxyAllowanceLazyStore;
- private static _throwValidationError(
- failureReason: FailureReason,
- tradeSide: TradeSide,
- transferType: TransferType,
- ): never {
- const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
- throw new Error(errMsg);
- }
- constructor(store: AbstractBalanceAndProxyAllowanceLazyStore) {
- this._store = store;
- }
- /**
- * Simulates transferFrom call performed by a proxy
- * @param tokenAddress Address of the token to be transferred
- * @param from Owner of the transferred tokens
- * @param to Recipient of the transferred tokens
- * @param amountInBaseUnits The amount of tokens being transferred
- * @param tradeSide Is Maker/Taker transferring
- * @param transferType Is it a fee payment or a value transfer
- */
- public async transferFromAsync(
- tokenAddress: string,
- from: string,
- to: string,
- amountInBaseUnits: BigNumber,
- tradeSide: TradeSide,
- transferType: TransferType,
- ): Promise<void> {
- // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/
- // allowances for the taker. We do however, want to increase the balance of the maker since the maker
- // might be relying on those funds to fill subsequent orders or pay the order's fees.
- if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) {
- await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
- return;
- }
- const balance = await this._store.getBalanceAsync(tokenAddress, from);
- const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
- if (proxyAllowance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
- }
- if (balance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
- }
- await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits);
- await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
- await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
- }
- private async _decreaseProxyAllowanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress);
- if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
- this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
- }
- }
- private async _increaseBalanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
- this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
- }
- private async _decreaseBalanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
- this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
- }
-}
diff --git a/packages/contract-wrappers/src/utils/order_validation_utils.ts b/packages/contract-wrappers/src/utils/order_validation_utils.ts
deleted file mode 100644
index c6ef26275..000000000
--- a/packages/contract-wrappers/src/utils/order_validation_utils.ts
+++ /dev/null
@@ -1,202 +0,0 @@
-import { getOrderHashHex, isValidSignature, OrderError } from '@0xproject/order-utils';
-import { ExchangeContractErrs, Order, SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
-
-import { ExchangeContract } from '../contract_wrappers/generated/exchange';
-import { TradeSide, TransferType } from '../types';
-import { constants } from '../utils/constants';
-import { utils } from '../utils/utils';
-
-import { ExchangeTransferSimulator } from './exchange_transfer_simulator';
-
-export class OrderValidationUtils {
- private _exchangeContract: ExchangeContract;
- public static validateCancelOrderThrowIfInvalid(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- unavailableTakerTokenAmount: BigNumber,
- ): void {
- if (cancelTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
- }
- if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
- }
- const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderCancelExpired);
- }
- }
- public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- senderAddress: string,
- zrxTokenAddress: string,
- ): Promise<void> {
- const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- senderAddress,
- fillMakerTokenAmount,
- TradeSide.Maker,
- TransferType.Trade,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.takerTokenAddress,
- senderAddress,
- signedOrder.maker,
- fillTakerTokenAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const makerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- signedOrder.maker,
- signedOrder.feeRecipient,
- makerFeeAmount,
- TradeSide.Maker,
- TransferType.Fee,
- );
- const takerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.takerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- senderAddress,
- signedOrder.feeRecipient,
- takerFeeAmount,
- TradeSide.Taker,
- TransferType.Fee,
- );
- }
- private static _validateRemainingFillAmountNotZeroOrThrow(
- takerTokenAmount: BigNumber,
- unavailableTakerTokenAmount: BigNumber,
- ): void {
- if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
- }
- }
- private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber): void {
- const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderFillExpired);
- }
- }
- private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
- const fillMakerTokenAmount = numerator
- .mul(target)
- .div(denominator)
- .round(0);
- return fillMakerTokenAmount;
- }
- constructor(exchangeContract: ExchangeContract) {
- this._exchangeContract = exchangeContract;
- }
- public async validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- zrxTokenAddress: string,
- expectedFillTakerTokenAmount?: BigNumber,
- ): Promise<void> {
- const orderHash = getOrderHashHex(signedOrder);
- const unavailableTakerTokenAmount = await this._exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash,
- );
- OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
- signedOrder.takerTokenAmount,
- unavailableTakerTokenAmount,
- );
- OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
- let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- if (!_.isUndefined(expectedFillTakerTokenAmount)) {
- fillTakerTokenAmount = expectedFillTakerTokenAmount;
- }
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- signedOrder.taker,
- zrxTokenAddress,
- );
- }
- public async validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- zrxTokenAddress: string,
- ): Promise<BigNumber> {
- if (fillTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderFillAmountZero);
- }
- const orderHash = getOrderHashHex(signedOrder);
- if (!isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) {
- throw new Error(OrderError.InvalidSignature);
- }
- const unavailableTakerTokenAmount = await this._exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash,
- );
- OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
- signedOrder.takerTokenAmount,
- unavailableTakerTokenAmount,
- );
- if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
- throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
- }
- OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
- const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount)
- ? remainingTakerTokenAmount
- : fillTakerTokenAmount;
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- filledTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
-
- const wouldRoundingErrorOccur = await this._exchangeContract.isRoundingError.callAsync(
- filledTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- if (wouldRoundingErrorOccur) {
- throw new Error(ExchangeContractErrs.OrderFillRoundingError);
- }
- return filledTakerTokenAmount;
- }
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- zrxTokenAddress: string,
- ): Promise<void> {
- const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- if (filledTakerTokenAmount !== fillTakerTokenAmount) {
- throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
- }
- }
-}
diff --git a/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts b/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts
deleted file mode 100644
index cbc52df7f..000000000
--- a/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import { BlockchainLifecycle } from '@0xproject/dev-utils';
-import { BlockParamLiteral, Token } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import * as chai from 'chai';
-
-import { ContractWrappers, ExchangeContractErrs } from '../src';
-import { BalanceAndProxyAllowanceLazyStore } from '../src/stores/balance_proxy_allowance_lazy_store';
-import { TradeSide, TransferType } from '../src/types';
-import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator';
-
-import { chaiSetup } from './utils/chai_setup';
-import { constants } from './utils/constants';
-import { provider, web3Wrapper } from './utils/web3_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-
-describe('ExchangeTransferSimulator', () => {
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const contractWrappers = new ContractWrappers(provider, config);
- const transferAmount = new BigNumber(5);
- let userAddresses: string[];
- let tokens: Token[];
- let coinbase: string;
- let sender: string;
- let recipient: string;
- let exampleTokenAddress: string;
- let exchangeTransferSimulator: ExchangeTransferSimulator;
- let txHash: string;
- before(async () => {
- userAddresses = await web3Wrapper.getAvailableAddressesAsync();
- [coinbase, sender, recipient] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- exampleTokenAddress = tokens[0].address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#transferFromAsync', () => {
- beforeEach(() => {
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- contractWrappers.token,
- BlockParamLiteral.Latest,
- );
- exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- });
- it("throws if the user doesn't have enough allowance", async () => {
- return expect(
- exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
- });
- it("throws if the user doesn't have enough balance", async () => {
- txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- return expect(
- exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
- });
- it('updates balances and proxyAllowance after transfer', async () => {
- txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- await exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const store = (exchangeTransferSimulator as any)._store;
- const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
- const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
- expect(senderBalance).to.be.bignumber.equal(0);
- expect(recipientBalance).to.be.bignumber.equal(transferAmount);
- expect(senderProxyAllowance).to.be.bignumber.equal(0);
- });
- it("doesn't update proxyAllowance after transfer if unlimited", async () => {
- txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- await exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const store = (exchangeTransferSimulator as any)._store;
- const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
- const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
- expect(senderBalance).to.be.bignumber.equal(0);
- expect(recipientBalance).to.be.bignumber.equal(transferAmount);
- expect(senderProxyAllowance).to.be.bignumber.equal(
- contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
- );
- });
- });
-});
diff --git a/packages/contract-wrappers/test/exchange_wrapper_test.ts b/packages/contract-wrappers/test/exchange_wrapper_test.ts
deleted file mode 100644
index cf69d4813..000000000
--- a/packages/contract-wrappers/test/exchange_wrapper_test.ts
+++ /dev/null
@@ -1,1227 +0,0 @@
-import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils';
-import { FillScenarios } from '@0xproject/fill-scenarios';
-import { getOrderHashHex } from '@0xproject/order-utils';
-import { BlockParamLiteral, DoneCallback, OrderState } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import * as chai from 'chai';
-import 'mocha';
-
-import {
- BlockRange,
- ContractWrappers,
- DecodedLogEvent,
- ExchangeContractErrs,
- ExchangeEvents,
- LogCancelContractEventArgs,
- LogFillContractEventArgs,
- OrderCancellationRequest,
- OrderFillRequest,
- SignedOrder,
- Token,
-} from '../src';
-
-import { chaiSetup } from './utils/chai_setup';
-import { constants } from './utils/constants';
-import { TokenUtils } from './utils/token_utils';
-import { provider, web3Wrapper } from './utils/web3_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-
-const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777';
-
-describe('ExchangeWrapper', () => {
- let contractWrappers: ContractWrappers;
- let tokenUtils: TokenUtils;
- let tokens: Token[];
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let exchangeContractAddress: string;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- contractWrappers = new ContractWrappers(provider, config);
- exchangeContractAddress = contractWrappers.exchange.getContractAddress();
- userAddresses = await web3Wrapper.getAvailableAddressesAsync();
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- await fillScenarios.initTokenBalancesAsync();
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('fillOrKill order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const takerTokenFillAmount = new BigNumber(5);
- before(async () => {
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- describe('#batchFillOrKillAsync', () => {
- it('successfully batch fillOrKill', async () => {
- const fillableAmount = new BigNumber(5);
- const partialFillTakerAmount = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: partialFillTakerAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: partialFillTakerAmount,
- },
- ];
- await contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress);
- });
- describe('order transaction options', () => {
- let signedOrder: SignedOrder;
- let orderFillRequests: OrderFillRequest[];
- const fillableAmount = new BigNumber(5);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: new BigNumber(0),
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- describe('#fillOrKillOrderAsync', () => {
- let signedOrder: SignedOrder;
- const fillableAmount = new BigNumber(5);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- describe('successful fills', () => {
- it('should fill a valid order', async () => {
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(fillableAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(0);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(0);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(fillableAmount);
- await contractWrappers.exchange.fillOrKillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- takerAddress,
- );
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount));
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(takerTokenFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(takerTokenFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount));
- });
- it('should partially fill a valid order', async () => {
- const partialFillAmount = new BigNumber(3);
- await contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(partialFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(partialFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
- });
- });
- describe('order transaction options', () => {
- const emptyFillableAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- });
- describe('fill order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- const takerTokenFillAmount = new BigNumber(5);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- before(async () => {
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- describe('#fillOrderAsync', () => {
- describe('successful fills', () => {
- it('should fill a valid order', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(fillableAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(0);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(0);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(fillableAmount);
- const txHash = await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount));
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(takerTokenFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(takerTokenFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount));
- });
- it('should partially fill the valid order', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const partialFillAmount = new BigNumber(3);
- const txHash = await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- partialFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress),
- ).to.be.bignumber.equal(partialFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(partialFillAmount);
- expect(
- await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress),
- ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
- });
- it('should fill the valid orders with fees', async () => {
- const makerFee = new BigNumber(1);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- const txHash = await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- expect(
- await contractWrappers.token.getBalanceAsync(zrxTokenAddress, feeRecipient),
- ).to.be.bignumber.equal(makerFee.plus(takerFee));
- });
- });
- describe('order transaction options', () => {
- let signedOrder: SignedOrder;
- const emptyFillTakerAmount = new BigNumber(0);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- describe('negative fill amount', async () => {
- let signedOrder: SignedOrder;
- const negativeFillTakerAmount = new BigNumber(-100);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should not allow the exchange wrapper to fill if amount is negative', async () => {
- return expect(
- contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- negativeFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejected();
- });
- });
- });
- describe('#batchFillOrdersAsync', () => {
- let signedOrder: SignedOrder;
- let signedOrderHashHex: string;
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let orderFillBatch: OrderFillRequest[];
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- signedOrderHashHex = getOrderHashHex(signedOrder);
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = getOrderHashHex(anotherSignedOrder);
- });
- describe('successful batch fills', () => {
- beforeEach(() => {
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount,
- },
- ];
- });
- it('should throw if a batch is empty', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrdersAsync(
- [],
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- });
- it('should successfully fill multiple orders', async () => {
- const txHash = await contractWrappers.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(
- anotherOrderHashHex,
- );
- expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount);
- expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount);
- });
- });
- describe('order transaction options', () => {
- beforeEach(async () => {
- const emptyFillTakerAmount = new BigNumber(0);
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount: emptyFillTakerAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: emptyFillTakerAmount,
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- describe('negative batch fill amount', async () => {
- beforeEach(async () => {
- const negativeFillTakerAmount = new BigNumber(-100);
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: negativeFillTakerAmount,
- },
- ];
- });
- it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => {
- return expect(
- contractWrappers.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejected();
- });
- });
- });
- describe('#fillOrdersUpTo', () => {
- let signedOrder: SignedOrder;
- let signedOrderHashHex: string;
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let signedOrders: SignedOrder[];
- const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- signedOrderHashHex = getOrderHashHex(signedOrder);
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = getOrderHashHex(anotherSignedOrder);
- signedOrders = [signedOrder, anotherSignedOrder];
- });
- describe('successful batch fills', () => {
- it('should throw if a batch is empty', async () => {
- return expect(
- contractWrappers.exchange.fillOrdersUpToAsync(
- [],
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- });
- it('should successfully fill up to specified amount when all orders are fully funded', async () => {
- const txHash = await contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(
- anotherOrderHashHex,
- );
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- const remainingFillAmount = fillableAmount.minus(1);
- expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
- it('should successfully fill up to specified amount and leave the rest of the orders untouched', async () => {
- const txHash = await contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const zeroAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- expect(zeroAmount).to.be.bignumber.equal(0);
- });
- it('should successfully fill up to specified amount even if filling all orders would fail', async () => {
- const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9,
- // but won't have 10 to fully fill all orders in a batch.
- await contractWrappers.token.transferAsync(
- makerTokenAddress,
- makerAddress,
- coinbase,
- missingBalance,
- );
- const txHash = await contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(
- anotherOrderHashHex,
- );
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- const remainingFillAmount = fillableAmount.minus(1);
- expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
- });
- describe('failed batch fills', () => {
- it("should fail validation if user doesn't have enough balance without fill up to", async () => {
- const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8
- await contractWrappers.token.transferAsync(
- makerTokenAddress,
- makerAddress,
- coinbase,
- missingBalance,
- );
- return expect(
- contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
- });
- });
- describe('order transaction options', () => {
- const emptyFillUpToAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- });
- describe('cancel order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- let signedOrder: SignedOrder;
- let orderHashHex: string;
- const cancelAmount = new BigNumber(3);
- beforeEach(async () => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- orderHashHex = getOrderHashHex(signedOrder);
- });
- describe('#cancelOrderAsync', () => {
- describe('successful cancels', () => {
- it('should cancel an order', async () => {
- const txHash = await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const cancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHashHex);
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
- });
- describe('order transaction options', () => {
- const emptyCancelTakerTokenAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- });
- });
- describe('#batchCancelOrdersAsync', () => {
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let cancelBatch: OrderCancellationRequest[];
- beforeEach(async () => {
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = getOrderHashHex(anotherSignedOrder);
- cancelBatch = [
- {
- order: signedOrder,
- takerTokenCancelAmount: cancelAmount,
- },
- {
- order: anotherSignedOrder,
- takerTokenCancelAmount: cancelAmount,
- },
- ];
- });
- describe('failed batch cancels', () => {
- it('should throw when orders have different makers', async () => {
- const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- takerAddress,
- takerAddress,
- fillableAmount,
- );
- return expect(
- contractWrappers.exchange.batchCancelOrdersAsync([
- cancelBatch[0],
- {
- order: signedOrderWithDifferentMaker,
- takerTokenCancelAmount: cancelAmount,
- },
- ]),
- ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- });
- });
- describe('successful batch cancels', () => {
- it('should cancel a batch of orders', async () => {
- await contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch);
- const cancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHashHex);
- const anotherCancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(
- anotherOrderHashHex,
- );
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
- });
- describe('order transaction options', () => {
- beforeEach(async () => {
- const emptyTakerTokenCancelAmount = new BigNumber(0);
- cancelBatch = [
- {
- order: signedOrder,
- takerTokenCancelAmount: emptyTakerTokenCancelAmount,
- },
- {
- order: anotherSignedOrder,
- takerTokenCancelAmount: emptyTakerTokenCancelAmount,
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith(
- ExchangeContractErrs.OrderCancelAmountZero,
- );
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- });
- });
- });
- describe('tests that require partially filled order', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let takerAddress: string;
- let fillableAmount: BigNumber;
- let partialFillAmount: BigNumber;
- let signedOrder: SignedOrder;
- let orderHash: string;
- before(() => {
- takerAddress = userAddresses[1];
- tokenUtils = new TokenUtils(tokens);
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- fillableAmount = new BigNumber(5);
- partialFillAmount = new BigNumber(2);
- signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- takerAddress,
- fillableAmount,
- partialFillAmount,
- );
- orderHash = getOrderHashHex(signedOrder);
- });
- describe('#getUnavailableTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(
- contractWrappers.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex),
- ).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const unavailableValueT = await contractWrappers.exchange.getUnavailableTakerAmountAsync(
- NON_EXISTENT_ORDER_HASH,
- );
- expect(unavailableValueT).to.be.bignumber.equal(0);
- });
- it('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
- const unavailableValueT = await contractWrappers.exchange.getUnavailableTakerAmountAsync(orderHash);
- expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount);
- });
- });
- describe('#getFilledTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(
- contractWrappers.exchange.getFilledTakerAmountAsync(invalidOrderHashHex),
- ).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const filledValueT = await contractWrappers.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(filledValueT).to.be.bignumber.equal(0);
- });
- it('should return the filledValueT for a valid and partially filled orderHash', async () => {
- const filledValueT = await contractWrappers.exchange.getFilledTakerAmountAsync(orderHash);
- expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
- });
- });
- describe('#getCancelledTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(
- contractWrappers.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex),
- ).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(
- NON_EXISTENT_ORDER_HASH,
- );
- expect(cancelledValueT).to.be.bignumber.equal(0);
- });
- it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
- const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHash);
- expect(cancelledValueT).to.be.bignumber.equal(0);
- });
- it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
- const cancelAmount = fillableAmount.minus(partialFillAmount);
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHash);
- expect(cancelledValueT).to.be.bignumber.equal(cancelAmount);
- });
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let takerAddress: string;
- let makerAddress: string;
- let fillableAmount: BigNumber;
- let signedOrder: SignedOrder;
- const takerTokenFillAmountInBaseUnits = new BigNumber(1);
- const cancelTakerAmountInBaseUnits = new BigNumber(1);
- before(() => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- fillableAmount = new BigNumber(5);
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- afterEach(async () => {
- contractWrappers.exchange.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
- (async () => {
- const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
- },
- );
- contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
- await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- })().catch(done);
- });
- it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
- (async () => {
- const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel);
- },
- );
- contractWrappers.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback);
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
- })().catch(done);
- });
- it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled);
-
- contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID);
-
- const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
- },
- );
- contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
- await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- const subscriptionToken = contractWrappers.exchange.subscribe(
- ExchangeEvents.LogFill,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- contractWrappers.exchange.unsubscribe(subscriptionToken);
- await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- done();
- })().catch(done);
- });
- });
- describe('#getOrderHashHexUsingContractCallAsync', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- before(async () => {
- [, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- it("get's the same hash as the local function", async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const orderHash = getOrderHashHex(signedOrder);
- const orderHashFromContract = await (contractWrappers.exchange as any)._getOrderHashHexUsingContractCallAsync(
- signedOrder,
- );
- expect(orderHash).to.equal(orderHashFromContract);
- });
- });
- describe('#getZRXTokenAddressAsync', () => {
- it('gets the same token as is in token registry', () => {
- const zrxAddress = contractWrappers.exchange.getZRXTokenAddress();
- const zrxToken = tokenUtils.getProtocolTokenOrThrow();
- expect(zrxAddress).to.equal(zrxToken.address);
- });
- });
- describe('#getLogsAsync', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(async () => {
- [, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- it('should get logs with decoded args emitted by LogFill', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const eventName = ExchangeEvents.LogFill;
- const indexFilterValues = {};
- const logs = await contractWrappers.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
- expect(logs).to.have.length(1);
- expect(logs[0].event).to.be.equal(eventName);
- });
- it('should only get the logs with the correct event name', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const differentEventName = ExchangeEvents.LogCancel;
- const indexFilterValues = {};
- const logs = await contractWrappers.exchange.getLogsAsync(
- differentEventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
-
- const differentMakerAddress = userAddresses[2];
- const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- differentMakerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await contractWrappers.exchange.fillOrderAsync(
- anotherSignedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
-
- const eventName = ExchangeEvents.LogFill;
- const indexFilterValues = {
- maker: differentMakerAddress,
- };
- const logs = await contractWrappers.exchange.getLogsAsync<LogFillContractEventArgs>(
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args.maker).to.be.equal(differentMakerAddress);
- });
- });
- describe('#getOrderStateAsync', () => {
- let maker: string;
- let taker: string;
- let makerToken: Token;
- let takerToken: Token;
- let signedOrder: SignedOrder;
- let orderState: OrderState;
- const fillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), constants.ZRX_DECIMALS);
- before(async () => {
- [, maker, taker] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- [makerToken, takerToken] = tokenUtils.getDummyTokens();
- });
- it('should report orderStateValid when order is fillable', async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- orderState = await contractWrappers.exchange.getOrderStateAsync(signedOrder);
- expect(orderState.isValid).to.be.true();
- });
- it('should report orderStateInvalid when maker allowance set to 0', async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- await contractWrappers.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0));
- orderState = await contractWrappers.exchange.getOrderStateAsync(signedOrder);
- expect(orderState.isValid).to.be.false();
- });
- });
-}); // tslint:disable:max-file-line-count
diff --git a/packages/contract-wrappers/test/order_validation_test.ts b/packages/contract-wrappers/test/order_validation_test.ts
deleted file mode 100644
index 2afea2d5f..000000000
--- a/packages/contract-wrappers/test/order_validation_test.ts
+++ /dev/null
@@ -1,539 +0,0 @@
-import { BlockchainLifecycle } from '@0xproject/dev-utils';
-import { FillScenarios } from '@0xproject/fill-scenarios';
-import { OrderError } from '@0xproject/order-utils';
-import { BlockParamLiteral } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import * as chai from 'chai';
-import * as Sinon from 'sinon';
-
-import { ContractWrappers, ExchangeContractErrs, SignedOrder, Token } from '../src';
-import { BalanceAndProxyAllowanceLazyStore } from '../src/stores/balance_proxy_allowance_lazy_store';
-import { TradeSide, TransferType } from '../src/types';
-import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator';
-import { OrderValidationUtils } from '../src/utils/order_validation_utils';
-
-import { chaiSetup } from './utils/chai_setup';
-import { constants } from './utils/constants';
-import { TokenUtils } from './utils/token_utils';
-import { provider, web3Wrapper } from './utils/web3_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-
-describe('OrderValidation', () => {
- let contractWrappers: ContractWrappers;
- let userAddresses: string[];
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let exchangeContractAddress: string;
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- const fillTakerAmount = new BigNumber(5);
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- contractWrappers = new ContractWrappers(provider, config);
- exchangeContractAddress = contractWrappers.exchange.getContractAddress();
- userAddresses = await web3Wrapper.getAvailableAddressesAsync();
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('validateOrderFillableOrThrowAsync', () => {
- it('should succeed if the order is fillable', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should succeed if the maker is buying ZRX and has no ZRX balance', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- zrxTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress);
- await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance);
- await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should succeed if the maker is buying ZRX and has no ZRX balance and there is no specified taker', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- zrxTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- constants.NULL_ADDRESS,
- fillableAmount,
- feeRecipient,
- );
- const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress);
- await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance);
- await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should succeed if the order is asymmetric and fillable', async () => {
- const makerFillableAmount = fillableAmount;
- // tslint:disable-next-line:custom-no-magic-numbers
- const takerFillableAmount = fillableAmount.minus(4);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerFillableAmount,
- takerFillableAmount,
- );
- await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should throw when the order is fully filled or cancelled', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
- ExchangeContractErrs.OrderRemainingFillAmountZero,
- );
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
- ExchangeContractErrs.OrderFillExpired,
- );
- });
- });
- describe('validateFillOrderAndThrowIfInvalidAsync', () => {
- it('should throw when the fill amount is zero', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const zeroFillAmount = new BigNumber(0);
- return expect(
- contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- zeroFillAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should throw when the signature is invalid', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- // 27 <--> 28
- // tslint:disable-next-line:custom-no-magic-numbers
- signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
- return expect(
- contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillableAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(OrderError.InvalidSignature);
- });
- it('should throw when the order is fully filled or cancelled', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(
- contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillableAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- it('should throw when sender is not a taker', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- // tslint:disable-next-line:custom-no-magic-numbers
- const nonTakerAddress = userAddresses[6];
- return expect(
- contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillTakerAmount,
- nonTakerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(
- contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillTakerAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
- });
- it('should throw when there a rounding error would have occurred', async () => {
- const makerAmount = new BigNumber(3);
- const takerAmount = new BigNumber(5);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerAmount,
- takerAmount,
- );
- const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
- return expect(
- contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillTakerAmountThatCausesRoundingError,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
- });
- });
- describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
- it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const tooLargeFillAmount = new BigNumber(7);
- const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
- await contractWrappers.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
- await contractWrappers.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
- await contractWrappers.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
- await contractWrappers.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
-
- return expect(
- contractWrappers.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder,
- tooLargeFillAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
- });
- });
- describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
- let signedOrder: SignedOrder;
- const cancelAmount = new BigNumber(3);
- beforeEach(async () => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should throw when cancel amount is zero', async () => {
- const zeroCancelAmount = new BigNumber(0);
- return expect(
- contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(
- contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
- });
- it('should throw when order is already cancelled or filled', async () => {
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(
- contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
- });
- });
- describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
- let exchangeTransferSimulator: ExchangeTransferSimulator;
- let transferFromAsync: Sinon.SinonSpy;
- const bigNumberMatch = (expected: BigNumber) => {
- return Sinon.match((value: BigNumber) => value.eq(expected));
- };
- beforeEach('create exchangeTransferSimulator', async () => {
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- contractWrappers.token,
- BlockParamLiteral.Latest,
- );
- exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- transferFromAsync = Sinon.spy();
- exchangeTransferSimulator.transferFromAsync = transferFromAsync as any;
- });
- it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillableAmount,
- takerAddress,
- zrxTokenAddress,
- );
- // tslint:disable-next-line:custom-no-magic-numbers
- expect(transferFromAsync.callCount).to.be.equal(4);
- expect(
- transferFromAsync
- .getCall(0)
- .calledWith(
- makerTokenAddress,
- makerAddress,
- takerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(1)
- .calledWith(
- takerTokenAddress,
- takerAddress,
- makerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(2)
- .calledWith(
- zrxTokenAddress,
- makerAddress,
- feeRecipient,
- bigNumberMatch(makerFee),
- TradeSide.Maker,
- TransferType.Fee,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(3)
- .calledWith(
- zrxTokenAddress,
- takerAddress,
- feeRecipient,
- bigNumberMatch(takerFee),
- TradeSide.Taker,
- TransferType.Fee,
- ),
- ).to.be.true();
- });
- it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- constants.NULL_ADDRESS,
- fillableAmount,
- feeRecipient,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillableAmount,
- takerAddress,
- zrxTokenAddress,
- );
- // tslint:disable-next-line:custom-no-magic-numbers
- expect(transferFromAsync.callCount).to.be.equal(4);
- expect(
- transferFromAsync
- .getCall(0)
- .calledWith(
- makerTokenAddress,
- makerAddress,
- takerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(1)
- .calledWith(
- takerTokenAddress,
- takerAddress,
- makerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(2)
- .calledWith(
- zrxTokenAddress,
- makerAddress,
- feeRecipient,
- bigNumberMatch(makerFee),
- TradeSide.Maker,
- TransferType.Fee,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(3)
- .calledWith(
- zrxTokenAddress,
- takerAddress,
- feeRecipient,
- bigNumberMatch(takerFee),
- TradeSide.Taker,
- TransferType.Fee,
- ),
- ).to.be.true();
- });
- it('should correctly round the fillMakerTokenAmount', async () => {
- const makerTokenAmount = new BigNumber(3);
- const takerTokenAmount = new BigNumber(1);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerTokenAmount,
- takerTokenAmount,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- takerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- // tslint:disable-next-line:custom-no-magic-numbers
- expect(transferFromAsync.callCount).to.be.equal(4);
- const makerFillAmount = transferFromAsync.getCall(0).args[3];
- expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount);
- });
- it('should correctly round the makerFeeAmount', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(4);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- constants.NULL_ADDRESS,
- );
- const fillTakerTokenAmount = fillableAmount.div(2).round(0);
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- const makerPartialFee = makerFee.div(2);
- const takerPartialFee = takerFee.div(2);
- // tslint:disable-next-line:custom-no-magic-numbers
- expect(transferFromAsync.callCount).to.be.equal(4);
- const partialMakerFee = transferFromAsync.getCall(2).args[3];
- expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee);
- const partialTakerFee = transferFromAsync.getCall(3).args[3];
- expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
- });
- });
-}); // tslint:disable-line:max-file-line-count
diff --git a/packages/contract-wrappers/test/token_registry_wrapper_test.ts b/packages/contract-wrappers/test/token_registry_wrapper_test.ts
deleted file mode 100644
index 6576d789d..000000000
--- a/packages/contract-wrappers/test/token_registry_wrapper_test.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import { BlockchainLifecycle } from '@0xproject/dev-utils';
-import { schemas, SchemaValidator } from '@0xproject/json-schemas';
-import * as chai from 'chai';
-import * as _ from 'lodash';
-import 'mocha';
-
-import { ContractWrappers, Token } from '../src';
-
-import { chaiSetup } from './utils/chai_setup';
-import { constants } from './utils/constants';
-import { provider, web3Wrapper } from './utils/web3_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-
-const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7;
-
-describe('TokenRegistryWrapper', () => {
- let contractWrappers: ContractWrappers;
- let tokens: Token[];
- const tokenAddressBySymbol: { [symbol: string]: string } = {};
- const tokenAddressByName: { [symbol: string]: string } = {};
- const tokenBySymbol: { [symbol: string]: Token } = {};
- const tokenByName: { [symbol: string]: Token } = {};
- const registeredSymbol = 'ZRX';
- const registeredName = '0x Protocol Token';
- const unregisteredSymbol = 'MAL';
- const unregisteredName = 'Malicious Token';
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- contractWrappers = new ContractWrappers(provider, config);
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- _.map(tokens, token => {
- tokenAddressBySymbol[token.symbol] = token.address;
- tokenAddressByName[token.name] = token.address;
- tokenBySymbol[token.symbol] = token;
- tokenByName[token.name] = token;
- });
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#getTokensAsync', () => {
- it('should return all the tokens added to the tokenRegistry during the migration', async () => {
- expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
-
- const schemaValidator = new SchemaValidator();
- _.each(tokens, token => {
- const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- });
- });
- });
- describe('#getTokenAddressesAsync', () => {
- it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
- const tokenAddresses = await contractWrappers.tokenRegistry.getTokenAddressesAsync();
- expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
-
- const schemaValidator = new SchemaValidator();
- _.each(tokenAddresses, tokenAddress => {
- const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- expect(tokenAddress).to.not.be.equal(constants.NULL_ADDRESS);
- });
- });
- });
- describe('#getTokenAddressBySymbol', () => {
- it('should return correct address for a token in the registry', async () => {
- const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(
- registeredSymbol,
- );
- expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
- });
- it('should return undefined for a token out of registry', async () => {
- const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(
- unregisteredSymbol,
- );
- expect(tokenAddress).to.be.undefined();
- });
- });
- describe('#getTokenAddressByName', () => {
- it('should return correct address for a token in the registry', async () => {
- const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync(
- registeredName,
- );
- expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
- });
- it('should return undefined for a token out of registry', async () => {
- const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync(
- unregisteredName,
- );
- expect(tokenAddress).to.be.undefined();
- });
- });
- describe('#getTokenBySymbol', () => {
- it('should return correct token for a token in the registry', async () => {
- const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
- expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
- });
- it('should return undefined for a token out of registry', async () => {
- const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
- expect(token).to.be.undefined();
- });
- });
- describe('#getTokenByName', () => {
- it('should return correct token for a token in the registry', async () => {
- const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
- expect(token).to.be.deep.equal(tokenByName[registeredName]);
- });
- it('should return undefined for a token out of registry', async () => {
- const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
- expect(token).to.be.undefined();
- });
- });
- describe('#getTokenIfExistsAsync', () => {
- it('should return the token added to the tokenRegistry during the migration', async () => {
- const aToken = tokens[0];
-
- const token = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(aToken.address);
- const schemaValidator = new SchemaValidator();
- const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- });
- it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
- const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
- const tokenIfExists = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
- expect(tokenIfExists).to.be.undefined();
- });
- });
-});