diff options
author | Fabio Berger <me@fabioberger.com> | 2017-11-13 11:17:18 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2017-11-13 11:17:18 +0800 |
commit | c4ee2d73865a1444c079b9e2836b7630a0adf03e (patch) | |
tree | b9c7794e7022fb189675d914f5fe58dcabd67dec /src/utils | |
parent | a74ec0effa818a86233fe64cb0dad2c61bbb4bb6 (diff) | |
download | dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.gz dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.bz2 dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.lz dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.xz dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.tar.zst dexon-sol-tools-c4ee2d73865a1444c079b9e2836b7630a0adf03e.zip |
Switch over to Lerna + Yarn Workspaces setup for a mono-repo approach
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/abi_decoder.ts | 68 | ||||
-rw-r--r-- | src/utils/assert.ts | 101 | ||||
-rw-r--r-- | src/utils/constants.ts | 11 | ||||
-rw-r--r-- | src/utils/decorators.ts | 35 | ||||
-rw-r--r-- | src/utils/exchange_transfer_simulator.ts | 88 | ||||
-rw-r--r-- | src/utils/filter_utils.ts | 82 | ||||
-rw-r--r-- | src/utils/interval_utils.ts | 20 | ||||
-rw-r--r-- | src/utils/order_state_utils.ts | 119 | ||||
-rw-r--r-- | src/utils/order_validation_utils.ts | 166 | ||||
-rw-r--r-- | src/utils/signature_utils.ts | 44 | ||||
-rw-r--r-- | src/utils/utils.ts | 55 |
11 files changed, 0 insertions, 789 deletions
diff --git a/src/utils/abi_decoder.ts b/src/utils/abi_decoder.ts deleted file mode 100644 index 840ad9be0..000000000 --- a/src/utils/abi_decoder.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as Web3 from 'web3'; -import * as _ from 'lodash'; -import BigNumber from 'bignumber.js'; -import {AbiType, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes, ContractEventArgs} from '../types'; -import * as SolidityCoder from 'web3/lib/solidity/coder'; - -export class AbiDecoder { - private savedABIs: Web3.AbiDefinition[] = []; - private methodIds: {[signatureHash: string]: Web3.EventAbi} = {}; - 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)); - - _.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 = this.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); - } - private padZeros(address: string) { - let formatted = address; - if (_.startsWith(formatted, '0x')) { - formatted = formatted.slice(2); - } - - formatted = _.padStart(formatted, 40, '0'); - return `0x${formatted}`; - } -} diff --git a/src/utils/assert.ts b/src/utils/assert.ts deleted file mode 100644 index e5c9439f3..000000000 --- a/src/utils/assert.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as _ from 'lodash'; -import * as Web3 from 'web3'; -import BigNumber from 'bignumber.js'; -import {SchemaValidator, Schema} from '0x-json-schemas'; -import {Web3Wrapper} from '../web3_wrapper'; -import {signatureUtils} from '../utils/signature_utils'; -import {ECSignature} from '../types'; - -const HEX_REGEX = /^0x[0-9A-F]*$/i; - -export const assert = { - isBigNumber(variableName: string, value: BigNumber): void { - const isBigNumber = _.isObject(value) && (value as any).isBigNumber; - this.assert(isBigNumber, this.typeAssertionMessage(variableName, 'BigNumber', value)); - }, - isValidBaseUnitAmount(variableName: string, value: BigNumber) { - assert.isBigNumber(variableName, value); - const hasDecimals = value.decimalPlaces() !== 0; - this.assert( - !hasDecimals, `${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`, - ); - }, - 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`); - }, - isUndefined(value: any, variableName?: string): void { - this.assert(_.isUndefined(value), this.typeAssertionMessage(variableName, 'undefined', value)); - }, - isString(variableName: string, value: string): void { - this.assert(_.isString(value), this.typeAssertionMessage(variableName, 'string', value)); - }, - isFunction(variableName: string, value: any): void { - this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value)); - }, - isHexString(variableName: string, value: string): void { - this.assert(_.isString(value) && HEX_REGEX.test(value), - this.typeAssertionMessage(variableName, 'HexString', value)); - }, - isETHAddressHex(variableName: string, value: string): void { - const web3 = new Web3(); - this.assert(web3.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value)); - this.assert( - web3.isAddress(value) && value.toLowerCase() === value, - `Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`, - ); - }, - doesBelongToStringEnum(variableName: string, value: string, - stringEnum: any /* There is no base type for every string enum */): void { - const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]); - const enumValues = _.keys(stringEnum); - const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`); - const enumValuesAsString = enumValuesAsStrings.join(', '); - assert.assert( - doesBelongToStringEnum, - `Expected ${variableName} to be one of: ${enumValuesAsString}, encountered: ${value}`, - ); - }, - async isSenderAddressAsync(variableName: string, senderAddressHex: string, - web3Wrapper: Web3Wrapper): Promise<void> { - assert.isETHAddressHex(variableName, senderAddressHex); - const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex); - assert.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'); - }, - hasAtMostOneUniqueValue(value: any[], errMsg: string): void { - this.assert(_.uniq(value).length <= 1, errMsg); - }, - isNumber(variableName: string, value: number): void { - this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value)); - }, - isBoolean(variableName: string, value: boolean): void { - this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value)); - }, - isWeb3Provider(variableName: string, value: Web3.Provider): void { - const isWeb3Provider = _.isFunction((value as any).send) || _.isFunction((value as any).sendAsync); - this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value)); - }, - doesConformToSchema(variableName: string, value: any, schema: Schema): void { - const schemaValidator = new SchemaValidator(); - const validationResult = schemaValidator.validate(value, schema); - const hasValidationErrors = validationResult.errors.length > 0; - const msg = `Expected ${variableName} to conform to schema ${schema.id} -Encountered: ${JSON.stringify(value, null, '\t')} -Validation errors: ${validationResult.errors.join(', ')}`; - this.assert(!hasValidationErrors, msg); - }, - assert(condition: boolean, message: string): void { - if (!condition) { - throw new Error(message); - } - }, - typeAssertionMessage(variableName: string, type: string, value: any): string { - return `Expected ${variableName} to be of type ${type}, encountered: ${value}`; - }, -}; diff --git a/src/utils/constants.ts b/src/utils/constants.ts deleted file mode 100644 index 3de3f5bc1..000000000 --- a/src/utils/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -import BigNumber from 'bignumber.js'; - -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', - UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), - DEFAULT_BLOCK_POLLING_INTERVAL: 1000, -}; diff --git a/src/utils/decorators.ts b/src/utils/decorators.ts deleted file mode 100644 index ec750b891..000000000 --- a/src/utils/decorators.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as _ from 'lodash'; -import {constants} from './constants'; -import {AsyncMethod, ZeroExError} from '../types'; - -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); - - // 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) { - 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; - } - }; - - return descriptor; - }, -}; diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts deleted file mode 100644 index 308ef06db..000000000 --- a/src/utils/exchange_transfer_simulator.ts +++ /dev/null @@ -1,88 +0,0 @@ -import * as _ from 'lodash'; -import BigNumber from 'bignumber.js'; -import {ExchangeContractErrs, TradeSide, TransferType, BlockParamLiteral} from '../types'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; - -enum FailureReason { - 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, - }, - }, -}; - -export class ExchangeTransferSimulator { - private store: BalanceAndProxyAllowanceLazyStore; - private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; - constructor(token: TokenWrapper) { - this.store = new BalanceAndProxyAllowanceLazyStore(token); - 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)) { - this.throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); - } - if (balance.lessThan(amountInBaseUnits)) { - this.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 throwValidationError(failureReason: FailureReason, tradeSide: TradeSide, - transferType: TransferType): Promise<never> { - const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType]; - throw new Error(errMsg); - } -} diff --git a/src/utils/filter_utils.ts b/src/utils/filter_utils.ts deleted file mode 100644 index e09a95a6e..000000000 --- a/src/utils/filter_utils.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as _ from 'lodash'; -import * as Web3 from 'web3'; -import * as uuid from 'uuid/v4'; -import * as ethUtil from 'ethereumjs-util'; -import * as jsSHA3 from 'js-sha3'; -import {ContractEvents, IndexedFilterValues, SubscriptionOpts} from '../types'; - -const TOPIC_LENGTH = 32; - -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; - 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(subscriptionOpts)) { - filter = { - ...subscriptionOpts, - ...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/src/utils/interval_utils.ts b/src/utils/interval_utils.ts deleted file mode 100644 index 62b79f2f5..000000000 --- a/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/src/utils/order_state_utils.ts b/src/utils/order_state_utils.ts deleted file mode 100644 index f82601cae..000000000 --- a/src/utils/order_state_utils.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as _ from 'lodash'; -import * as Web3 from 'web3'; -import BigNumber from 'bignumber.js'; -import { - ExchangeContractErrs, - SignedOrder, - OrderRelevantState, - MethodOpts, - OrderState, - OrderStateValid, - OrderStateInvalid, -} from '../types'; -import {ZeroEx} from '../0x'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {utils} from '../utils/utils'; -import {constants} from '../utils/constants'; -import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store'; -import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; - -export class OrderStateUtils { - private balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; - private 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 { - this.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 = await exchange.getZRXTokenAddressAsync(); - 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 canceledTakerTokenAmount = 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 fillableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]); - const remainingFillableMakerTokenAmount = BigNumber.min(fillableMakerTokenAmount, remainingMakerTokenAmount); - // TODO: Handle edge case where maker token is ZRX with fee - const orderRelevantState = { - makerBalance, - makerProxyAllowance, - makerFeeBalance, - makerFeeProxyAllowance, - filledTakerTokenAmount, - canceledTakerTokenAmount, - remainingFillableMakerTokenAmount, - }; - return orderRelevantState; - } - private validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { - const unavailableTakerTokenAmount = orderRelevantState.canceledTakerTokenAmount.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); - } - } - // TODO Add linear function solver when maker token is ZRX #badass - // Return the max amount that's fillable - } -} diff --git a/src/utils/order_validation_utils.ts b/src/utils/order_validation_utils.ts deleted file mode 100644 index f03703c4e..000000000 --- a/src/utils/order_validation_utils.ts +++ /dev/null @@ -1,166 +0,0 @@ -import * as _ from 'lodash'; -import BigNumber from 'bignumber.js'; -import {ExchangeContractErrs, SignedOrder, Order, ZeroExError, TradeSide, TransferType} from '../types'; -import {ZeroEx} from '../0x'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {utils} from '../utils/utils'; -import {constants} from '../utils/constants'; -import {ExchangeTransferSimulator} from './exchange_transfer_simulator'; - -export class OrderValidationUtils { - private tokenWrapper: TokenWrapper; - private exchangeWrapper: ExchangeWrapper; - constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) { - this.tokenWrapper = tokenWrapper; - 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); - this.validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, unavailableTakerTokenAmount, - ); - this.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - if (!_.isUndefined(expectedFillTakerTokenAmount)) { - fillTakerTokenAmount = expectedFillTakerTokenAmount; - } - const fillMakerTokenAmount = this.getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.taker, fillMakerTokenAmount, - TradeSide.Maker, TransferType.Trade, - ); - const makerFeeAmount = this.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); - this.validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, unavailableTakerTokenAmount, - ); - if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { - throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); - } - this.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) ? - remainingTakerTokenAmount : - fillTakerTokenAmount; - await this.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); - } - } - public async validateCancelOrderThrowIfInvalidAsync(order: Order, - cancelTakerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ): Promise<void> { - if (cancelTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderCancelAmountZero); - } - if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); - if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderCancelExpired); - } - } - public async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, senderAddress: string, zrxTokenAddress: string): Promise<void> { - const fillMakerTokenAmount = this.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 = this.getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, TradeSide.Maker, - TransferType.Fee, - ); - const takerFeeAmount = this.getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.takerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, senderAddress, signedOrder.feeRecipient, takerFeeAmount, TradeSide.Taker, - TransferType.Fee, - ); - } - private validateRemainingFillAmountNotZeroOrThrow( - takerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber, - ) { - if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); - } - } - private validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) { - const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); - if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderFillExpired); - } - } - private getPartialAmount(numerator: BigNumber, denominator: BigNumber, - target: BigNumber): BigNumber { - const fillMakerTokenAmount = numerator - .mul(target) - .div(denominator) - .round(0); - return fillMakerTokenAmount; - } -} diff --git a/src/utils/signature_utils.ts b/src/utils/signature_utils.ts deleted file mode 100644 index d066f8bf0..000000000 --- a/src/utils/signature_utils.ts +++ /dev/null @@ -1,44 +0,0 @@ -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; - }, -}; diff --git a/src/utils/utils.ts b/src/utils/utils.ts deleted file mode 100644 index 280f3e979..000000000 --- a/src/utils/utils.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as _ from 'lodash'; -import * as ethABI from 'ethereumjs-abi'; -import * as ethUtil from 'ethereumjs-util'; -import {Order, SignedOrder, SolidityTypes} from '../types'; -import BigNumber from 'bignumber.js'; -import BN = require('bn.js'); - -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); - }, - isParityNode(nodeVersion: string): boolean { - return _.includes(nodeVersion, 'Parity'); - }, - isTestRpc(nodeVersion: string): boolean { - return _.includes(nodeVersion, 'TestRPC'); - }, - 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; - }, - getCurrentUnixTimestamp(): BigNumber { - return new BigNumber(Date.now() / 1000); - }, -}; |