diff options
Diffstat (limited to 'packages/0x.js/src/utils')
-rw-r--r-- | packages/0x.js/src/utils/abi_decoder.ts | 33 | ||||
-rw-r--r-- | packages/0x.js/src/utils/assert.ts | 28 | ||||
-rw-r--r-- | packages/0x.js/src/utils/class_utils.ts | 18 | ||||
-rw-r--r-- | packages/0x.js/src/utils/constants.ts | 3 | ||||
-rw-r--r-- | packages/0x.js/src/utils/decorators.ts | 94 | ||||
-rw-r--r-- | packages/0x.js/src/utils/exchange_transfer_simulator.ts | 83 | ||||
-rw-r--r-- | packages/0x.js/src/utils/filter_utils.ts | 26 | ||||
-rw-r--r-- | packages/0x.js/src/utils/interval_utils.ts | 20 | ||||
-rw-r--r-- | packages/0x.js/src/utils/order_state_utils.ts | 89 | ||||
-rw-r--r-- | packages/0x.js/src/utils/order_validation_utils.ts | 172 | ||||
-rw-r--r-- | packages/0x.js/src/utils/signature_utils.ts | 7 | ||||
-rw-r--r-- | packages/0x.js/src/utils/utils.ts | 45 |
12 files changed, 367 insertions, 251 deletions
diff --git a/packages/0x.js/src/utils/abi_decoder.ts b/packages/0x.js/src/utils/abi_decoder.ts index f26b057f0..bbd2a0b1d 100644 --- a/packages/0x.js/src/utils/abi_decoder.ts +++ b/packages/0x.js/src/utils/abi_decoder.ts @@ -1,14 +1,14 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as Web3 from 'web3'; import * as SolidityCoder from 'web3/lib/solidity/coder'; -import {AbiType, ContractEventArgs, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes} from '../types'; +import { AbiType, ContractEventArgs, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes } from '../types'; export class AbiDecoder { - private savedABIs: Web3.AbiDefinition[] = []; - private methodIds: {[signatureHash: string]: Web3.EventAbi} = {}; - private static padZeros(address: string) { + private _savedABIs: Web3.AbiDefinition[] = []; + private _methodIds: { [signatureHash: string]: Web3.EventAbi } = {}; + private static _padZeros(address: string) { let formatted = address; if (_.startsWith(formatted, '0x')) { formatted = formatted.slice(2); @@ -18,13 +18,14 @@ export class AbiDecoder { return `0x${formatted}`; } constructor(abiArrays: Web3.AbiDefinition[][]) { - _.map(abiArrays, this.addABI.bind(this)); + _.map(abiArrays, this._addABI.bind(this)); } // This method can only decode logs from the 0x & ERC20 smart contracts public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( - log: Web3.LogEntry): LogWithDecodedArgs<ArgsType>|RawLog { + log: Web3.LogEntry, + ): LogWithDecodedArgs<ArgsType> | RawLog { const methodId = log.topics[0]; - const event = this.methodIds[methodId]; + const event = this._methodIds[methodId]; if (_.isUndefined(event)) { return log; } @@ -41,10 +42,12 @@ export class AbiDecoder { // Indexed parameters are stored in topics. Non-indexed ones in decodedData let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++]; if (param.type === SolidityTypes.Address) { - value = AbiDecoder.padZeros(new BigNumber(value).toString(16)); - } else if (param.type === SolidityTypes.Uint256 || - param.type === SolidityTypes.Uint8 || - param.type === SolidityTypes.Uint) { + value = AbiDecoder._padZeros(new BigNumber(value).toString(16)); + } else if ( + param.type === SolidityTypes.Uint256 || + param.type === SolidityTypes.Uint8 || + param.type === SolidityTypes.Uint + ) { value = new BigNumber(value); } decodedParams[param.name] = value; @@ -56,14 +59,14 @@ export class AbiDecoder { args: decodedParams, }; } - private addABI(abiArray: Web3.AbiDefinition[]): void { + private _addABI(abiArray: Web3.AbiDefinition[]): void { _.map(abiArray, (abi: Web3.AbiDefinition) => { if (abi.type === AbiType.Event) { const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`; const signatureHash = new Web3().sha3(signature); - this.methodIds[signatureHash] = abi; + this._methodIds[signatureHash] = abi; } }); - this.savedABIs = this.savedABIs.concat(abiArray); + this._savedABIs = this._savedABIs.concat(abiArray); } } diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts index 4cf6caf77..c21f2dbca 100644 --- a/packages/0x.js/src/utils/assert.ts +++ b/packages/0x.js/src/utils/assert.ts @@ -1,14 +1,14 @@ -import {assert as sharedAssert} from '@0xproject/assert'; -import {Schema, SchemaValidator} from '@0xproject/json-schemas'; -import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; +import { assert as sharedAssert } from '@0xproject/assert'; +// We need those two unused imports because they're actually used by sharedAssert which gets injected here +// tslint:disable-next-line:no-unused-variable +import { Schema } from '@0xproject/json-schemas'; +// tslint:disable-next-line:no-unused-variable +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; -import {ECSignature} from '../types'; -import {signatureUtils} from '../utils/signature_utils'; - -const HEX_REGEX = /^0x[0-9A-F]*$/i; +import { ECSignature } from '../types'; +import { signatureUtils } from '../utils/signature_utils'; export const assert = { ...sharedAssert, @@ -16,11 +16,15 @@ export const assert = { const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress); this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`); }, - async isSenderAddressAsync(variableName: string, senderAddressHex: string, - web3Wrapper: Web3Wrapper): Promise<void> { + async isSenderAddressAsync( + variableName: string, + senderAddressHex: string, + web3Wrapper: Web3Wrapper, + ): Promise<void> { sharedAssert.isETHAddressHex(variableName, senderAddressHex); const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex); - sharedAssert.assert(isSenderAddressAvailable, + sharedAssert.assert( + isSenderAddressAvailable, `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`, ); }, diff --git a/packages/0x.js/src/utils/class_utils.ts b/packages/0x.js/src/utils/class_utils.ts deleted file mode 100644 index 04e60ee57..000000000 --- a/packages/0x.js/src/utils/class_utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as _ from 'lodash'; - -export const classUtils = { - // This is useful for classes that have nested methods. Nested methods don't get bound out of the box. - bindAll(self: any, exclude: string[] = ['contructor'], thisArg?: any): void { - for (const key of Object.getOwnPropertyNames(self)) { - const val = self[key]; - if (!_.includes(exclude, key)) { - if (_.isFunction(val)) { - self[key] = val.bind(thisArg || self); - } else if (_.isObject(val)) { - classUtils.bindAll(val, exclude, self); - } - } - } - return self; - }, -}; diff --git a/packages/0x.js/src/utils/constants.ts b/packages/0x.js/src/utils/constants.ts index 3de3f5bc1..06beec8e2 100644 --- a/packages/0x.js/src/utils/constants.ts +++ b/packages/0x.js/src/utils/constants.ts @@ -1,4 +1,4 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', @@ -6,6 +6,7 @@ export const constants = { MAX_DIGITS_IN_UNSIGNED_256_INT: 78, INVALID_JUMP_PATTERN: 'invalid JUMP at', OUT_OF_GAS_PATTERN: 'out of gas', + INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string', UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), DEFAULT_BLOCK_POLLING_INTERVAL: 1000, }; diff --git a/packages/0x.js/src/utils/decorators.ts b/packages/0x.js/src/utils/decorators.ts index 1760d8872..f774d734e 100644 --- a/packages/0x.js/src/utils/decorators.ts +++ b/packages/0x.js/src/utils/decorators.ts @@ -1,37 +1,91 @@ import * as _ from 'lodash'; -import {AsyncMethod, ZeroExError} from '../types'; +import { AsyncMethod, SyncMethod, ZeroExError } from '../types'; -import {constants} from './constants'; +import { constants } from './constants'; -export const decorators = { - /** - * Source: https://stackoverflow.com/a/29837695/3546986 - */ - contractCallErrorHandler(target: object, - key: string|symbol, - descriptor: TypedPropertyDescriptor<AsyncMethod>, - ): TypedPropertyDescriptor<AsyncMethod> { - const originalMethod = (descriptor.value as AsyncMethod); +type ErrorTransformer = (err: Error) => Error; + +const contractCallErrorTransformer = (error: Error) => { + if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) { + return new Error(ZeroExError.InvalidJump); + } + if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) { + return new Error(ZeroExError.OutOfGas); + } + return error; +}; + +const schemaErrorTransformer = (error: Error) => { + if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) { + const errMsg = + 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; + return new Error(errMsg); + } + return error; +}; + +/** + * Source: https://stackoverflow.com/a/29837695/3546986 + */ +const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { + const asyncErrorHandlingDecorator = ( + target: object, + key: string | symbol, + descriptor: TypedPropertyDescriptor<AsyncMethod>, + ) => { + const originalMethod = descriptor.value as AsyncMethod; // Do not use arrow syntax here. Use a function expression in // order to use the correct value of `this` in this method // tslint:disable-next-line:only-arrow-functions - descriptor.value = async function(...args: any[]) { + descriptor.value = async function(...args: any[]) { try { const result = await originalMethod.apply(this, args); return result; } catch (error) { - if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) { - throw new Error(ZeroExError.InvalidJump); - } - if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) { - throw new Error(ZeroExError.OutOfGas); - } - throw error; + const transformedError = errorTransformer(error); + throw transformedError; } }; return descriptor; - }, + }; + + return asyncErrorHandlingDecorator; +}; + +const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { + const syncErrorHandlingDecorator = ( + target: object, + key: string | symbol, + descriptor: TypedPropertyDescriptor<SyncMethod>, + ) => { + const originalMethod = descriptor.value as SyncMethod; + + // Do not use arrow syntax here. Use a function expression in + // order to use the correct value of `this` in this method + // tslint:disable-next-line:only-arrow-functions + descriptor.value = function(...args: any[]) { + try { + const result = originalMethod.apply(this, args); + return result; + } catch (error) { + const transformedError = errorTransformer(error); + throw transformedError; + } + }; + + return descriptor; + }; + + return syncErrorHandlingDecorator; +}; + +// _.flow(f, g) = f ∘ g +const zeroExErrorTransformer = _.flow(schemaErrorTransformer, contractCallErrorTransformer); + +export const decorators = { + asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer), + syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer), }; diff --git a/packages/0x.js/src/utils/exchange_transfer_simulator.ts b/packages/0x.js/src/utils/exchange_transfer_simulator.ts index 2574bd9ac..662cd210c 100644 --- a/packages/0x.js/src/utils/exchange_transfer_simulator.ts +++ b/packages/0x.js/src/utils/exchange_transfer_simulator.ts @@ -1,9 +1,9 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; -import {BlockParamLiteral, ExchangeContractErrs, TradeSide, TransferType} from '../types'; +import { TokenWrapper } from '../contract_wrappers/token_wrapper'; +import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store'; +import { BlockParamLiteral, ExchangeContractErrs, TradeSide, TransferType } from '../types'; enum FailureReason { Balance = 'balance', @@ -34,16 +34,19 @@ const ERR_MSG_MAPPING = { }; export class ExchangeTransferSimulator { - private store: BalanceAndProxyAllowanceLazyStore; - private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; - private static throwValidationError(failureReason: FailureReason, tradeSide: TradeSide, - transferType: TransferType): never { + private _store: BalanceAndProxyAllowanceLazyStore; + private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; + private static _throwValidationError( + failureReason: FailureReason, + tradeSide: TradeSide, + transferType: TransferType, + ): never { const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType]; throw new Error(errMsg); } constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) { - this.store = new BalanceAndProxyAllowanceLazyStore(token, defaultBlock); - this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + this._store = new BalanceAndProxyAllowanceLazyStore(token, defaultBlock); + this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; } /** * Simulates transferFrom call performed by a proxy @@ -54,36 +57,50 @@ export class ExchangeTransferSimulator { * @param tradeSide Is Maker/Taker transferring * @param transferType Is it a fee payment or a value transfer */ - public async transferFromAsync(tokenAddress: string, from: string, to: string, - amountInBaseUnits: BigNumber, tradeSide: TradeSide, - transferType: TransferType): Promise<void> { - const balance = await this.store.getBalanceAsync(tokenAddress, from); - const proxyAllowance = await this.store.getProxyAllowanceAsync(tokenAddress, from); + public async transferFromAsync( + tokenAddress: string, + from: string, + to: string, + amountInBaseUnits: BigNumber, + tradeSide: TradeSide, + transferType: TransferType, + ): Promise<void> { + const balance = await this._store.getBalanceAsync(tokenAddress, from); + const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from); if (proxyAllowance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator.throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); + ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); } if (balance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator.throwValidationError(FailureReason.Balance, tradeSide, transferType); + ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); } - await this.decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits); - await this.decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits); - await this.increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); + await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits); + await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits); + await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); } - private async decreaseProxyAllowanceAsync(tokenAddress: string, userAddress: string, - amountInBaseUnits: BigNumber): Promise<void> { - const proxyAllowance = await this.store.getProxyAllowanceAsync(tokenAddress, userAddress); - if (!proxyAllowance.eq(this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { - this.store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits)); + private async _decreaseProxyAllowanceAsync( + tokenAddress: string, + userAddress: string, + amountInBaseUnits: BigNumber, + ): Promise<void> { + const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress); + if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { + this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits)); } } - private async increaseBalanceAsync(tokenAddress: string, userAddress: string, - amountInBaseUnits: BigNumber): Promise<void> { - const balance = await this.store.getBalanceAsync(tokenAddress, userAddress); - this.store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits)); + private async _increaseBalanceAsync( + tokenAddress: string, + userAddress: string, + amountInBaseUnits: BigNumber, + ): Promise<void> { + const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); + this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits)); } - private async decreaseBalanceAsync(tokenAddress: string, userAddress: string, - amountInBaseUnits: BigNumber): Promise<void> { - const balance = await this.store.getBalanceAsync(tokenAddress, userAddress); - this.store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits)); + private async _decreaseBalanceAsync( + tokenAddress: string, + userAddress: string, + amountInBaseUnits: BigNumber, + ): Promise<void> { + const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); + this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits)); } } diff --git a/packages/0x.js/src/utils/filter_utils.ts b/packages/0x.js/src/utils/filter_utils.ts index 57c3ee71c..97205ace3 100644 --- a/packages/0x.js/src/utils/filter_utils.ts +++ b/packages/0x.js/src/utils/filter_utils.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import * as uuid from 'uuid/v4'; import * as Web3 from 'web3'; -import {ContractEvents, IndexedFilterValues, SubscriptionOpts} from '../types'; +import { BlockRange, ContractEvents, IndexedFilterValues } from '../types'; const TOPIC_LENGTH = 32; @@ -12,10 +12,14 @@ export const filterUtils = { generateUUID(): string { return uuid(); }, - getFilter(address: string, eventName: ContractEvents, - indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi, - subscriptionOpts?: SubscriptionOpts): Web3.FilterObject { - const eventAbi = _.find(abi, {name: eventName}) as Web3.EventAbi; + getFilter( + address: string, + eventName: ContractEvents, + indexFilterValues: IndexedFilterValues, + abi: Web3.ContractAbi, + blockRange?: BlockRange, + ): Web3.FilterObject { + const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi; const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName); const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature)); const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues); @@ -24,9 +28,9 @@ export const filterUtils = { address, topics, }; - if (!_.isUndefined(subscriptionOpts)) { + if (!_.isUndefined(blockRange)) { filter = { - ...subscriptionOpts, + ...blockRange, ...filter, }; } @@ -37,8 +41,8 @@ export const filterUtils = { const signature = `${eventAbi.name}(${types.join(',')})`; return signature; }, - getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string|null> { - const topics: Array<string|null> = []; + getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> { + const topics: Array<string | null> = []; for (const eventInput of abi.inputs) { if (!eventInput.indexed) { continue; @@ -65,12 +69,12 @@ export const filterUtils = { } return true; }, - matchesTopics(logTopics: string[], filterTopics: Array<string[]|string|null>): boolean { + matchesTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean { const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils)); const matchesTopics = _.every(matchesTopic); return matchesTopics; }, - matchesTopic(logTopic: string, filterTopic: string[]|string|null): boolean { + matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean { if (_.isArray(filterTopic)) { return _.includes(filterTopic, logTopic); } diff --git a/packages/0x.js/src/utils/interval_utils.ts b/packages/0x.js/src/utils/interval_utils.ts deleted file mode 100644 index 62b79f2f5..000000000 --- a/packages/0x.js/src/utils/interval_utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as _ from 'lodash'; - -export const intervalUtils = { - setAsyncExcludingInterval(fn: () => Promise<void>, intervalMs: number) { - let locked = false; - const intervalId = setInterval(async () => { - if (locked) { - return; - } else { - locked = true; - await fn(); - locked = false; - } - }, intervalMs); - return intervalId; - }, - clearAsyncExcludingInterval(intervalId: NodeJS.Timer): void { - clearInterval(intervalId); - }, -}; diff --git a/packages/0x.js/src/utils/order_state_utils.ts b/packages/0x.js/src/utils/order_state_utils.ts index 6b7f811ae..b7a55ff42 100644 --- a/packages/0x.js/src/utils/order_state_utils.ts +++ b/packages/0x.js/src/utils/order_state_utils.ts @@ -1,31 +1,26 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; -import {ZeroEx} from '../0x'; -import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {RemainingFillableCalculator} from '../order_watcher/remaining_fillable_calculator'; -import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; -import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store'; +import { ZeroEx } from '../0x'; +import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; +import { RemainingFillableCalculator } from '../order_watcher/remaining_fillable_calculator'; +import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store'; +import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store'; import { ExchangeContractErrs, - MethodOpts, OrderRelevantState, OrderState, OrderStateInvalid, OrderStateValid, SignedOrder, } from '../types'; -import {constants} from '../utils/constants'; -import {utils} from '../utils/utils'; const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001; export class OrderStateUtils { - private balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; - private orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; - private static validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { + private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; + private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; + private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add( orderRelevantState.filledTakerTokenAmount, ); @@ -49,23 +44,28 @@ export class OrderStateUtils { } } const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount - .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) - .dividedBy(signedOrder.makerTokenAmount); - if (orderRelevantState.remainingFillableTakerTokenAmount - .lessThan(minFillableTakerTokenAmountWithinNoRoundingErrorRange)) { + .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) + .dividedBy(signedOrder.makerTokenAmount); + if ( + orderRelevantState.remainingFillableTakerTokenAmount.lessThan( + minFillableTakerTokenAmountWithinNoRoundingErrorRange, + ) + ) { throw new Error(ExchangeContractErrs.OrderFillRoundingError); } } - constructor(balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore, - orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore) { - this.balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore; - this.orderFilledCancelledLazyStore = orderFilledCancelledLazyStore; + constructor( + balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore, + orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore, + ) { + this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore; + this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore; } public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> { const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder); try { - OrderStateUtils.validateIfOrderIsValid(signedOrder, orderRelevantState); + OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState); const orderState: OrderStateValid = { isValid: true, orderHash, @@ -86,40 +86,47 @@ export class OrderStateUtils { // If we pass it from the instantiator - there is no opportunity to get it there // because JS doesn't support async constructors. // Moreover - it's cached under the hood so it's equivalent to an async constructor. - const exchange = (this.orderFilledCancelledLazyStore as any).exchange as ExchangeWrapper; + const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper; const zrxTokenAddress = exchange.getZRXTokenAddress(); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - const makerBalance = await this.balanceAndProxyAllowanceLazyStore.getBalanceAsync( - signedOrder.makerTokenAddress, signedOrder.maker, + const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( + signedOrder.makerTokenAddress, + signedOrder.maker, ); - const makerProxyAllowance = await this.balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( - signedOrder.makerTokenAddress, signedOrder.maker, + const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( + signedOrder.makerTokenAddress, + signedOrder.maker, ); - const makerFeeBalance = await this.balanceAndProxyAllowanceLazyStore.getBalanceAsync( - zrxTokenAddress, signedOrder.maker, + const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( + zrxTokenAddress, + signedOrder.maker, ); - const makerFeeProxyAllowance = await this.balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( - zrxTokenAddress, signedOrder.maker, + const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( + zrxTokenAddress, + signedOrder.maker, ); - const filledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash); - const cancelledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getCancelledTakerAmountAsync( + const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash); + const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync( orderHash, ); const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash); const totalMakerTokenAmount = signedOrder.makerTokenAmount; const totalTakerTokenAmount = signedOrder.takerTokenAmount; const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount); - const remainingMakerTokenAmount = remainingTakerTokenAmount.times(totalMakerTokenAmount) - .dividedToIntegerBy(totalTakerTokenAmount); + const remainingMakerTokenAmount = remainingTakerTokenAmount + .times(totalMakerTokenAmount) + .dividedToIntegerBy(totalTakerTokenAmount); const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]); const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]); const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress; - const remainingFillableCalculator = new RemainingFillableCalculator(signedOrder, - isMakerTokenZRX, - transferrableMakerTokenAmount, - transferrableFeeTokenAmount, - remainingMakerTokenAmount); + const remainingFillableCalculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableFeeTokenAmount, + remainingMakerTokenAmount, + ); const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable(); const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable(); const orderRelevantState = { diff --git a/packages/0x.js/src/utils/order_validation_utils.ts b/packages/0x.js/src/utils/order_validation_utils.ts index d514483e0..917d414c8 100644 --- a/packages/0x.js/src/utils/order_validation_utils.ts +++ b/packages/0x.js/src/utils/order_validation_utils.ts @@ -1,20 +1,20 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import {ZeroEx} from '../0x'; -import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {ExchangeContractErrs, Order, SignedOrder, TradeSide, TransferType, ZeroExError} from '../types'; -import {constants} from '../utils/constants'; -import {utils} from '../utils/utils'; +import { ZeroEx } from '../0x'; +import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; +import { ExchangeContractErrs, Order, SignedOrder, TradeSide, TransferType, ZeroExError } from '../types'; +import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; -import {ExchangeTransferSimulator} from './exchange_transfer_simulator'; +import { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export class OrderValidationUtils { - private tokenWrapper: TokenWrapper; - private exchangeWrapper: ExchangeWrapper; + private _exchangeWrapper: ExchangeWrapper; public static validateCancelOrderThrowIfInvalid( - order: Order, cancelTakerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber, + order: Order, + cancelTakerTokenAmount: BigNumber, + unavailableTakerTokenAmount: BigNumber, ): void { if (cancelTakerTokenAmount.eq(0)) { throw new Error(ExchangeContractErrs.OrderCancelAmountZero); @@ -28,102 +28,135 @@ export class OrderValidationUtils { } } public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, senderAddress: string, zrxTokenAddress: string, + exchangeTradeEmulator: ExchangeTransferSimulator, + signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber, + senderAddress: string, + zrxTokenAddress: string, ): Promise<void> { - const fillMakerTokenAmount = OrderValidationUtils.getPartialAmount( + const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount, ); await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, signedOrder.maker, senderAddress, fillMakerTokenAmount, - TradeSide.Maker, TransferType.Trade, + signedOrder.makerTokenAddress, + signedOrder.maker, + senderAddress, + fillMakerTokenAmount, + TradeSide.Maker, + TransferType.Trade, ); await exchangeTradeEmulator.transferFromAsync( - signedOrder.takerTokenAddress, senderAddress, signedOrder.maker, fillTakerTokenAmount, - TradeSide.Taker, TransferType.Trade, + signedOrder.takerTokenAddress, + senderAddress, + signedOrder.maker, + fillTakerTokenAmount, + TradeSide.Taker, + TransferType.Trade, ); - const makerFeeAmount = OrderValidationUtils.getPartialAmount( + const makerFeeAmount = OrderValidationUtils._getPartialAmount( fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerFee, ); await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, TradeSide.Maker, + zrxTokenAddress, + signedOrder.maker, + signedOrder.feeRecipient, + makerFeeAmount, + TradeSide.Maker, TransferType.Fee, ); - const takerFeeAmount = OrderValidationUtils.getPartialAmount( + const takerFeeAmount = OrderValidationUtils._getPartialAmount( fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.takerFee, ); await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, senderAddress, signedOrder.feeRecipient, takerFeeAmount, TradeSide.Taker, + zrxTokenAddress, + senderAddress, + signedOrder.feeRecipient, + takerFeeAmount, + TradeSide.Taker, TransferType.Fee, ); } - private static validateRemainingFillAmountNotZeroOrThrow( - takerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber, + private static _validateRemainingFillAmountNotZeroOrThrow( + takerTokenAmount: BigNumber, + unavailableTakerTokenAmount: BigNumber, ) { if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); } } - private static validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) { + private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) { const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { throw new Error(ExchangeContractErrs.OrderFillExpired); } } - private static getPartialAmount(numerator: BigNumber, denominator: BigNumber, - target: BigNumber): BigNumber { + private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { const fillMakerTokenAmount = numerator - .mul(target) - .div(denominator) - .round(0); + .mul(target) + .div(denominator) + .round(0); return fillMakerTokenAmount; } - constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) { - this.tokenWrapper = tokenWrapper; - this.exchangeWrapper = exchangeWrapper; + constructor(exchangeWrapper: ExchangeWrapper) { + this._exchangeWrapper = exchangeWrapper; } public async validateOrderFillableOrThrowAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, zrxTokenAddress: string, - expectedFillTakerTokenAmount?: BigNumber): Promise<void> { + exchangeTradeEmulator: ExchangeTransferSimulator, + signedOrder: SignedOrder, + zrxTokenAddress: string, + expectedFillTakerTokenAmount?: BigNumber, + ): Promise<void> { const orderHash = utils.getOrderHashHex(signedOrder); - const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, unavailableTakerTokenAmount, + const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); + OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( + signedOrder.takerTokenAmount, + unavailableTakerTokenAmount, ); - OrderValidationUtils.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); + OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); if (!_.isUndefined(expectedFillTakerTokenAmount)) { fillTakerTokenAmount = expectedFillTakerTokenAmount; } - const fillMakerTokenAmount = OrderValidationUtils.getPartialAmount( + const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount, ); await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.taker, fillMakerTokenAmount, - TradeSide.Maker, TransferType.Trade, - ); - const makerFeeAmount = OrderValidationUtils.getPartialAmount( + signedOrder.makerTokenAddress, + signedOrder.maker, + signedOrder.taker, + fillMakerTokenAmount, + TradeSide.Maker, + TransferType.Trade, + ); + const makerFeeAmount = OrderValidationUtils._getPartialAmount( fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerFee, ); await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, - TradeSide.Maker, TransferType.Fee, + zrxTokenAddress, + signedOrder.maker, + signedOrder.feeRecipient, + makerFeeAmount, + TradeSide.Maker, + TransferType.Fee, ); } public async validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, takerAddress: string, - zrxTokenAddress: string): Promise<BigNumber> { + exchangeTradeEmulator: ExchangeTransferSimulator, + signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber, + takerAddress: string, + zrxTokenAddress: string, + ): Promise<BigNumber> { if (fillTakerTokenAmount.eq(0)) { throw new Error(ExchangeContractErrs.OrderFillAmountZero); } @@ -131,24 +164,31 @@ export class OrderValidationUtils { if (!ZeroEx.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) { throw new Error(ZeroExError.InvalidSignature); } - const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, unavailableTakerTokenAmount, + const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); + OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( + signedOrder.takerTokenAmount, + unavailableTakerTokenAmount, ); if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); } - OrderValidationUtils.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); + OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) ? - remainingTakerTokenAmount : - fillTakerTokenAmount; + const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) + ? remainingTakerTokenAmount + : fillTakerTokenAmount; await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator, signedOrder, filledTakerTokenAmount, takerAddress, zrxTokenAddress, + exchangeTradeEmulator, + signedOrder, + filledTakerTokenAmount, + takerAddress, + zrxTokenAddress, ); - const wouldRoundingErrorOccur = await this.exchangeWrapper.isRoundingErrorAsync( - filledTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount, + const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync( + filledTakerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerTokenAmount, ); if (wouldRoundingErrorOccur) { throw new Error(ExchangeContractErrs.OrderFillRoundingError); @@ -156,10 +196,18 @@ export class OrderValidationUtils { return filledTakerTokenAmount; } public async validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, takerAddress: string, zrxTokenAddress: string): Promise<void> { + exchangeTradeEmulator: ExchangeTransferSimulator, + signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber, + takerAddress: string, + zrxTokenAddress: string, + ): Promise<void> { const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, + exchangeTradeEmulator, + signedOrder, + fillTakerTokenAmount, + takerAddress, + zrxTokenAddress, ); if (filledTakerTokenAmount !== fillTakerTokenAmount) { throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); diff --git a/packages/0x.js/src/utils/signature_utils.ts b/packages/0x.js/src/utils/signature_utils.ts index aaf04e7b0..b0f1d61ef 100644 --- a/packages/0x.js/src/utils/signature_utils.ts +++ b/packages/0x.js/src/utils/signature_utils.ts @@ -1,6 +1,6 @@ import * as ethUtil from 'ethereumjs-util'; -import {ECSignature} from '../types'; +import { ECSignature } from '../types'; export const signatureUtils = { isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean { @@ -11,7 +11,8 @@ export const signatureUtils = { msgHashBuff, signature.v, ethUtil.toBuffer(signature.r), - ethUtil.toBuffer(signature.s)); + ethUtil.toBuffer(signature.s), + ); const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); return retrievedAddress === signerAddress; } catch (err) { @@ -34,7 +35,7 @@ export const signatureUtils = { return ecSignature; }, parseSignatureHexAsRSV(signatureHex: string): ECSignature { - const {v, r, s} = ethUtil.fromRpcSig(signatureHex); + const { v, r, s } = ethUtil.fromRpcSig(signatureHex); const ecSignature: ECSignature = { v, r: ethUtil.bufferToHex(r), diff --git a/packages/0x.js/src/utils/utils.ts b/packages/0x.js/src/utils/utils.ts index 04ae34aac..e42a1a853 100644 --- a/packages/0x.js/src/utils/utils.ts +++ b/packages/0x.js/src/utils/utils.ts @@ -1,10 +1,10 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import BN = require('bn.js'); import * as ethABI from 'ethereumjs-abi'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import {Order, SignedOrder, SolidityTypes} from '../types'; +import { Order, SignedOrder, SolidityTypes } from '../types'; export const utils = { /** @@ -29,20 +29,35 @@ export const utils = { spawnSwitchErr(name: string, value: any): Error { return new Error(`Unexpected switch value: ${value} encountered for ${name}`); }, - getOrderHashHex(order: Order|SignedOrder): string { + getOrderHashHex(order: Order | SignedOrder): string { const orderParts = [ - {value: order.exchangeContractAddress, type: SolidityTypes.Address}, - {value: order.maker, type: SolidityTypes.Address}, - {value: order.taker, type: SolidityTypes.Address}, - {value: order.makerTokenAddress, type: SolidityTypes.Address}, - {value: order.takerTokenAddress, type: SolidityTypes.Address}, - {value: order.feeRecipient, type: SolidityTypes.Address}, - {value: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.Uint256}, - {value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.Uint256}, - {value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.Uint256}, - {value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.Uint256}, - {value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.Uint256}, - {value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256}, + { value: order.exchangeContractAddress, type: SolidityTypes.Address }, + { value: order.maker, type: SolidityTypes.Address }, + { value: order.taker, type: SolidityTypes.Address }, + { value: order.makerTokenAddress, type: SolidityTypes.Address }, + { value: order.takerTokenAddress, type: SolidityTypes.Address }, + { value: order.feeRecipient, type: SolidityTypes.Address }, + { + value: utils.bigNumberToBN(order.makerTokenAmount), + type: SolidityTypes.Uint256, + }, + { + value: utils.bigNumberToBN(order.takerTokenAmount), + type: SolidityTypes.Uint256, + }, + { + value: utils.bigNumberToBN(order.makerFee), + type: SolidityTypes.Uint256, + }, + { + value: utils.bigNumberToBN(order.takerFee), + type: SolidityTypes.Uint256, + }, + { + value: utils.bigNumberToBN(order.expirationUnixTimestampSec), + type: SolidityTypes.Uint256, + }, + { value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256 }, ]; const types = _.map(orderParts, o => o.type); const values = _.map(orderParts, o => o.value); |