diff options
Diffstat (limited to 'src/contract_wrappers')
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 113 |
1 files changed, 89 insertions, 24 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index afaa89c94..a7d1f52ae 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -10,6 +10,7 @@ import { OrderValues, OrderAddresses, Order, + OrderFillOrKillRequest, SignedOrder, ContractEvent, ExchangeEvents, @@ -27,6 +28,7 @@ import {utils} from '../utils/utils'; import {ContractWrapper} from './contract_wrapper'; import * as ExchangeArtifacts from '../artifacts/Exchange.json'; import {ecSignatureSchema} from '../schemas/ec_signature_schema'; +import {orderFillOrKillRequestsSchema} from '../schemas/order_fill_or_kill_requests_schema'; import {signedOrderSchema, orderSchema} from '../schemas/order_schemas'; import {SchemaValidator} from '../utils/schema_validator'; import {constants} from '../utils/constants'; @@ -71,23 +73,6 @@ export class ExchangeWrapper extends ContractWrapper { await this.stopWatchingExchangeLogEventsAsync(); delete this.exchangeContractIfExists; } - private async isValidSignatureUsingContractCallAsync(dataHex: string, ecSignature: ECSignature, - signerAddressHex: string): Promise<boolean> { - assert.isHexString('dataHex', dataHex); - assert.doesConformToSchema('ecSignature', ecSignature, ecSignatureSchema); - assert.isETHAddressHex('signerAddressHex', signerAddressHex); - - const exchangeInstance = await this.getExchangeContractAsync(); - - const isValidSignature = await exchangeInstance.isValidSignature.call( - signerAddressHex, - dataHex, - ecSignature.v, - ecSignature.r, - ecSignature.s, - ); - return isValidSignature; - } /** * 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 @@ -321,13 +306,8 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeInstance = await this.getExchangeContractAsync(); await this.validateFillOrderAndThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress); - // Check that fillValue available >= fillTakerAmount - const orderHashHex = await this.getOrderHashHexAsync(signedOrder); - const unavailableTakerAmount = await this.getUnavailableTakerAmountAsync(orderHashHex); - const remainingTakerAmount = signedOrder.takerTokenAmount.minus(unavailableTakerAmount); - if (remainingTakerAmount < fillTakerAmount) { - throw new Error(ExchangeContractErrs.INSUFFICIENT_REMAINING_FILL_AMOUNT); - } + await this.validateFillOrKillOrderAndThrowIfInvalidAsync(signedOrder, exchangeInstance.address, + fillTakerAmount); const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(signedOrder); @@ -357,6 +337,63 @@ export class ExchangeWrapper extends ContractWrapper { this.throwErrorLogsAsErrors(response.logs); } /** + * 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. + */ + public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[], + takerAddress: string) { + await assert.isSenderAddressAsync('takerAddress', takerAddress, this.web3Wrapper); + assert.doesConformToSchema('orderFillOrKillRequests', + SchemaValidator.convertToJSONSchemaCompatibleObject(orderFillOrKillRequests), + orderFillOrKillRequestsSchema, + ); + const exchangeInstance = await this.getExchangeContractAsync(); + _.each(orderFillOrKillRequests, request => { + this.validateFillOrKillOrderAndThrowIfInvalidAsync(request.signedOrder, + exchangeInstance.address, + request.fillTakerAmount); + }); + + const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillOrKillRequests, request => { + return [ + ...ExchangeWrapper.getOrderAddressesAndValues(request.signedOrder), + request.fillTakerAmount, + 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, fillTakerAmounts, vParams, rParams, sParams] = + _.unzip<any>(orderAddressesValuesAndTakerTokenFillAmounts); + + const gas = await exchangeInstance.batchFillOrKill.estimateGas( + orderAddresses, + orderValues, + fillTakerAmounts, + vParams, + rParams, + sParams, + { + from: takerAddress, + }, + ); + const response: ContractResponse = await exchangeInstance.batchFillOrKill( + orderAddresses, + orderValues, + fillTakerAmounts, + vParams, + rParams, + sParams, + { + from: takerAddress, + gas, + }, + ); + this.throwErrorLogsAsErrors(response.logs); + } + /** * Cancel a given fill amount of an order. Cancellations are cumulative. */ public async cancelOrderAsync( @@ -469,6 +506,23 @@ export class ExchangeWrapper extends ContractWrapper { logEventObj.watch(callback); this.exchangeLogEventObjs.push(logEventObj); } + private async isValidSignatureUsingContractCallAsync(dataHex: string, ecSignature: ECSignature, + signerAddressHex: string): Promise<boolean> { + assert.isHexString('dataHex', dataHex); + assert.doesConformToSchema('ecSignature', ecSignature, ecSignatureSchema); + assert.isETHAddressHex('signerAddressHex', signerAddressHex); + + const exchangeInstance = await this.getExchangeContractAsync(); + + const isValidSignature = await exchangeInstance.isValidSignature.call( + signerAddressHex, + dataHex, + ecSignature.v, + ecSignature.r, + ecSignature.s, + ); + return isValidSignature; + } private async getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> { const exchangeInstance = await this.getExchangeContractAsync(); const orderHashHex = utils.getOrderHashHex(order, exchangeInstance.address); @@ -526,6 +580,17 @@ export class ExchangeWrapper extends ContractWrapper { throw new Error(ExchangeContractErrs.ORDER_CANCEL_EXPIRED); } } + private async validateFillOrKillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder, + exchangeAddress: string, + fillTakerAmount: BigNumber.BigNumber) { + // Check that fillValue available >= fillTakerAmount + const orderHashHex = utils.getOrderHashHex(signedOrder, exchangeAddress); + const unavailableTakerAmount = await this.getUnavailableTakerAmountAsync(orderHashHex); + const remainingTakerAmount = signedOrder.takerTokenAmount.minus(unavailableTakerAmount); + if (remainingTakerAmount < fillTakerAmount) { + throw new Error(ExchangeContractErrs.INSUFFICIENT_REMAINING_FILL_AMOUNT); + } + } /** * This method does not currently validate the edge-case where the makerToken or takerToken is also the token used * to pay fees (ZRX). It is possible for them to have enough for fees and the transfer but not both. |