From aa10844d9e5b42841f75806de1cc45b89d9b50c6 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Sat, 16 Nov 2019 08:52:59 +1000 Subject: [PATCH] Remove Lodash as a dependency in BaseContract --- .../templates/TypeScript/contract.handlebars | 6 ++- packages/base-contract/package.json | 1 - packages/base-contract/src/index.ts | 38 ++++++++++--------- .../base-contract/src/subscription_manager.ts | 14 +++---- packages/base-contract/src/utils.ts | 15 +++++--- .../base-contract/src/utils/filter_utils.ts | 16 ++++---- 6 files changed, 49 insertions(+), 41 deletions(-) diff --git a/packages/abi-gen/templates/TypeScript/contract.handlebars b/packages/abi-gen/templates/TypeScript/contract.handlebars index 46d8f3c2ba..c2a76e1d2b 100644 --- a/packages/abi-gen/templates/TypeScript/contract.handlebars +++ b/packages/abi-gen/templates/TypeScript/contract.handlebars @@ -304,13 +304,15 @@ export class {{contractName}}Contract extends BaseContract { this._subscriptionManager = new SubscriptionManager<{{contractName}}EventArgs, {{contractName}}Events>( {{contractName}}Contract.ABI(), this._web3Wrapper, - );{{/if~}} + ); + {{/if~}} + {{contractName}}Contract.ABI().forEach((item, index) => { if (item.type === 'function') { const methodAbi = item as MethodAbi; this._methodABIIndex[methodAbi.name] = index; } - }) + }); } } diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index e62bf02160..2d2170951e 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -55,7 +55,6 @@ "ethereumjs-vm": "^4.0.0", "ethers": "~4.0.4", "js-sha3": "^0.7.0", - "lodash": "^4.17.11", "uuid": "^3.3.2" }, "publishConfig": { diff --git a/packages/base-contract/src/index.ts b/packages/base-contract/src/index.ts index 8b181ec494..b22dedf20e 100644 --- a/packages/base-contract/src/index.ts +++ b/packages/base-contract/src/index.ts @@ -29,7 +29,6 @@ import Account from 'ethereumjs-account'; import * as util from 'ethereumjs-util'; import { default as VM } from 'ethereumjs-vm'; import PStateManager from 'ethereumjs-vm/dist/state/promisified'; -import * as _ from 'lodash'; export { methodAbiToFunctionSignature } from './utils'; @@ -100,7 +99,7 @@ export class BaseContract { values: any[], formatter: (type: string, value: any) => any, ): any { - return _.map(values, (value: any, i: number) => formatABIDataItem(abis[i], value, formatter)); + return values.map((value: any, i: number) => formatABIDataItem(abis[i], value, formatter)); } protected static _lowercaseAddress(type: string, value: string): string { return type === 'address' ? value.toLowerCase() : value; @@ -109,8 +108,7 @@ export class BaseContract { return BigNumber.isBigNumber(value) ? value.toString() : value; } protected static _lookupConstructorAbi(abi: ContractAbi): ConstructorAbi { - const constructorAbiIfExists = _.find( - abi, + const constructorAbiIfExists = abi.find( (abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Constructor, // tslint:disable-next-line:no-unnecessary-type-assertion ) as ConstructorAbi | undefined; @@ -180,15 +178,14 @@ export class BaseContract { txData: T, estimateGasAsync?: (txData: T) => Promise, ): Promise { - const removeUndefinedProperties = _.pickBy.bind(_); - const txDataWithDefaults = removeUndefinedProperties(txData); + const txDataWithDefaults = BaseContract._removeUndefinedProperties(txData); if (txDataWithDefaults.gas === undefined && estimateGasAsync !== undefined) { txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults); } if (txDataWithDefaults.from !== undefined) { txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase(); } - return txDataWithDefaults; + return txDataWithDefaults as TxData; } protected static _assertCallParams(callData: Partial, defaultBlock?: BlockParam): void { assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ @@ -200,6 +197,11 @@ export class BaseContract { assert.isBlockParam('defaultBlock', defaultBlock); } } + private static _removeUndefinedProperties(props: any): T { + const clonedProps = { ...props }; + Object.keys(clonedProps).forEach(key => clonedProps[key] === undefined && delete clonedProps[key]); + return clonedProps; + } protected _promiseWithTransactionHash( txHashPromise: Promise, opts: AwaitTransactionSuccessOpts, @@ -224,19 +226,19 @@ export class BaseContract { // 1. Optional param passed in to public method call // 2. Global config passed in at library instantiation // 3. Gas estimate calculation + safety margin - const removeUndefinedProperties = _.pickBy.bind(_); + // tslint:disable-next-line:no-object-literal-type-assertion const txDataWithDefaults = { to: this.address, - ...removeUndefinedProperties(this._web3Wrapper.getContractDefaults()), - ...removeUndefinedProperties(txData), - }; + ...this._web3Wrapper.getContractDefaults(), + ...BaseContract._removeUndefinedProperties(txData), + } as T; if (txDataWithDefaults.gas === undefined && estimateGasAsync !== undefined) { txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults); } if (txDataWithDefaults.from !== undefined) { txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase(); } - return txDataWithDefaults; + return txDataWithDefaults as TxData; } protected async _evmExecAsync(encodedData: string): Promise { const encodedDataBytes = Buffer.from(encodedData.substr(2), 'hex'); @@ -300,7 +302,7 @@ export class BaseContract { return abiEncoder; } protected _lookupAbi(functionSignature: string): MethodAbi { - const methodAbi = _.find(this.abi, (abiDefinition: AbiDefinition) => { + const methodAbi = this.abi.find((abiDefinition: AbiDefinition) => { if (abiDefinition.type !== AbiType.Function) { return false; } @@ -362,14 +364,16 @@ export class BaseContract { (abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Function, ) as MethodAbi[]; this._abiEncoderByFunctionSignature = {}; - _.each(methodAbis, methodAbi => { + methodAbis.forEach(methodAbi => { const abiEncoder = new AbiEncoder.Method(methodAbi); const functionSignature = abiEncoder.getSignature(); this._abiEncoderByFunctionSignature[functionSignature] = abiEncoder; this._web3Wrapper.abiDecoder.addABI(abi, contractName); }); - _.each(logDecodeDependencies, (dependencyAbi, dependencyName) => { - this._web3Wrapper.abiDecoder.addABI(dependencyAbi, dependencyName); - }); + if (logDecodeDependencies) { + Object.entries(logDecodeDependencies).forEach(([dependencyName, dependencyAbi]) => + this._web3Wrapper.abiDecoder.addABI(dependencyAbi, dependencyName), + ); + } } } diff --git a/packages/base-contract/src/subscription_manager.ts b/packages/base-contract/src/subscription_manager.ts index 9f0bdd6cc2..9b7bfe942d 100644 --- a/packages/base-contract/src/subscription_manager.ts +++ b/packages/base-contract/src/subscription_manager.ts @@ -11,7 +11,6 @@ import { RawLogEntry, } from 'ethereum-types'; import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; -import * as _ from 'lodash'; import { EventCallback, IndexedFilterValues } from '@0x/types'; @@ -48,10 +47,8 @@ export class SubscriptionManager { - this.unsubscribe(filterToken); - }); + const filterTokens = Object.keys(this._filterCallbacks); + filterTokens.forEach(filterToken => this.unsubscribe(filterToken)); } public unsubscribe(filterToken: string, err?: Error): void { if (this._filters[filterToken] === undefined) { @@ -63,7 +60,7 @@ export class SubscriptionManager>> { const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange); const logs = await this._web3Wrapper.getLogsAsync(filter); - const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this)); + const logsWithDecodedArguments = logs.map(this._tryToDecodeLogOrNoop.bind(this)) as any[]; return logsWithDecodedArguments; } protected _tryToDecodeLogOrNoop( @@ -111,7 +108,8 @@ export class SubscriptionManager marshaller.unmarshalLog(rawLog)); logs.forEach(log => { - _.forEach(this._filters, (filter: FilterObject, filterToken: string) => { + Object.keys(this._filters).forEach((filterToken: string) => { + const filter = this._filters[filterToken]; if (filterUtils.matchesFilter(log, filter)) { const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs; const logEvent = { diff --git a/packages/base-contract/src/utils.ts b/packages/base-contract/src/utils.ts index 24273051f2..0de13ea2c0 100644 --- a/packages/base-contract/src/utils.ts +++ b/packages/base-contract/src/utils.ts @@ -1,12 +1,11 @@ import { DataItem, MethodAbi } from 'ethereum-types'; -import * as _ from 'lodash'; // tslint:disable-next-line:completed-docs export function formatABIDataItem(abi: DataItem, value: any, formatter: (type: string, value: any) => any): any { const trailingArrayRegex = /\[\d*\]$/; if (abi.type.match(trailingArrayRegex)) { const arrayItemType = abi.type.replace(trailingArrayRegex, ''); - return _.map(value, val => { + return value.map((val: any) => { const arrayItemAbi = { ...abi, type: arrayItemType, @@ -15,9 +14,15 @@ export function formatABIDataItem(abi: DataItem, value: any, formatter: (type: s }); } else if (abi.type === 'tuple') { const formattedTuple: { [componentName: string]: DataItem } = {}; - _.forEach(abi.components, componentABI => { - formattedTuple[componentABI.name] = formatABIDataItem(componentABI, value[componentABI.name], formatter); - }); + if (abi.components) { + abi.components.forEach(componentABI => { + formattedTuple[componentABI.name] = formatABIDataItem( + componentABI, + value[componentABI.name], + formatter, + ); + }); + } return formattedTuple; } else { return formatter(abi.type, value); diff --git a/packages/base-contract/src/utils/filter_utils.ts b/packages/base-contract/src/utils/filter_utils.ts index 8611a86541..e914c94066 100644 --- a/packages/base-contract/src/utils/filter_utils.ts +++ b/packages/base-contract/src/utils/filter_utils.ts @@ -3,7 +3,6 @@ import { BigNumber } from '@0x/utils'; import { BlockRange, ContractAbi, EventAbi, FilterObject, LogEntry } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as jsSHA3 from 'js-sha3'; -import * as _ from 'lodash'; import * as uuid from 'uuid/v4'; const TOPIC_LENGTH = 32; @@ -19,7 +18,8 @@ export const filterUtils = { abi: ContractAbi, blockRange?: BlockRange, ): FilterObject { - const eventAbi = _.find(abi, { name: eventName }) as EventAbi; + // tslint:disable:next-line no-unnecessary-type-assertion + const eventAbi = abi.find(abiDefinition => (abiDefinition as EventAbi).name === eventName) as EventAbi; const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi); const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature)); const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues); @@ -37,7 +37,7 @@ export const filterUtils = { return filter; }, getEventSignatureFromAbiByName(eventAbi: EventAbi): string { - const types = _.map(eventAbi.inputs, 'type'); + const types = eventAbi.inputs.map(i => i.type); const signature = `${eventAbi.name}(${types.join(',')})`; return signature; }, @@ -76,15 +76,15 @@ export const filterUtils = { return true; }, doesMatchTopics(logTopics: string[], filterTopics: Array): boolean { - const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils)); - const doesMatchTopics = _.every(matchesTopic); + const matchesTopic = logTopics.map((logTopic, i) => filterUtils.matchesTopic(logTopic, filterTopics[i])); + const doesMatchTopics = matchesTopic.every(m => m); return doesMatchTopics; }, matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean { - if (_.isArray(filterTopic)) { - return _.includes(filterTopic, logTopic); + if (Array.isArray(filterTopic)) { + return filterTopic.includes(logTopic); } - if (_.isString(filterTopic)) { + if (typeof filterTopic === 'string') { return filterTopic === logTopic; } // null topic is a wildcard