From 8ab80914e01ce67020ee2c94e6390309ca1b850f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 8 Jun 2017 11:25:47 +0200 Subject: Implement batchFillOrKill and tests --- src/contract_wrappers/exchange_wrapper.ts | 77 ++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) (limited to 'src/contract_wrappers/exchange_wrapper.ts') diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 4f132656e..901ea4749 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, @@ -188,13 +189,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); @@ -223,6 +219,62 @@ 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 to the desired fillAmount or aborted. + */ + public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[], + takerAddress: string) { + await assert.isSenderAddressAsync('takerAddress', takerAddress, this.web3Wrapper); + const exchangeInstance = await this.getExchangeContractAsync(); + _.each(orderFillOrKillRequests, request => { + assert.doesConformToSchema('signedOrder', + SchemaValidator.convertToJSONSchemaCompatibleObject(request.signedOrder as object), + signedOrderSchema); + assert.isBigNumber('fillTakerAmount', request.fillTakerAmount); + 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, + ]; + }); + + const [orderAddresses, orderValues, fillTakerAmounts, vParams, rParams, sParams] = + _.unzip(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. */ @@ -334,6 +386,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. -- cgit v1.2.3 From 23a4dcb729084c0e00c759da487ae3959f436b3a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 8 Jun 2017 18:13:54 +0200 Subject: Add orderFillOrKillRequestsSchema and validate using it instead of looping over the requests and validating them individually --- src/contract_wrappers/exchange_wrapper.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/contract_wrappers/exchange_wrapper.ts') diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 6ef87b7ed..cfbea43a5 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -28,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'; @@ -276,12 +277,12 @@ export class ExchangeWrapper extends ContractWrapper { 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 => { - assert.doesConformToSchema('signedOrder', - SchemaValidator.convertToJSONSchemaCompatibleObject(request.signedOrder as object), - signedOrderSchema); - assert.isBigNumber('fillTakerAmount', request.fillTakerAmount); this.validateFillOrKillOrderAndThrowIfInvalidAsync(request.signedOrder, exchangeInstance.address, request.fillTakerAmount); -- cgit v1.2.3 From 80b76e58172313e396c81c719bd46094b9491b84 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 8 Jun 2017 18:14:02 +0200 Subject: Improve comment --- src/contract_wrappers/exchange_wrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/contract_wrappers/exchange_wrapper.ts') diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index cfbea43a5..972e11b8c 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -272,7 +272,7 @@ export class ExchangeWrapper extends ContractWrapper { } /** * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically - * filled to the desired fillAmount or aborted. + * filled (each to the specified fillAmount) or aborted. */ public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[], takerAddress: string) { -- cgit v1.2.3 From 655c71a46b9e3ad82380efe6b37e9140ecc130cf Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 8 Jun 2017 18:16:28 +0200 Subject: add comment explaining the use of any --- src/contract_wrappers/exchange_wrapper.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src/contract_wrappers/exchange_wrapper.ts') diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 972e11b8c..46613af77 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -298,6 +298,7 @@ export class ExchangeWrapper extends ContractWrapper { ]; }); + // We use _.unzip because _.unzip doesn't type check if values have different types :'( const [orderAddresses, orderValues, fillTakerAmounts, vParams, rParams, sParams] = _.unzip(orderAddressesValuesAndTakerTokenFillAmounts); -- cgit v1.2.3 From b1b879e217f383fa694214e731969647f9f6fd78 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 8 Jun 2017 18:17:30 +0200 Subject: fix indentation --- src/contract_wrappers/exchange_wrapper.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src/contract_wrappers/exchange_wrapper.ts') diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 46613af77..73ec0886c 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -303,15 +303,15 @@ export class ExchangeWrapper extends ContractWrapper { _.unzip(orderAddressesValuesAndTakerTokenFillAmounts); const gas = await exchangeInstance.batchFillOrKill.estimateGas( - orderAddresses, - orderValues, - fillTakerAmounts, - vParams, - rParams, - sParams, - { - from: takerAddress, - }, + orderAddresses, + orderValues, + fillTakerAmounts, + vParams, + rParams, + sParams, + { + from: takerAddress, + }, ); const response: ContractResponse = await exchangeInstance.batchFillOrKill( orderAddresses, @@ -320,10 +320,10 @@ export class ExchangeWrapper extends ContractWrapper { vParams, rParams, sParams, - { - from: takerAddress, - gas, - }, + { + from: takerAddress, + gas, + }, ); this.throwErrorLogsAsErrors(response.logs); } -- cgit v1.2.3