From f58c203653873d3363c2c5e7662a61569146a36b Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Wed, 23 Aug 2017 14:06:31 +0200 Subject: Move order validation functions to orderValidationUtils and make isRoundingError public --- src/contract_wrappers/exchange_wrapper.ts | 77 +++++++++++-------------------- src/utils/order_validation_utils.ts | 64 ++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 90f63a55b..4589107a6 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -84,7 +84,7 @@ export class ExchangeWrapper extends ContractWrapper { constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper) { super(web3Wrapper); this._tokenWrapper = tokenWrapper; - this._orderValidationUtils = new OrderValidationUtils(tokenWrapper); + this._orderValidationUtils = new OrderValidationUtils(tokenWrapper, this); this._exchangeLogEventEmitters = []; } /** @@ -640,27 +640,9 @@ export class ExchangeWrapper extends ContractWrapper { assert.doesConformToSchema('signedOrder', signedOrder, signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - if (fillTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); - } - if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { - throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); - if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderFillExpired); - } - const zrxTokenAddress = await this._getZRXTokenAddressAsync(signedOrder.exchangeContractAddress); - await this._orderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, - ); - - const wouldRoundingErrorOccur = await this._isRoundingErrorAsync( - fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount, - ); - if (wouldRoundingErrorOccur) { - throw new Error(ExchangeContractErrs.OrderFillRoundingError); - } + const zrxTokenAddress = await this._getZRXTokenAddressAsync(); + await this._orderValidationUtils.validateFillOrderAndThrowIfInvalidAsync( + signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); } /** * Checks if order cancel will succeed and throws an error otherwise. @@ -672,18 +654,10 @@ export class ExchangeWrapper extends ContractWrapper { order: Order, cancelTakerTokenAmount: BigNumber.BigNumber): Promise { assert.doesConformToSchema('order', order, orderSchema); assert.isBigNumber('cancelTakerTokenAmount', cancelTakerTokenAmount); - if (cancelTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderCancelAmountZero); - } const orderHash = utils.getOrderHashHex(order); - const unavailableAmount = await this.getUnavailableTakerAmountAsync(orderHash); - if (order.takerTokenAmount.minus(unavailableAmount).eq(0)) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); - if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderCancelExpired); - } + const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); + await this._orderValidationUtils.validateCancelOrderAndThrowIfInvalidAsync( + order, cancelTakerTokenAmount, unavailableTakerTokenAmount); } /** * Checks if fillOrKill order will succeed and throws an error otherwise. @@ -696,14 +670,24 @@ export class ExchangeWrapper extends ContractWrapper { public async validateFillOrKillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber, takerAddress: string): Promise { - await this.validateFillOrderAndThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); - // Check that fillValue available >= fillTakerAmount - const orderHashHex = utils.getOrderHashHex(signedOrder); - const unavailableTakerAmount = await this.getUnavailableTakerAmountAsync(orderHashHex); - const remainingTakerAmount = signedOrder.takerTokenAmount.minus(unavailableTakerAmount); - if (remainingTakerAmount < fillTakerTokenAmount) { - throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); - } + assert.doesConformToSchema('signedOrder', signedOrder, signedOrderSchema); + assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + const zrxTokenAddress = await this._getZRXTokenAddressAsync(); + await this._orderValidationUtils.validateFillOrKillOrderAndThrowIfInvalidAsync( + signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); + } + public async isRoundingErrorAsync(numerator: BigNumber.BigNumber, + demoninator: BigNumber.BigNumber, + makerTokenAmount: BigNumber.BigNumber): Promise { + assert.isBigNumber('numerator', numerator); + assert.isBigNumber('demoninator', demoninator); + assert.isBigNumber('makerTokenAmount', makerTokenAmount); + const exchangeInstance = await this._getExchangeContractAsync(); + const isRoundingError = await exchangeInstance.isRoundingError.call( + numerator, demoninator, makerTokenAmount, + ); + return isRoundingError; } private async _invalidateContractInstancesAsync(): Promise { await this.stopWatchingAllEventsAsync(); @@ -740,15 +724,6 @@ export class ExchangeWrapper extends ContractWrapper { throw new Error(errMessage); } } - private async _isRoundingErrorAsync(numerator: BigNumber.BigNumber, - demoninator: BigNumber.BigNumber, - makerTokenAmount: BigNumber.BigNumber): Promise { - const exchangeInstance = await this._getExchangeContractAsync(); - const isRoundingError = await exchangeInstance.isRoundingError.call( - numerator, demoninator, makerTokenAmount, - ); - return isRoundingError; - } private async _getExchangeContractAsync(): Promise { if (!_.isUndefined(this._exchangeContractIfExists)) { return this._exchangeContractIfExists; @@ -757,7 +732,7 @@ export class ExchangeWrapper extends ContractWrapper { this._exchangeContractIfExists = contractInstance as ExchangeContract; return this._exchangeContractIfExists; } - private async _getZRXTokenAddressAsync(exchangeContractAddress: string): Promise { + private async _getZRXTokenAddressAsync(): Promise { const exchangeInstance = await this._getExchangeContractAsync(); const ZRXtokenAddress = await exchangeInstance.ZRX_TOKEN_CONTRACT.call(); return ZRXtokenAddress; diff --git a/src/utils/order_validation_utils.ts b/src/utils/order_validation_utils.ts index 8a737040c..686dfe33f 100644 --- a/src/utils/order_validation_utils.ts +++ b/src/utils/order_validation_utils.ts @@ -1,10 +1,70 @@ -import {ExchangeContractErrs, SignedOrder} from '../types'; +import {ExchangeContractErrs, SignedOrder, Order} from '../types'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; +import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; +import {utils} from '../utils/utils'; +import {constants} from '../utils/constants'; export class OrderValidationUtils { private tokenWrapper: TokenWrapper; - constructor(tokenWrapper: TokenWrapper) { + private exchangeWrapper: ExchangeWrapper; + constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) { this.tokenWrapper = tokenWrapper; + this.exchangeWrapper = exchangeWrapper; + } + public async validateFillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber.BigNumber, + takerAddress: string, + zrxTokenAddress: string): Promise { + if (fillTakerTokenAmount.eq(0)) { + throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); + } + if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { + throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); + } + const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); + if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { + throw new Error(ExchangeContractErrs.OrderFillExpired); + } + await this.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, + ); + + const wouldRoundingErrorOccur = await this.exchangeWrapper.isRoundingErrorAsync( + fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount, + ); + if (wouldRoundingErrorOccur) { + throw new Error(ExchangeContractErrs.OrderFillRoundingError); + } + } + public async validateFillOrKillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber.BigNumber, + takerAddress: string, + zrxTokenAddress: string): Promise { + await this.validateFillOrderAndThrowIfInvalidAsync( + signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); + // Check that fillValue available >= fillTakerAmount + const orderHashHex = utils.getOrderHashHex(signedOrder); + const unavailableTakerAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHashHex); + const remainingTakerAmount = signedOrder.takerTokenAmount.minus(unavailableTakerAmount); + if (remainingTakerAmount < fillTakerTokenAmount) { + throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); + } + } + public async validateCancelOrderAndThrowIfInvalidAsync(order: Order, + cancelTakerTokenAmount: BigNumber.BigNumber, + unavailableTakerTokenAmount: BigNumber.BigNumber, + ): Promise { + if (cancelTakerTokenAmount.eq(0)) { + throw new Error(ExchangeContractErrs.OrderCancelAmountZero); + } + const orderHash = utils.getOrderHashHex(order); + if (order.takerTokenAmount.minus(unavailableTakerTokenAmount).eq(0)) { + throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); + } + const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); + if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { + throw new Error(ExchangeContractErrs.OrderCancelExpired); + } } public async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( signedOrder: SignedOrder, fillTakerAmount: BigNumber.BigNumber, senderAddress: string, zrxTokenAddress: string, -- cgit v1.2.3