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>(
{{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;
}
})
});
}
}

View File

@ -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": {

View File

@ -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<number>,
): Promise<TxData> {
const removeUndefinedProperties = _.pickBy.bind(_);
const txDataWithDefaults = removeUndefinedProperties(txData);
const txDataWithDefaults = BaseContract._removeUndefinedProperties<T>(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<CallData>, defaultBlock?: BlockParam): void {
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
@ -200,6 +197,11 @@ export class BaseContract {
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(
txHashPromise: Promise<string>,
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<string> {
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),
);
}
}
}

View File

@ -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<ContractEventArgs, ContractEvents extends strin
this._onLogRemovedSubscriptionToken = undefined;
}
public unsubscribeAll(): void {
const filterTokens = _.keys(this._filterCallbacks);
_.each(filterTokens, filterToken => {
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<ContractEventArgs, ContractEvents extends strin
}
delete this._filters[filterToken];
delete this._filterCallbacks[filterToken];
if (_.isEmpty(this._filters)) {
if (Object.keys(this._filters).length === 0) {
this._stopBlockAndLogStream();
}
}
@ -94,7 +91,7 @@ export class SubscriptionManager<ContractEventArgs, ContractEvents extends strin
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
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<ArgsType extends ContractEventArgs>(
@ -111,7 +108,8 @@ export class SubscriptionManager<ContractEventArgs, ContractEvents extends strin
): void {
const logs: LogEntry[] = rawLogs.map(rawLog => 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<ArgsType>;
const logEvent = {

View File

@ -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);

View File

@ -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<string[] | string | null>): 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