aboutsummaryrefslogtreecommitdiffstats
path: root/src/contract_wrappers/exchange_wrapper.ts
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2017-11-13 11:17:18 +0800
committerFabio Berger <me@fabioberger.com>2017-11-13 11:17:18 +0800
commitc4ee2d73865a1444c079b9e2836b7630a0adf03e (patch)
treeb9c7794e7022fb189675d914f5fe58dcabd67dec /src/contract_wrappers/exchange_wrapper.ts
parenta74ec0effa818a86233fe64cb0dad2c61bbb4bb6 (diff)
downloaddexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar
dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.gz
dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.bz2
dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.lz
dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.xz
dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.zst
dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.zip
Switch over to Lerna + Yarn Workspaces setup for a mono-repo approach
Diffstat (limited to 'src/contract_wrappers/exchange_wrapper.ts')
-rw-r--r--src/contract_wrappers/exchange_wrapper.ts866
1 files changed, 0 insertions, 866 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts
deleted file mode 100644
index fe0c5bc00..000000000
--- a/src/contract_wrappers/exchange_wrapper.ts
+++ /dev/null
@@ -1,866 +0,0 @@
-import * as _ from 'lodash';
-import * as Web3 from 'web3';
-import BigNumber from 'bignumber.js';
-import {schemas} from '0x-json-schemas';
-import {Web3Wrapper} from '../web3_wrapper';
-import {
- ECSignature,
- ExchangeContract,
- ExchangeContractErrCodes,
- ExchangeContractErrs,
- ZeroExError,
- OrderValues,
- OrderAddresses,
- Order,
- SignedOrder,
- ExchangeEvents,
- SubscriptionOpts,
- IndexedFilterValues,
- OrderCancellationRequest,
- OrderFillRequest,
- LogErrorContractEventArgs,
- LogFillContractEventArgs,
- LogCancelContractEventArgs,
- LogWithDecodedArgs,
- MethodOpts,
- ValidateOrderFillableOpts,
- OrderTransactionOpts,
- RawLog,
- EventCallback,
- ExchangeContractEventArgs,
- DecodedLogArgs,
-} from '../types';
-import {assert} from '../utils/assert';
-import {utils} from '../utils/utils';
-import {OrderValidationUtils} from '../utils/order_validation_utils';
-import {ContractWrapper} from './contract_wrapper';
-import {TokenWrapper} from './token_wrapper';
-import {decorators} from '../utils/decorators';
-import {AbiDecoder} from '../utils/abi_decoder';
-import {ExchangeTransferSimulator} from '../utils/exchange_transfer_simulator';
-import {artifacts} from '../artifacts';
-
-const SHOULD_VALIDATE_BY_DEFAULT = true;
-
-/**
- * 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 {
- private _exchangeContractIfExists?: ExchangeContract;
- private _orderValidationUtils: OrderValidationUtils;
- private _tokenWrapper: TokenWrapper;
- private _exchangeContractErrCodesToMsg = {
- [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 static _getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] {
- const orderAddresses: OrderAddresses = [
- order.maker,
- order.taker,
- order.makerTokenAddress,
- order.takerTokenAddress,
- order.feeRecipient,
- ];
- const orderValues: OrderValues = [
- order.makerTokenAmount,
- order.takerTokenAmount,
- order.makerFee,
- order.takerFee,
- order.expirationUnixTimestampSec,
- order.salt,
- ];
- return [orderAddresses, orderValues];
- }
- constructor(web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder,
- tokenWrapper: TokenWrapper, contractAddressIfExists?: string) {
- super(web3Wrapper, abiDecoder);
- this._tokenWrapper = tokenWrapper;
- this._orderValidationUtils = new OrderValidationUtils(tokenWrapper, this);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * 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 canceled.
- */
- 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;
- let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash, 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;
- let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, 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 getCanceledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, 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 Web3.Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.contractCallErrorHandler
- 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 exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress);
- }
-
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
-
- const gas = await exchangeInstance.fillOrder.estimateGasAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- },
- );
- const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- gas,
- },
- );
- 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 Web3.Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.contractCallErrorHandler
- 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 shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- for (const signedOrder of signedOrders) {
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress);
- }
- }
-
- if (_.isEmpty(signedOrders)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
-
- const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
- return [
- ...ExchangeWrapper._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 gas = await exchangeInstance.fillOrdersUpTo.estimateGasAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- },
- );
- const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- gas,
- },
- );
- 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
- * Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.contractCallErrorHandler
- 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 shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- for (const orderFillRequest of orderFillRequests) {
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount,
- takerAddress, zrxTokenAddress,
- );
- }
- }
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
-
- const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
- return [
- ...ExchangeWrapper._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 gas = await exchangeInstance.batchFillOrders.estimateGasAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- },
- );
- const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- gas,
- },
- );
- 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 Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.contractCallErrorHandler
- 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 exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress);
- }
-
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
-
- const gas = await exchangeInstance.fillOrKillOrder.estimateGasAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- },
- );
- const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- gas,
- },
- );
- 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 Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.contractCallErrorHandler
- 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);
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- for (const orderFillRequest of orderFillRequests) {
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount,
- takerAddress, zrxTokenAddress,
- );
- }
- }
-
- const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
- return [
- ...ExchangeWrapper._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 gas = await exchangeInstance.batchFillOrKillOrders.estimateGasAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmounts,
- vParams,
- rParams,
- sParams,
- {
- from: takerAddress,
- },
- );
- const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmounts,
- vParams,
- rParams,
- sParams,
- {
- from: takerAddress,
- gas,
- },
- );
- 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.contractCallErrorHandler
- 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 exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const orderHash = utils.getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- await this._orderValidationUtils.validateCancelOrderThrowIfInvalidAsync(
- order, cancelTakerTokenAmount, unavailableTakerTokenAmount);
- }
-
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
- const gas = await exchangeInstance.cancelOrder.estimateGasAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmount,
- {
- from: order.maker,
- },
- );
- const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmount,
- {
- from: order.maker,
- gas,
- },
- );
- 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.contractCallErrorHandler
- 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 shouldValidate = _.isUndefined(orderTransactionOpts) ?
- SHOULD_VALIDATE_BY_DEFAULT :
- orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- for (const orderCancellationRequest of orderCancellationRequests) {
- const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- await this._orderValidationUtils.validateCancelOrderThrowIfInvalidAsync(
- orderCancellationRequest.order, orderCancellationRequest.takerTokenCancelAmount,
- unavailableTakerTokenAmount,
- );
- }
-
- }
- if (_.isEmpty(orderCancellationRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
- const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
- return [
- ...ExchangeWrapper._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 gas = await exchangeInstance.batchCancelOrders.estimateGasAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmounts,
- {
- from: maker,
- },
- );
- const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmounts,
- {
- from: maker,
- gas,
- },
- );
- 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 async subscribeAsync<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents, indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>): Promise<string> {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const exchangeContractAddress = await this.getContractAddressAsync();
- const subscriptionToken = this._subscribe<ArgsType>(
- exchangeContractAddress, eventName, indexFilterValues, artifacts.ExchangeArtifact.abi, callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Gets historical logs without creating a subscription
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param subscriptionOpts Subscriptions options that let you configure the subscription.
- * @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, subscriptionOpts: SubscriptionOpts, indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('subscriptionOpts', subscriptionOpts, schemas.subscriptionOptsSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const exchangeContractAddress = await this.getContractAddressAsync();
- const logs = await this._getLogsAsync<ArgsType>(
- exchangeContractAddress, eventName, subscriptionOpts, indexFilterValues, artifacts.ExchangeArtifact.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 async getContractAddressAsync(): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const exchangeAddress = exchangeInstance.address;
- return exchangeAddress;
- }
- /**
- * 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 = await this.getZRXTokenAddressAsync();
- const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- await this._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 Web3.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 zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, 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 = utils.getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- await this._orderValidationUtils.validateCancelOrderThrowIfInvalidAsync(
- 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 Web3.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 zrxTokenAddress = await this.getZRXTokenAddressAsync();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper);
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, 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 emmited by Exchange contract on transaction failure.
- * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
- */
- public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs>|Web3.LogEntry>): void {
- const errLog = _.find(logs, {
- event: ExchangeEvents.LogError,
- }) as LogWithDecodedArgs<LogErrorContractEventArgs>|undefined;
- if (!_.isUndefined(errLog)) {
- const logArgs = errLog.args;
- const errCode = logArgs.errorId.toNumber();
- const errMessage = this._exchangeContractErrCodesToMsg[errCode];
- throw new Error(errMessage);
- }
- }
- /**
- * Returns the ZRX token address used by the exchange contract.
- * @return Address of ZRX token
- */
- public async getZRXTokenAddressAsync(): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const ZRXtokenAddress = await exchangeInstance.ZRX_TOKEN_CONTRACT.callAsync();
- return ZRXtokenAddress;
- }
- private async _invalidateContractInstancesAsync(): Promise<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 exchangeInstance = await this._getExchangeContractAsync();
-
- const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
- signerAddressHex,
- 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] = ExchangeWrapper._getOrderAddressesAndValues(order);
- const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
- return orderHashHex;
- }
- private async _getExchangeContractAsync(): Promise<ExchangeContract> {
- if (!_.isUndefined(this._exchangeContractIfExists)) {
- return this._exchangeContractIfExists;
- }
- const contractInstance = await this._instantiateContractIfExistsAsync<ExchangeContract>(
- artifacts.ExchangeArtifact, this._contractAddressIfExists,
- );
- this._exchangeContractIfExists = contractInstance as ExchangeContract;
- return this._exchangeContractIfExists;
- }
- private async _getTokenTransferProxyAddressAsync(): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const tokenTransferProxyAddress = await exchangeInstance.TOKEN_TRANSFER_PROXY_CONTRACT.callAsync();
- const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase();
- return tokenTransferProxyAddressLowerCase;
- }
-}