Refactor to use OrderCancellationRequest

This commit is contained in:
Leonid Logvinov 2017-06-07 14:40:28 +02:00
parent 91101eb8ec
commit c3cd5812e6
No known key found for this signature in database
GPG Key ID: 0DD294BFDE8C95D4
4 changed files with 45 additions and 34 deletions

View File

@ -18,7 +18,7 @@ import {
CreateContractEvent, CreateContractEvent,
ContractEventObj, ContractEventObj,
EventCallback, EventCallback,
ContractResponse, ContractResponse, OrderCancellationRequest,
} from '../types'; } from '../types';
import {assert} from '../utils/assert'; import {assert} from '../utils/assert';
import {utils} from '../utils/utils'; import {utils} from '../utils/utils';
@ -208,29 +208,31 @@ export class ExchangeWrapper extends ContractWrapper {
/** /**
* Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction. * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
*/ */
public async batchCancelOrderAsync( public async batchCancelOrderAsync(cancellationRequestsBatch: OrderCancellationRequest[]): Promise<void> {
orders: Array<Order|SignedOrder>, takerTokenCancelAmounts: BigNumber.BigNumber[]): Promise<void> { const makers = _.map(cancellationRequestsBatch, cancellationRequest => cancellationRequest.order.maker);
const makers = _.map(orders, order => order.maker); assert.assert(!_.isEmpty(cancellationRequestsBatch), 'Can not cancel an empty batch');
assert.isSameLength('orders', orders, 'takerTokenCancelAmounts', takerTokenCancelAmounts);
assert.assert(!_.isEmpty(orders), 'Can not cancel an empty batch');
assert.assert(_.uniq(makers).length === 1, 'Can not cancel orders from multiple makers in a single batch'); assert.assert(_.uniq(makers).length === 1, 'Can not cancel orders from multiple makers in a single batch');
const maker = makers[0]; const maker = makers[0];
// _.zip doesn't type check if values have different types :'( _.forEach(cancellationRequestsBatch,
const ordersAndTakerTokenCancelAmounts = _.zip<any>(orders, takerTokenCancelAmounts); async (cancellationRequest: OrderCancellationRequest) => {
_.forEach(ordersAndTakerTokenCancelAmounts,
async ([order, takerTokenCancelAmount]: [Order|SignedOrder, BigNumber.BigNumber]) => {
assert.doesConformToSchema('order', assert.doesConformToSchema('order',
SchemaValidator.convertToJSONSchemaCompatibleObject(order as object), orderSchema); SchemaValidator.convertToJSONSchemaCompatibleObject(cancellationRequest.order as object), orderSchema);
assert.isBigNumber('takerTokenCancelAmount', takerTokenCancelAmount); assert.isBigNumber('takerTokenCancelAmount', cancellationRequest.takerTokenCancelAmount);
await assert.isSenderAddressAvailableAsync(this.web3Wrapper, 'order.maker', order.maker); await assert.isSenderAddressAvailableAsync(this.web3Wrapper, 'order.maker',
await this.validateCancelOrderAndThrowIfInvalidAsync(order, takerTokenCancelAmount); cancellationRequest.order.maker);
await this.validateCancelOrderAndThrowIfInvalidAsync(
cancellationRequest.order, cancellationRequest.takerTokenCancelAmount);
}); });
const exchangeInstance = await this.getExchangeContractAsync(); const exchangeInstance = await this.getExchangeContractAsync();
const orderAddressesAndValues = _.map(orders, order => { const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(cancellationRequestsBatch, cancellationRequest => {
return ExchangeWrapper.getOrderAddressesAndValues(order); return [
...ExchangeWrapper.getOrderAddressesAndValues(cancellationRequest.order),
cancellationRequest.takerTokenCancelAmount,
];
}); });
// _.unzip doesn't type check if values have different types :'( // _.unzip doesn't type check if values have different types :'(
const [orderAddresses, orderValues] = _.unzip<any>(orderAddressesAndValues); const [orderAddresses, orderValues, takerTokenCancelAmounts] =
_.unzip<any>(orderAddressesValuesAndTakerTokenCancelAmounts);
const gas = await exchangeInstance.batchCancel.estimateGas( const gas = await exchangeInstance.batchCancel.estimateGas(
orderAddresses, orderAddresses,
orderValues, orderValues,

View File

@ -222,3 +222,8 @@ export interface SubscriptionOpts {
} }
export type DoneCallback = (err?: Error) => void; export type DoneCallback = (err?: Error) => void;
export interface OrderCancellationRequest {
order: Order|SignedOrder;
takerTokenCancelAmount: BigNumber.BigNumber;
}

View File

@ -42,12 +42,6 @@ export const assert = {
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 instance'); this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 instance');
}, },
isSameLength(variableName1: string, value1: any[], variableName2: string, value2: any[]) {
const length1 = value1.length;
const length2 = value2.length;
this.assert(length1 === length2, `${variableName1} and ${variableName2} length mismatch. \
${length1} != ${length2}`);
},
isNumber(variableName: string, value: number): void { isNumber(variableName: string, value: number): void {
this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value)); this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
}, },

View File

@ -16,7 +16,7 @@ import {
ExchangeEvents, ExchangeEvents,
ContractEvent, ContractEvent,
DoneCallback, DoneCallback,
ExchangeContractErrs, ExchangeContractErrs, OrderCancellationRequest,
} from '../src/types'; } from '../src/types';
import {FillScenarios} from './utils/fill_scenarios'; import {FillScenarios} from './utils/fill_scenarios';
import {TokenUtils} from './utils/token_utils'; import {TokenUtils} from './utils/token_utils';
@ -377,34 +377,44 @@ describe('ExchangeWrapper', () => {
describe('#batchCancelOrderAsync', () => { describe('#batchCancelOrderAsync', () => {
let anotherSignedOrder: SignedOrder; let anotherSignedOrder: SignedOrder;
let anotherOrderHashHex: string; let anotherOrderHashHex: string;
let cancelBatch: OrderCancellationRequest[];
beforeEach(async () => { beforeEach(async () => {
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
); );
anotherOrderHashHex = await zeroEx.getOrderHashHexAsync(anotherSignedOrder); anotherOrderHashHex = await zeroEx.getOrderHashHexAsync(anotherSignedOrder);
cancelBatch = [
{
order: signedOrder,
takerTokenCancelAmount: cancelAmount,
},
{
order: anotherSignedOrder,
takerTokenCancelAmount: cancelAmount,
},
];
}); });
describe('failed batch cancels', () => { describe('failed batch cancels', () => {
it('should throw when length of orders and cancelAmounts mismatch', async () => {
return expect(zeroEx.exchange.batchCancelOrderAsync([signedOrder], []))
.to.be.rejectedWith('orders and takerTokenCancelAmounts length mismatch. 1 != 0');
});
it('should throw when orders are empty', async () => { it('should throw when orders are empty', async () => {
return expect(zeroEx.exchange.batchCancelOrderAsync([], [])) return expect(zeroEx.exchange.batchCancelOrderAsync([]))
.to.be.rejectedWith('Can not cancel an empty batch'); .to.be.rejectedWith('Can not cancel an empty batch');
}); });
it.only('should throw when orders have different makers', async () => { it.only('should throw when orders have different makers', async () => {
const signedOrderWithADifferentMaker = await fillScenarios.createFillableSignedOrderAsync( const signedOrderWithADifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, takerAddress, takerAddress, fillableAmount, makerTokenAddress, takerTokenAddress, takerAddress, takerAddress, fillableAmount,
); );
return expect(zeroEx.exchange.batchCancelOrderAsync( return expect(zeroEx.exchange.batchCancelOrderAsync([
[signedOrder, signedOrderWithADifferentMaker], [cancelAmount, cancelAmount])) cancelBatch[0],
.to.be.rejectedWith('Can not cancel orders from multiple makers in a single batch'); {
order: signedOrderWithADifferentMaker,
takerTokenCancelAmount: cancelAmount,
},
])).to.be.rejectedWith('Can not cancel orders from multiple makers in a single batch');
}); });
}); });
describe('successful batch cancels', () => { describe('successful batch cancels', () => {
it('should cancel a batch of orders', async () => { it('should cancel a batch of orders', async () => {
await zeroEx.exchange.batchCancelOrderAsync( await zeroEx.exchange.batchCancelOrderAsync(cancelBatch);
[signedOrder, anotherSignedOrder], [cancelAmount, cancelAmount]);
const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex); const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
const anotherCancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync( const anotherCancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(
anotherOrderHashHex); anotherOrderHashHex);