Remove Lodash as a dependency in BaseContract

This commit is contained in:
Jacob Evans 2019-11-16 08:52:59 +10:00
parent be52079182
commit aa10844d9e
No known key found for this signature in database
GPG Key ID: 2036DA2ADDFB0842
6 changed files with 49 additions and 41 deletions

View File

@ -304,13 +304,15 @@ export class {{contractName}}Contract extends BaseContract {
this._subscriptionManager = new SubscriptionManager<{{contractName}}EventArgs, {{contractName}}Events>( this._subscriptionManager = new SubscriptionManager<{{contractName}}EventArgs, {{contractName}}Events>(
{{contractName}}Contract.ABI(), {{contractName}}Contract.ABI(),
this._web3Wrapper, this._web3Wrapper,
);{{/if~}} );
{{/if~}}
{{contractName}}Contract.ABI().forEach((item, index) => { {{contractName}}Contract.ABI().forEach((item, index) => {
if (item.type === 'function') { if (item.type === 'function') {
const methodAbi = item as MethodAbi; const methodAbi = item as MethodAbi;
this._methodABIIndex[methodAbi.name] = index; this._methodABIIndex[methodAbi.name] = index;
} }
}) });
} }
} }

View File

@ -55,7 +55,6 @@
"ethereumjs-vm": "^4.0.0", "ethereumjs-vm": "^4.0.0",
"ethers": "~4.0.4", "ethers": "~4.0.4",
"js-sha3": "^0.7.0", "js-sha3": "^0.7.0",
"lodash": "^4.17.11",
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"publishConfig": { "publishConfig": {

View File

@ -29,7 +29,6 @@ import Account from 'ethereumjs-account';
import * as util from 'ethereumjs-util'; import * as util from 'ethereumjs-util';
import { default as VM } from 'ethereumjs-vm'; import { default as VM } from 'ethereumjs-vm';
import PStateManager from 'ethereumjs-vm/dist/state/promisified'; import PStateManager from 'ethereumjs-vm/dist/state/promisified';
import * as _ from 'lodash';
export { methodAbiToFunctionSignature } from './utils'; export { methodAbiToFunctionSignature } from './utils';
@ -100,7 +99,7 @@ export class BaseContract {
values: any[], values: any[],
formatter: (type: string, value: any) => any, formatter: (type: string, value: any) => 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 { protected static _lowercaseAddress(type: string, value: string): string {
return type === 'address' ? value.toLowerCase() : value; return type === 'address' ? value.toLowerCase() : value;
@ -109,8 +108,7 @@ export class BaseContract {
return BigNumber.isBigNumber(value) ? value.toString() : value; return BigNumber.isBigNumber(value) ? value.toString() : value;
} }
protected static _lookupConstructorAbi(abi: ContractAbi): ConstructorAbi { protected static _lookupConstructorAbi(abi: ContractAbi): ConstructorAbi {
const constructorAbiIfExists = _.find( const constructorAbiIfExists = abi.find(
abi,
(abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Constructor, (abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Constructor,
// tslint:disable-next-line:no-unnecessary-type-assertion // tslint:disable-next-line:no-unnecessary-type-assertion
) as ConstructorAbi | undefined; ) as ConstructorAbi | undefined;
@ -180,15 +178,14 @@ export class BaseContract {
txData: T, txData: T,
estimateGasAsync?: (txData: T) => Promise<number>, estimateGasAsync?: (txData: T) => Promise<number>,
): Promise<TxData> { ): Promise<TxData> {
const removeUndefinedProperties = _.pickBy.bind(_); const txDataWithDefaults = BaseContract._removeUndefinedProperties<T>(txData);
const txDataWithDefaults = removeUndefinedProperties(txData);
if (txDataWithDefaults.gas === undefined && estimateGasAsync !== undefined) { if (txDataWithDefaults.gas === undefined && estimateGasAsync !== undefined) {
txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults); txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults);
} }
if (txDataWithDefaults.from !== undefined) { if (txDataWithDefaults.from !== undefined) {
txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase(); txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase();
} }
return txDataWithDefaults; return txDataWithDefaults as TxData;
} }
protected static _assertCallParams(callData: Partial<CallData>, defaultBlock?: BlockParam): void { protected static _assertCallParams(callData: Partial<CallData>, defaultBlock?: BlockParam): void {
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
@ -200,6 +197,11 @@ export class BaseContract {
assert.isBlockParam('defaultBlock', defaultBlock); assert.isBlockParam('defaultBlock', defaultBlock);
} }
} }
private static _removeUndefinedProperties<T>(props: any): T {
const clonedProps = { ...props };
Object.keys(clonedProps).forEach(key => clonedProps[key] === undefined && delete clonedProps[key]);
return clonedProps;
}
protected _promiseWithTransactionHash( protected _promiseWithTransactionHash(
txHashPromise: Promise<string>, txHashPromise: Promise<string>,
opts: AwaitTransactionSuccessOpts, opts: AwaitTransactionSuccessOpts,
@ -224,19 +226,19 @@ export class BaseContract {
// 1. Optional param passed in to public method call // 1. Optional param passed in to public method call
// 2. Global config passed in at library instantiation // 2. Global config passed in at library instantiation
// 3. Gas estimate calculation + safety margin // 3. Gas estimate calculation + safety margin
const removeUndefinedProperties = _.pickBy.bind(_); // tslint:disable-next-line:no-object-literal-type-assertion
const txDataWithDefaults = { const txDataWithDefaults = {
to: this.address, to: this.address,
...removeUndefinedProperties(this._web3Wrapper.getContractDefaults()), ...this._web3Wrapper.getContractDefaults(),
...removeUndefinedProperties(txData), ...BaseContract._removeUndefinedProperties(txData),
}; } as T;
if (txDataWithDefaults.gas === undefined && estimateGasAsync !== undefined) { if (txDataWithDefaults.gas === undefined && estimateGasAsync !== undefined) {
txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults); txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults);
} }
if (txDataWithDefaults.from !== undefined) { if (txDataWithDefaults.from !== undefined) {
txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase(); txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase();
} }
return txDataWithDefaults; return txDataWithDefaults as TxData;
} }
protected async _evmExecAsync(encodedData: string): Promise<string> { protected async _evmExecAsync(encodedData: string): Promise<string> {
const encodedDataBytes = Buffer.from(encodedData.substr(2), 'hex'); const encodedDataBytes = Buffer.from(encodedData.substr(2), 'hex');
@ -300,7 +302,7 @@ export class BaseContract {
return abiEncoder; return abiEncoder;
} }
protected _lookupAbi(functionSignature: string): MethodAbi { protected _lookupAbi(functionSignature: string): MethodAbi {
const methodAbi = _.find(this.abi, (abiDefinition: AbiDefinition) => { const methodAbi = this.abi.find((abiDefinition: AbiDefinition) => {
if (abiDefinition.type !== AbiType.Function) { if (abiDefinition.type !== AbiType.Function) {
return false; return false;
} }
@ -362,14 +364,16 @@ export class BaseContract {
(abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Function, (abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Function,
) as MethodAbi[]; ) as MethodAbi[];
this._abiEncoderByFunctionSignature = {}; this._abiEncoderByFunctionSignature = {};
_.each(methodAbis, methodAbi => { methodAbis.forEach(methodAbi => {
const abiEncoder = new AbiEncoder.Method(methodAbi); const abiEncoder = new AbiEncoder.Method(methodAbi);
const functionSignature = abiEncoder.getSignature(); const functionSignature = abiEncoder.getSignature();
this._abiEncoderByFunctionSignature[functionSignature] = abiEncoder; this._abiEncoderByFunctionSignature[functionSignature] = abiEncoder;
this._web3Wrapper.abiDecoder.addABI(abi, contractName); this._web3Wrapper.abiDecoder.addABI(abi, contractName);
}); });
_.each(logDecodeDependencies, (dependencyAbi, dependencyName) => { if (logDecodeDependencies) {
this._web3Wrapper.abiDecoder.addABI(dependencyAbi, dependencyName); Object.entries(logDecodeDependencies).forEach(([dependencyName, dependencyAbi]) =>
}); this._web3Wrapper.abiDecoder.addABI(dependencyAbi, dependencyName),
);
}
} }
} }

View File

@ -11,7 +11,6 @@ import {
RawLogEntry, RawLogEntry,
} from 'ethereum-types'; } from 'ethereum-types';
import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
import * as _ from 'lodash';
import { EventCallback, IndexedFilterValues } from '@0x/types'; import { EventCallback, IndexedFilterValues } from '@0x/types';
@ -48,10 +47,8 @@ export class SubscriptionManager<ContractEventArgs, ContractEvents extends strin
this._onLogRemovedSubscriptionToken = undefined; this._onLogRemovedSubscriptionToken = undefined;
} }
public unsubscribeAll(): void { public unsubscribeAll(): void {
const filterTokens = _.keys(this._filterCallbacks); const filterTokens = Object.keys(this._filterCallbacks);
_.each(filterTokens, filterToken => { filterTokens.forEach(filterToken => this.unsubscribe(filterToken));
this.unsubscribe(filterToken);
});
} }
public unsubscribe(filterToken: string, err?: Error): void { public unsubscribe(filterToken: string, err?: Error): void {
if (this._filters[filterToken] === undefined) { if (this._filters[filterToken] === undefined) {
@ -63,7 +60,7 @@ export class SubscriptionManager<ContractEventArgs, ContractEvents extends strin
} }
delete this._filters[filterToken]; delete this._filters[filterToken];
delete this._filterCallbacks[filterToken]; delete this._filterCallbacks[filterToken];
if (_.isEmpty(this._filters)) { if (Object.keys(this._filters).length === 0) {
this._stopBlockAndLogStream(); this._stopBlockAndLogStream();
} }
} }
@ -94,7 +91,7 @@ export class SubscriptionManager<ContractEventArgs, ContractEvents extends strin
): Promise<Array<LogWithDecodedArgs<ArgsType>>> { ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange); const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange);
const logs = await this._web3Wrapper.getLogsAsync(filter); 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; return logsWithDecodedArguments;
} }
protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
@ -111,7 +108,8 @@ export class SubscriptionManager<ContractEventArgs, ContractEvents extends strin
): void { ): void {
const logs: LogEntry[] = rawLogs.map(rawLog => marshaller.unmarshalLog(rawLog)); const logs: LogEntry[] = rawLogs.map(rawLog => marshaller.unmarshalLog(rawLog));
logs.forEach(log => { 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)) { if (filterUtils.matchesFilter(log, filter)) {
const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>; const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>;
const logEvent = { const logEvent = {

View File

@ -1,12 +1,11 @@
import { DataItem, MethodAbi } from 'ethereum-types'; import { DataItem, MethodAbi } from 'ethereum-types';
import * as _ from 'lodash';
// tslint:disable-next-line:completed-docs // tslint:disable-next-line:completed-docs
export function formatABIDataItem(abi: DataItem, value: any, formatter: (type: string, value: any) => any): any { export function formatABIDataItem(abi: DataItem, value: any, formatter: (type: string, value: any) => any): any {
const trailingArrayRegex = /\[\d*\]$/; const trailingArrayRegex = /\[\d*\]$/;
if (abi.type.match(trailingArrayRegex)) { if (abi.type.match(trailingArrayRegex)) {
const arrayItemType = abi.type.replace(trailingArrayRegex, ''); const arrayItemType = abi.type.replace(trailingArrayRegex, '');
return _.map(value, val => { return value.map((val: any) => {
const arrayItemAbi = { const arrayItemAbi = {
...abi, ...abi,
type: arrayItemType, type: arrayItemType,
@ -15,9 +14,15 @@ export function formatABIDataItem(abi: DataItem, value: any, formatter: (type: s
}); });
} else if (abi.type === 'tuple') { } else if (abi.type === 'tuple') {
const formattedTuple: { [componentName: string]: DataItem } = {}; const formattedTuple: { [componentName: string]: DataItem } = {};
_.forEach(abi.components, componentABI => { if (abi.components) {
formattedTuple[componentABI.name] = formatABIDataItem(componentABI, value[componentABI.name], formatter); abi.components.forEach(componentABI => {
formattedTuple[componentABI.name] = formatABIDataItem(
componentABI,
value[componentABI.name],
formatter,
);
}); });
}
return formattedTuple; return formattedTuple;
} else { } else {
return formatter(abi.type, value); return formatter(abi.type, value);

View File

@ -3,7 +3,6 @@ import { BigNumber } from '@0x/utils';
import { BlockRange, ContractAbi, EventAbi, FilterObject, LogEntry } from 'ethereum-types'; import { BlockRange, ContractAbi, EventAbi, FilterObject, LogEntry } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util'; import * as ethUtil from 'ethereumjs-util';
import * as jsSHA3 from 'js-sha3'; import * as jsSHA3 from 'js-sha3';
import * as _ from 'lodash';
import * as uuid from 'uuid/v4'; import * as uuid from 'uuid/v4';
const TOPIC_LENGTH = 32; const TOPIC_LENGTH = 32;
@ -19,7 +18,8 @@ export const filterUtils = {
abi: ContractAbi, abi: ContractAbi,
blockRange?: BlockRange, blockRange?: BlockRange,
): FilterObject { ): 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 eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi);
const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature)); const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues); const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
@ -37,7 +37,7 @@ export const filterUtils = {
return filter; return filter;
}, },
getEventSignatureFromAbiByName(eventAbi: EventAbi): string { getEventSignatureFromAbiByName(eventAbi: EventAbi): string {
const types = _.map(eventAbi.inputs, 'type'); const types = eventAbi.inputs.map(i => i.type);
const signature = `${eventAbi.name}(${types.join(',')})`; const signature = `${eventAbi.name}(${types.join(',')})`;
return signature; return signature;
}, },
@ -76,15 +76,15 @@ export const filterUtils = {
return true; return true;
}, },
doesMatchTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean { doesMatchTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils)); const matchesTopic = logTopics.map((logTopic, i) => filterUtils.matchesTopic(logTopic, filterTopics[i]));
const doesMatchTopics = _.every(matchesTopic); const doesMatchTopics = matchesTopic.every(m => m);
return doesMatchTopics; return doesMatchTopics;
}, },
matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean { matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
if (_.isArray(filterTopic)) { if (Array.isArray(filterTopic)) {
return _.includes(filterTopic, logTopic); return filterTopic.includes(logTopic);
} }
if (_.isString(filterTopic)) { if (typeof filterTopic === 'string') {
return filterTopic === logTopic; return filterTopic === logTopic;
} }
// null topic is a wildcard // null topic is a wildcard