diff options
Diffstat (limited to 'src/contract_wrappers/exchange_wrapper.ts')
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 150 |
1 files changed, 112 insertions, 38 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 54d7f62d5..d02a6e642 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -28,6 +28,8 @@ import { LogCancelContractEventArgs, LogWithDecodedArgs, MethodOpts, + ValidateOrderFillableOpts, + OrderTransactionOpts, } from '../types'; import {assert} from '../utils/assert'; import {utils} from '../utils/utils'; @@ -39,6 +41,8 @@ import {TokenWrapper} from './token_wrapper'; import {decorators} from '../utils/decorators'; 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. @@ -153,19 +157,26 @@ export class ExchangeWrapper extends ContractWrapper { * @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. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber, shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - await this.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await this.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); @@ -210,12 +221,14 @@ export class ExchangeWrapper extends ContractWrapper { * @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. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async fillOrdersUpToAsync(signedOrders: SignedOrder[], fillTakerTokenAmount: BigNumber.BigNumber, shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); assert.hasAtMostOneUniqueValue(takerTokenAddresses, @@ -226,10 +239,15 @@ export class ExchangeWrapper extends ContractWrapper { assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - for (const signedOrder of signedOrders) { - await this.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillTakerTokenAmount, takerAddress); + + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(signedOrders.map(signedOrder => this.validateFillOrderThrowIfInvalidAsync( + signedOrder, fillTakerTokenAmount, takerAddress))); } + if (_.isEmpty(signedOrders)) { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); } @@ -291,12 +309,14 @@ export class ExchangeWrapper extends ContractWrapper { * @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. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async batchFillOrdersAsync(orderFillRequests: OrderFillRequest[], shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); const exchangeContractAddresses = _.map( orderFillRequests, @@ -306,9 +326,13 @@ export class ExchangeWrapper extends ContractWrapper { ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - for (const orderFillRequest of orderFillRequests) { - await this.validateFillOrderThrowIfInvalidAsync( - orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, takerAddress); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(orderFillRequests.map(orderFillRequest => this.validateFillOrderThrowIfInvalidAsync( + orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, takerAddress)), + ); } if (_.isEmpty(orderFillRequests)) { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); @@ -364,18 +388,25 @@ export class ExchangeWrapper extends ContractWrapper { * @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. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async fillOrKillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - await this.validateFillOrKillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await this.validateFillOrKillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); @@ -410,11 +441,13 @@ export class ExchangeWrapper extends ContractWrapper { * @param orderFillOrKillRequests An array of objects that conform to the OrderFillOrKillRequest 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. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[], - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('orderFillOrKillRequests', orderFillOrKillRequests, schemas.orderFillOrKillRequestsSchema); const exchangeContractAddresses = _.map( @@ -428,9 +461,14 @@ export class ExchangeWrapper extends ContractWrapper { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); } const exchangeInstance = await this._getExchangeContractAsync(); - for (const request of orderFillOrKillRequests) { - await this.validateFillOrKillOrderThrowIfInvalidAsync( - request.signedOrder, request.fillTakerAmount, takerAddress); + + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(orderFillOrKillRequests.map(request => this.validateFillOrKillOrderThrowIfInvalidAsync( + request.signedOrder, request.fillTakerAmount, takerAddress)), + ); } const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillOrKillRequests, request => { @@ -477,17 +515,25 @@ export class ExchangeWrapper extends ContractWrapper { * @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. - * @return Transaction hash. + * @param transactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler - public async cancelOrderAsync( - order: Order|SignedOrder, cancelTakerTokenAmount: BigNumber.BigNumber): Promise<string> { + public async cancelOrderAsync(order: Order|SignedOrder, + cancelTakerTokenAmount: BigNumber.BigNumber, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('order', order, schemas.orderSchema); assert.isBigNumber('takerTokenCancelAmount', cancelTakerTokenAmount); await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - await this.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); + + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await this.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); + } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order); const gas = await exchangeInstance.cancelOrder.estimateGasAsync( @@ -514,10 +560,12 @@ export class ExchangeWrapper extends ContractWrapper { * All orders must be from the same maker. * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest * interface. - * @return Transaction hash. + * @param transactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler - public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<string> { + public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[], + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests, schemas.orderCancellationRequestsSchema); const exchangeContractAddresses = _.map( @@ -530,10 +578,13 @@ export class ExchangeWrapper extends ContractWrapper { assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); const maker = makers[0]; await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); - for (const cancellationRequest of orderCancellationRequests) { - await this.validateCancelOrderThrowIfInvalidAsync( - cancellationRequest.order, cancellationRequest.takerTokenCancelAmount, - ); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(orderCancellationRequests.map(cancellationRequest => + this.validateCancelOrderThrowIfInvalidAsync( + cancellationRequest.order, cancellationRequest.takerTokenCancelAmount))); } if (_.isEmpty(orderCancellationRequests)) { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); @@ -624,6 +675,25 @@ export class ExchangeWrapper extends ContractWrapper { 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; + await this._orderValidationUtils.validateOrderFillableOrThrowAsync( + 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. @@ -637,7 +707,7 @@ export class ExchangeWrapper extends ContractWrapper { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const zrxTokenAddress = await this._getZRXTokenAddressAsync(); + const zrxTokenAddress = await this.getZRXTokenAddressAsync(); await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync( signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); } @@ -670,7 +740,7 @@ export class ExchangeWrapper extends ContractWrapper { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const zrxTokenAddress = await this._getZRXTokenAddressAsync(); + const zrxTokenAddress = await this.getZRXTokenAddressAsync(); await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); } @@ -708,6 +778,15 @@ export class ExchangeWrapper extends ContractWrapper { 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> { await this.stopWatchingAllEventsAsync(); delete this._exchangeContractIfExists; @@ -745,11 +824,6 @@ export class ExchangeWrapper extends ContractWrapper { this._exchangeContractIfExists = contractInstance as ExchangeContract; return this._exchangeContractIfExists; } - private async _getZRXTokenAddressAsync(): Promise<string> { - const exchangeInstance = await this._getExchangeContractAsync(); - const ZRXtokenAddress = await exchangeInstance.ZRX_TOKEN_CONTRACT.callAsync(); - return ZRXtokenAddress; - } private async _getTokenTransferProxyAddressAsync(): Promise<string> { const exchangeInstance = await this._getExchangeContractAsync(); const tokenTransferProxyAddress = await exchangeInstance.TOKEN_TRANSFER_PROXY_CONTRACT.callAsync(); |