diff options
author | Fabio Berger <me@fabioberger.com> | 2018-01-30 20:26:46 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-01-30 20:26:46 +0800 |
commit | c6dece6bd1e5f5afa56b290557eb7a6245c76cb6 (patch) | |
tree | ad7a33ffe5d80c0eb41ae10fbc8314f193e52383 /packages/0x.js/src/utils | |
parent | 93a5b3f457c1211676296840c285759007a55500 (diff) | |
download | dexon-0x-contracts-c6dece6bd1e5f5afa56b290557eb7a6245c76cb6.tar.gz dexon-0x-contracts-c6dece6bd1e5f5afa56b290557eb7a6245c76cb6.tar.zst dexon-0x-contracts-c6dece6bd1e5f5afa56b290557eb7a6245c76cb6.zip |
Add config file specifically in prettier command and fix files
Diffstat (limited to 'packages/0x.js/src/utils')
-rw-r--r-- | packages/0x.js/src/utils/abi_decoder.ts | 118 | ||||
-rw-r--r-- | packages/0x.js/src/utils/assert.ts | 42 | ||||
-rw-r--r-- | packages/0x.js/src/utils/constants.ts | 16 | ||||
-rw-r--r-- | packages/0x.js/src/utils/decorators.ts | 114 | ||||
-rw-r--r-- | packages/0x.js/src/utils/exchange_transfer_simulator.ts | 182 | ||||
-rw-r--r-- | packages/0x.js/src/utils/filter_utils.ts | 150 | ||||
-rw-r--r-- | packages/0x.js/src/utils/order_state_utils.ts | 254 | ||||
-rw-r--r-- | packages/0x.js/src/utils/order_validation_utils.ts | 404 | ||||
-rw-r--r-- | packages/0x.js/src/utils/signature_utils.ts | 80 | ||||
-rw-r--r-- | packages/0x.js/src/utils/utils.ts | 116 |
10 files changed, 738 insertions, 738 deletions
diff --git a/packages/0x.js/src/utils/abi_decoder.ts b/packages/0x.js/src/utils/abi_decoder.ts index ace6dd165..bbd2a0b1d 100644 --- a/packages/0x.js/src/utils/abi_decoder.ts +++ b/packages/0x.js/src/utils/abi_decoder.ts @@ -6,67 +6,67 @@ import * as SolidityCoder from 'web3/lib/solidity/coder'; 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) { - let formatted = address; - if (_.startsWith(formatted, '0x')) { - formatted = formatted.slice(2); - } + 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); + } - formatted = _.padStart(formatted, 40, '0'); - return `0x${formatted}`; - } - constructor(abiArrays: Web3.AbiDefinition[][]) { - _.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 { - const methodId = log.topics[0]; - const event = this._methodIds[methodId]; - if (_.isUndefined(event)) { - return log; - } - const logData = log.data; - const decodedParams: DecodedLogArgs = {}; - let dataIndex = 0; - let topicsIndex = 1; + formatted = _.padStart(formatted, 40, '0'); + return `0x${formatted}`; + } + constructor(abiArrays: Web3.AbiDefinition[][]) { + _.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 { + const methodId = log.topics[0]; + const event = this._methodIds[methodId]; + if (_.isUndefined(event)) { + return log; + } + const logData = log.data; + const decodedParams: DecodedLogArgs = {}; + let dataIndex = 0; + let topicsIndex = 1; - const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed); - const dataTypes = _.map(nonIndexedInputs, input => input.type); - const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length)); + const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed); + const dataTypes = _.map(nonIndexedInputs, input => input.type); + const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length)); - _.map(event.inputs, (param: Web3.EventParameter) => { - // 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 = new BigNumber(value); - } - decodedParams[param.name] = value; - }); + _.map(event.inputs, (param: Web3.EventParameter) => { + // 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 = new BigNumber(value); + } + decodedParams[param.name] = value; + }); - return { - ...log, - event: event.name, - args: decodedParams, - }; - } - 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._savedABIs = this._savedABIs.concat(abiArray); - } + return { + ...log, + event: event.name, + args: decodedParams, + }; + } + 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._savedABIs = this._savedABIs.concat(abiArray); + } } diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts index 776ee7b79..c21f2dbca 100644 --- a/packages/0x.js/src/utils/assert.ts +++ b/packages/0x.js/src/utils/assert.ts @@ -11,25 +11,25 @@ import { ECSignature } from '../types'; import { signatureUtils } from '../utils/signature_utils'; export const assert = { - ...sharedAssert, - isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) { - 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> { - sharedAssert.isETHAddressHex(variableName, senderAddressHex); - const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex); - sharedAssert.assert( - isSenderAddressAvailable, - `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`, - ); - }, - async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> { - const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); - this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider'); - }, + ...sharedAssert, + isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) { + 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> { + sharedAssert.isETHAddressHex(variableName, senderAddressHex); + const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex); + sharedAssert.assert( + isSenderAddressAvailable, + `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`, + ); + }, + async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> { + const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); + this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider'); + }, }; diff --git a/packages/0x.js/src/utils/constants.ts b/packages/0x.js/src/utils/constants.ts index f32847ec2..06beec8e2 100644 --- a/packages/0x.js/src/utils/constants.ts +++ b/packages/0x.js/src/utils/constants.ts @@ -1,12 +1,12 @@ import { BigNumber } from '@0xproject/utils'; export const constants = { - NULL_ADDRESS: '0x0000000000000000000000000000000000000000', - TESTRPC_NETWORK_ID: 50, - 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, + NULL_ADDRESS: '0x0000000000000000000000000000000000000000', + TESTRPC_NETWORK_ID: 50, + 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 c1a022a81..f774d734e 100644 --- a/packages/0x.js/src/utils/decorators.ts +++ b/packages/0x.js/src/utils/decorators.ts @@ -7,85 +7,85 @@ import { constants } from './constants'; 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; + 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; + 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; + 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[]) { - try { - const result = await originalMethod.apply(this, args); - return result; - } catch (error) { - const transformedError = errorTransformer(error); - throw transformedError; - } - }; + // 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[]) { + try { + const result = await originalMethod.apply(this, args); + return result; + } catch (error) { + const transformedError = errorTransformer(error); + throw transformedError; + } + }; - return descriptor; - }; + return descriptor; + }; - return asyncErrorHandlingDecorator; + return asyncErrorHandlingDecorator; }; const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { - const syncErrorHandlingDecorator = ( - target: object, - key: string | symbol, - descriptor: TypedPropertyDescriptor<SyncMethod>, - ) => { - const originalMethod = descriptor.value as SyncMethod; + 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; - } - }; + // 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 descriptor; + }; - return syncErrorHandlingDecorator; + return syncErrorHandlingDecorator; }; // _.flow(f, g) = f ∘ g const zeroExErrorTransformer = _.flow(schemaErrorTransformer, contractCallErrorTransformer); export const decorators = { - asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer), - syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer), + 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 c9a2f87ce..662cd210c 100644 --- a/packages/0x.js/src/utils/exchange_transfer_simulator.ts +++ b/packages/0x.js/src/utils/exchange_transfer_simulator.ts @@ -6,101 +6,101 @@ import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allow import { BlockParamLiteral, ExchangeContractErrs, TradeSide, TransferType } from '../types'; enum FailureReason { - Balance = 'balance', - ProxyAllowance = 'proxyAllowance', + Balance = 'balance', + ProxyAllowance = 'proxyAllowance', } const ERR_MSG_MAPPING = { - [FailureReason.Balance]: { - [TradeSide.Maker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance, - }, - [TradeSide.Taker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance, - }, - }, - [FailureReason.ProxyAllowance]: { - [TradeSide.Maker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance, - }, - [TradeSide.Taker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance, - }, - }, + [FailureReason.Balance]: { + [TradeSide.Maker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance, + }, + [TradeSide.Taker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance, + }, + }, + [FailureReason.ProxyAllowance]: { + [TradeSide.Maker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance, + }, + [TradeSide.Taker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance, + }, + }, }; export class ExchangeTransferSimulator { - 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; - } - /** - * Simulates transferFrom call performed by a proxy - * @param tokenAddress Address of the token to be transferred - * @param from Owner of the transferred tokens - * @param to Recipient of the transferred tokens - * @param amountInBaseUnits The amount of tokens being transferred - * @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); - if (proxyAllowance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); - } - if (balance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); - } - 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 _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 _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; + } + /** + * Simulates transferFrom call performed by a proxy + * @param tokenAddress Address of the token to be transferred + * @param from Owner of the transferred tokens + * @param to Recipient of the transferred tokens + * @param amountInBaseUnits The amount of tokens being transferred + * @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); + if (proxyAllowance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); + } + if (balance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); + } + 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 _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)); + } } diff --git a/packages/0x.js/src/utils/filter_utils.ts b/packages/0x.js/src/utils/filter_utils.ts index c34ae8893..97205ace3 100644 --- a/packages/0x.js/src/utils/filter_utils.ts +++ b/packages/0x.js/src/utils/filter_utils.ts @@ -9,79 +9,79 @@ import { BlockRange, ContractEvents, IndexedFilterValues } from '../types'; const TOPIC_LENGTH = 32; export const filterUtils = { - generateUUID(): string { - return uuid(); - }, - 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); - const topics = [topicForEventSignature, ...topicsForIndexedArgs]; - let filter: Web3.FilterObject = { - address, - topics, - }; - if (!_.isUndefined(blockRange)) { - filter = { - ...blockRange, - ...filter, - }; - } - return filter; - }, - getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string { - const types = _.map(eventAbi.inputs, 'type'); - const signature = `${eventAbi.name}(${types.join(',')})`; - return signature; - }, - getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> { - const topics: Array<string | null> = []; - for (const eventInput of abi.inputs) { - if (!eventInput.indexed) { - continue; - } - if (_.isUndefined(indexFilterValues[eventInput.name])) { - // Null is a wildcard topic in a JSON-RPC call - topics.push(null); - } else { - const value = indexFilterValues[eventInput.name] as string; - const buffer = ethUtil.toBuffer(value); - const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH); - const topic = ethUtil.bufferToHex(paddedBuffer); - topics.push(topic); - } - } - return topics; - }, - matchesFilter(log: Web3.LogEntry, filter: Web3.FilterObject): boolean { - if (!_.isUndefined(filter.address) && log.address !== filter.address) { - return false; - } - if (!_.isUndefined(filter.topics)) { - return filterUtils.matchesTopics(log.topics, filter.topics); - } - return true; - }, - 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 { - if (_.isArray(filterTopic)) { - return _.includes(filterTopic, logTopic); - } - if (_.isString(filterTopic)) { - return filterTopic === logTopic; - } - // null topic is a wildcard - return true; - }, + generateUUID(): string { + return uuid(); + }, + 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); + const topics = [topicForEventSignature, ...topicsForIndexedArgs]; + let filter: Web3.FilterObject = { + address, + topics, + }; + if (!_.isUndefined(blockRange)) { + filter = { + ...blockRange, + ...filter, + }; + } + return filter; + }, + getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string { + const types = _.map(eventAbi.inputs, 'type'); + const signature = `${eventAbi.name}(${types.join(',')})`; + return signature; + }, + getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> { + const topics: Array<string | null> = []; + for (const eventInput of abi.inputs) { + if (!eventInput.indexed) { + continue; + } + if (_.isUndefined(indexFilterValues[eventInput.name])) { + // Null is a wildcard topic in a JSON-RPC call + topics.push(null); + } else { + const value = indexFilterValues[eventInput.name] as string; + const buffer = ethUtil.toBuffer(value); + const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH); + const topic = ethUtil.bufferToHex(paddedBuffer); + topics.push(topic); + } + } + return topics; + }, + matchesFilter(log: Web3.LogEntry, filter: Web3.FilterObject): boolean { + if (!_.isUndefined(filter.address) && log.address !== filter.address) { + return false; + } + if (!_.isUndefined(filter.topics)) { + return filterUtils.matchesTopics(log.topics, filter.topics); + } + return true; + }, + 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 { + if (_.isArray(filterTopic)) { + return _.includes(filterTopic, logTopic); + } + if (_.isString(filterTopic)) { + return filterTopic === logTopic; + } + // null topic is a wildcard + return true; + }, }; diff --git a/packages/0x.js/src/utils/order_state_utils.ts b/packages/0x.js/src/utils/order_state_utils.ts index 04d3b641e..b7a55ff42 100644 --- a/packages/0x.js/src/utils/order_state_utils.ts +++ b/packages/0x.js/src/utils/order_state_utils.ts @@ -7,138 +7,138 @@ import { RemainingFillableCalculator } from '../order_watcher/remaining_fillable import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store'; import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store'; import { - ExchangeContractErrs, - OrderRelevantState, - OrderState, - OrderStateInvalid, - OrderStateValid, - SignedOrder, + ExchangeContractErrs, + OrderRelevantState, + OrderState, + OrderStateInvalid, + OrderStateValid, + SignedOrder, } from '../types'; const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001; export class OrderStateUtils { - private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; - private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; - private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { - const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add( - orderRelevantState.filledTakerTokenAmount, - ); - const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - if (availableTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); - } + private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; + private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; + private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { + const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add( + orderRelevantState.filledTakerTokenAmount, + ); + const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); + if (availableTakerTokenAmount.eq(0)) { + throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); + } - if (orderRelevantState.makerBalance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerBalance); - } - if (orderRelevantState.makerProxyAllowance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerAllowance); - } - if (!signedOrder.makerFee.eq(0)) { - if (orderRelevantState.makerFeeBalance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance); - } - if (orderRelevantState.makerFeeProxyAllowance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance); - } - } - const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount - .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; - } - public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> { - const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder); - const orderHash = ZeroEx.getOrderHashHex(signedOrder); - try { - OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState); - const orderState: OrderStateValid = { - isValid: true, - orderHash, - orderRelevantState, - }; - return orderState; - } catch (err) { - const orderState: OrderStateInvalid = { - isValid: false, - orderHash, - error: err.message, - }; - return orderState; - } - } - public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> { - // HACK: We access the private property here but otherwise the interface will be less nice. - // 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 zrxTokenAddress = exchange.getZRXTokenAddress(); - const orderHash = ZeroEx.getOrderHashHex(signedOrder); - const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, - ); - const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, - ); - const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( - 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( - 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 transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]); - const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]); + if (orderRelevantState.makerBalance.eq(0)) { + throw new Error(ExchangeContractErrs.InsufficientMakerBalance); + } + if (orderRelevantState.makerProxyAllowance.eq(0)) { + throw new Error(ExchangeContractErrs.InsufficientMakerAllowance); + } + if (!signedOrder.makerFee.eq(0)) { + if (orderRelevantState.makerFeeBalance.eq(0)) { + throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance); + } + if (orderRelevantState.makerFeeProxyAllowance.eq(0)) { + throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance); + } + } + const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount + .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; + } + public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> { + const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + try { + OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState); + const orderState: OrderStateValid = { + isValid: true, + orderHash, + orderRelevantState, + }; + return orderState; + } catch (err) { + const orderState: OrderStateInvalid = { + isValid: false, + orderHash, + error: err.message, + }; + return orderState; + } + } + public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> { + // HACK: We access the private property here but otherwise the interface will be less nice. + // 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 zrxTokenAddress = exchange.getZRXTokenAddress(); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( + signedOrder.makerTokenAddress, + signedOrder.maker, + ); + const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( + signedOrder.makerTokenAddress, + signedOrder.maker, + ); + const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( + 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( + 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 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 remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable(); - const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable(); - const orderRelevantState = { - makerBalance, - makerProxyAllowance, - makerFeeBalance, - makerFeeProxyAllowance, - filledTakerTokenAmount, - cancelledTakerTokenAmount, - remainingFillableMakerTokenAmount, - remainingFillableTakerTokenAmount, - }; - return orderRelevantState; - } + const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress; + const remainingFillableCalculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableFeeTokenAmount, + remainingMakerTokenAmount, + ); + const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable(); + const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable(); + const orderRelevantState = { + makerBalance, + makerProxyAllowance, + makerFeeBalance, + makerFeeProxyAllowance, + filledTakerTokenAmount, + cancelledTakerTokenAmount, + remainingFillableMakerTokenAmount, + remainingFillableTakerTokenAmount, + }; + return orderRelevantState; + } } diff --git a/packages/0x.js/src/utils/order_validation_utils.ts b/packages/0x.js/src/utils/order_validation_utils.ts index 8ff22266f..917d414c8 100644 --- a/packages/0x.js/src/utils/order_validation_utils.ts +++ b/packages/0x.js/src/utils/order_validation_utils.ts @@ -10,207 +10,207 @@ import { utils } from '../utils/utils'; import { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export class OrderValidationUtils { - private _exchangeWrapper: ExchangeWrapper; - public static validateCancelOrderThrowIfInvalid( - order: Order, - cancelTakerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ): void { - if (cancelTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderCancelAmountZero); - } - if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderCancelExpired); - } - } - public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - senderAddress: string, - zrxTokenAddress: string, - ): Promise<void> { - const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, - senderAddress, - fillMakerTokenAmount, - TradeSide.Maker, - TransferType.Trade, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.takerTokenAddress, - senderAddress, - signedOrder.maker, - fillTakerTokenAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const makerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, - signedOrder.maker, - signedOrder.feeRecipient, - makerFeeAmount, - TradeSide.Maker, - TransferType.Fee, - ); - const takerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.takerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, - senderAddress, - signedOrder.feeRecipient, - takerFeeAmount, - TradeSide.Taker, - TransferType.Fee, - ); - } - private static _validateRemainingFillAmountNotZeroOrThrow( - takerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ) { - if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); - } - } - 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 { - const fillMakerTokenAmount = numerator - .mul(target) - .div(denominator) - .round(0); - return fillMakerTokenAmount; - } - constructor(exchangeWrapper: ExchangeWrapper) { - this._exchangeWrapper = exchangeWrapper; - } - public async validateOrderFillableOrThrowAsync( - 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, - ); - OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - if (!_.isUndefined(expectedFillTakerTokenAmount)) { - fillTakerTokenAmount = expectedFillTakerTokenAmount; - } - 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( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - 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> { - if (fillTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderFillAmountZero); - } - const orderHash = utils.getOrderHashHex(signedOrder); - 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, - ); - if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { - throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); - } - OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) - ? remainingTakerTokenAmount - : fillTakerTokenAmount; - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - filledTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); + private _exchangeWrapper: ExchangeWrapper; + public static validateCancelOrderThrowIfInvalid( + order: Order, + cancelTakerTokenAmount: BigNumber, + unavailableTakerTokenAmount: BigNumber, + ): void { + if (cancelTakerTokenAmount.eq(0)) { + throw new Error(ExchangeContractErrs.OrderCancelAmountZero); + } + if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) { + throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); + } + const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); + if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { + throw new Error(ExchangeContractErrs.OrderCancelExpired); + } + } + public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( + exchangeTradeEmulator: ExchangeTransferSimulator, + signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber, + senderAddress: string, + zrxTokenAddress: string, + ): Promise<void> { + const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( + fillTakerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerTokenAmount, + ); + await exchangeTradeEmulator.transferFromAsync( + signedOrder.makerTokenAddress, + signedOrder.maker, + senderAddress, + fillMakerTokenAmount, + TradeSide.Maker, + TransferType.Trade, + ); + await exchangeTradeEmulator.transferFromAsync( + signedOrder.takerTokenAddress, + senderAddress, + signedOrder.maker, + fillTakerTokenAmount, + TradeSide.Taker, + TransferType.Trade, + ); + const makerFeeAmount = OrderValidationUtils._getPartialAmount( + fillTakerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerFee, + ); + await exchangeTradeEmulator.transferFromAsync( + zrxTokenAddress, + signedOrder.maker, + signedOrder.feeRecipient, + makerFeeAmount, + TradeSide.Maker, + TransferType.Fee, + ); + const takerFeeAmount = OrderValidationUtils._getPartialAmount( + fillTakerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.takerFee, + ); + await exchangeTradeEmulator.transferFromAsync( + zrxTokenAddress, + senderAddress, + signedOrder.feeRecipient, + takerFeeAmount, + TradeSide.Taker, + TransferType.Fee, + ); + } + private static _validateRemainingFillAmountNotZeroOrThrow( + takerTokenAmount: BigNumber, + unavailableTakerTokenAmount: BigNumber, + ) { + if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { + throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); + } + } + 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 { + const fillMakerTokenAmount = numerator + .mul(target) + .div(denominator) + .round(0); + return fillMakerTokenAmount; + } + constructor(exchangeWrapper: ExchangeWrapper) { + this._exchangeWrapper = exchangeWrapper; + } + public async validateOrderFillableOrThrowAsync( + 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, + ); + OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); + let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); + if (!_.isUndefined(expectedFillTakerTokenAmount)) { + fillTakerTokenAmount = expectedFillTakerTokenAmount; + } + 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( + fillTakerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerFee, + ); + await exchangeTradeEmulator.transferFromAsync( + 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> { + if (fillTakerTokenAmount.eq(0)) { + throw new Error(ExchangeContractErrs.OrderFillAmountZero); + } + const orderHash = utils.getOrderHashHex(signedOrder); + 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, + ); + if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { + throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); + } + OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); + const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); + const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) + ? remainingTakerTokenAmount + : fillTakerTokenAmount; + await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( + exchangeTradeEmulator, + signedOrder, + filledTakerTokenAmount, + takerAddress, + zrxTokenAddress, + ); - const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync( - filledTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - if (wouldRoundingErrorOccur) { - throw new Error(ExchangeContractErrs.OrderFillRoundingError); - } - return filledTakerTokenAmount; - } - public async validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - zrxTokenAddress: string, - ): Promise<void> { - const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - if (filledTakerTokenAmount !== fillTakerTokenAmount) { - throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); - } - } + const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync( + filledTakerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerTokenAmount, + ); + if (wouldRoundingErrorOccur) { + throw new Error(ExchangeContractErrs.OrderFillRoundingError); + } + return filledTakerTokenAmount; + } + public async validateFillOrKillOrderThrowIfInvalidAsync( + exchangeTradeEmulator: ExchangeTransferSimulator, + signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber, + takerAddress: string, + zrxTokenAddress: string, + ): Promise<void> { + const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( + 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 19b5c76c7..b0f1d61ef 100644 --- a/packages/0x.js/src/utils/signature_utils.ts +++ b/packages/0x.js/src/utils/signature_utils.ts @@ -3,44 +3,44 @@ import * as ethUtil from 'ethereumjs-util'; import { ECSignature } from '../types'; export const signatureUtils = { - isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean { - const dataBuff = ethUtil.toBuffer(data); - const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff); - try { - const pubKey = ethUtil.ecrecover( - msgHashBuff, - signature.v, - ethUtil.toBuffer(signature.r), - ethUtil.toBuffer(signature.s), - ); - const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); - return retrievedAddress === signerAddress; - } catch (err) { - return false; - } - }, - parseSignatureHexAsVRS(signatureHex: string): ECSignature { - const signatureBuffer = ethUtil.toBuffer(signatureHex); - let v = signatureBuffer[0]; - if (v < 27) { - v += 27; - } - const r = signatureBuffer.slice(1, 33); - const s = signatureBuffer.slice(33, 65); - const ecSignature: ECSignature = { - v, - r: ethUtil.bufferToHex(r), - s: ethUtil.bufferToHex(s), - }; - return ecSignature; - }, - parseSignatureHexAsRSV(signatureHex: string): ECSignature { - const { v, r, s } = ethUtil.fromRpcSig(signatureHex); - const ecSignature: ECSignature = { - v, - r: ethUtil.bufferToHex(r), - s: ethUtil.bufferToHex(s), - }; - return ecSignature; - }, + isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean { + const dataBuff = ethUtil.toBuffer(data); + const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff); + try { + const pubKey = ethUtil.ecrecover( + msgHashBuff, + signature.v, + ethUtil.toBuffer(signature.r), + ethUtil.toBuffer(signature.s), + ); + const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); + return retrievedAddress === signerAddress; + } catch (err) { + return false; + } + }, + parseSignatureHexAsVRS(signatureHex: string): ECSignature { + const signatureBuffer = ethUtil.toBuffer(signatureHex); + let v = signatureBuffer[0]; + if (v < 27) { + v += 27; + } + const r = signatureBuffer.slice(1, 33); + const s = signatureBuffer.slice(33, 65); + const ecSignature: ECSignature = { + v, + r: ethUtil.bufferToHex(r), + s: ethUtil.bufferToHex(s), + }; + return ecSignature; + }, + parseSignatureHexAsRSV(signatureHex: string): ECSignature { + const { v, r, s } = ethUtil.fromRpcSig(signatureHex); + const ecSignature: ECSignature = { + v, + r: ethUtil.bufferToHex(r), + s: ethUtil.bufferToHex(s), + }; + return ecSignature; + }, }; diff --git a/packages/0x.js/src/utils/utils.ts b/packages/0x.js/src/utils/utils.ts index ae07941ef..42cf5d956 100644 --- a/packages/0x.js/src/utils/utils.ts +++ b/packages/0x.js/src/utils/utils.ts @@ -7,62 +7,62 @@ import * as _ from 'lodash'; import { Order, SignedOrder, SolidityTypes } from '../types'; export const utils = { - /** - * Converts BigNumber instance to BN - * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that - * expects values of Solidity type `uint` to be passed as type `BN`. - * We do not use BN anywhere else in the codebase. - */ - bigNumberToBN(value: BigNumber) { - return new BN(value.toString(), 10); - }, - consoleLog(message: string): void { - // tslint:disable-next-line: no-console - console.log(message); - }, - spawnSwitchErr(name: string, value: any): Error { - return new Error(`Unexpected switch value: ${value} encountered for ${name}`); - }, - 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 }, - ]; - const types = _.map(orderParts, o => o.type); - const values = _.map(orderParts, o => o.value); - const hashBuff = ethABI.soliditySHA3(types, values); - const hashHex = ethUtil.bufferToHex(hashBuff); - return hashHex; - }, - getCurrentUnixTimestampSec(): BigNumber { - return new BigNumber(Date.now() / 1000).round(); - }, - getCurrentUnixTimestampMs(): BigNumber { - return new BigNumber(Date.now()); - }, + /** + * Converts BigNumber instance to BN + * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that + * expects values of Solidity type `uint` to be passed as type `BN`. + * We do not use BN anywhere else in the codebase. + */ + bigNumberToBN(value: BigNumber) { + return new BN(value.toString(), 10); + }, + consoleLog(message: string): void { + // tslint:disable-next-line: no-console + console.log(message); + }, + spawnSwitchErr(name: string, value: any): Error { + return new Error(`Unexpected switch value: ${value} encountered for ${name}`); + }, + 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 }, + ]; + const types = _.map(orderParts, o => o.type); + const values = _.map(orderParts, o => o.value); + const hashBuff = ethABI.soliditySHA3(types, values); + const hashHex = ethUtil.bufferToHex(hashBuff); + return hashHex; + }, + getCurrentUnixTimestampSec(): BigNumber { + return new BigNumber(Date.now() / 1000).round(); + }, + getCurrentUnixTimestampMs(): BigNumber { + return new BigNumber(Date.now()); + }, }; |