aboutsummaryrefslogtreecommitdiffstats
path: root/packages/utils/src
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2019-01-15 22:10:50 +0800
committerFabio Berger <me@fabioberger.com>2019-01-15 22:10:50 +0800
commitae147e615af9da32ca3da0b92ef77815061ad5be (patch)
treed7bae435a99c668d02cc9ee9d84619c3056509f6 /packages/utils/src
parent98579300c1f78d8c360fa960cc73ffacb1012e91 (diff)
parent18084588ea9fa724d6e32c9a49c79d49f189ba7c (diff)
downloaddexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.tar
dexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.tar.gz
dexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.tar.bz2
dexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.tar.lz
dexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.tar.xz
dexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.tar.zst
dexon-sol-tools-ae147e615af9da32ca3da0b92ef77815061ad5be.zip
Merge branch 'development' into fix/dev-tools-pages/finalTouches
* development: (87 commits) Update packages/sol-tracing-utils/src/trace_collection_subprovider.ts Make mapping namings direct Remove unused tslint disable Revert "Remove logAsyncErrors hack" Remove logAsyncErrors hack Refactor logAsyncErrors to follow our conventions Export Sources and SourceCodes out of tracing utils Replace console.log with logUtils.log (#1515) strict decoding of return values using generics makerAssetFillAmount -> takerAssetFillAmount Ran prettier Linter Fix build after rebase Style cleanup for Compressed Calldata in Contract Wrappers PR Use simpler `_.find` to locate fillOrderBai Updated dutch auction wrapper Added back abi-gen-wrappers Renamed signatureParser.ts to signature_parser.ts Renamed decode rule `structsAsObjects` to `shouldConvertStructsToObjects` circle build failed. New commit to resubmit job. ...
Diffstat (limited to 'packages/utils/src')
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/data_type.ts20
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/types/set.ts34
-rw-r--r--packages/utils/src/abi_encoder/evm_data_type_factory.ts33
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/address.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/array.ts24
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/bool.ts2
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts2
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/int.ts8
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/method.ts15
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/pointer.ts8
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts2
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/string.ts2
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/tuple.ts20
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/uint.ts8
-rw-r--r--packages/utils/src/abi_encoder/index.ts2
-rw-r--r--packages/utils/src/abi_encoder/utils/constants.ts2
-rw-r--r--packages/utils/src/abi_encoder/utils/signature_parser.ts101
17 files changed, 247 insertions, 41 deletions
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
index 13cc87e2a..f23324721 100644
--- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
+++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
@@ -51,8 +51,26 @@ export abstract class DataType {
return value;
}
+ public decodeAsArray(returndata: string, rules?: DecodingRules): any[] {
+ const value = this.decode(returndata, rules);
+ const valuesAsArray = _.isObject(value) ? _.values(value) : [value];
+ return valuesAsArray;
+ }
+
+ public getSignature(isDetailed?: boolean): string {
+ if (_.isEmpty(this._dataItem.name) || !isDetailed) {
+ return this.getSignatureType();
+ }
+ const name = this.getDataItem().name;
+ const lastIndexOfScopeDelimiter = name.lastIndexOf('.');
+ const isScopedName = !_.isUndefined(lastIndexOfScopeDelimiter) && lastIndexOfScopeDelimiter > 0;
+ const shortName = isScopedName ? name.substr((lastIndexOfScopeDelimiter as number) + 1) : name;
+ const detailedSignature = `${shortName} ${this.getSignatureType()}`;
+ return detailedSignature;
+ }
+
public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock;
public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any;
- public abstract getSignature(): string;
+ public abstract getSignatureType(): string;
public abstract isStatic(): boolean;
}
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
index 00059a4b6..2c6c4b0f6 100644
--- a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
+++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
@@ -1,3 +1,4 @@
+import { ObjectMap } from '@0x/types';
import { DataItem } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -134,31 +135,26 @@ export abstract class AbstractSetDataType extends DataType {
const block = new SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
// Create blocks for members of set.
const memberCalldataBlocks: CalldataBlock[] = [];
- const childMap = _.cloneDeep(this._memberIndexByName);
- _.forOwn(obj, (value: any, key: string) => {
- if (!(key in childMap)) {
+ _.forEach(this._memberIndexByName, (memberIndex: number, memberName: string) => {
+ if (!(memberName in obj)) {
throw new Error(
- `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`,
+ `Could not assign tuple to object: missing key '${memberName}' in object ${JSON.stringify(obj)}`,
);
}
- const memberBlock = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, block);
+ const memberValue: any = (obj as ObjectMap<any>)[memberName];
+ const memberBlock = this._members[memberIndex].generateCalldataBlock(memberValue, block);
memberCalldataBlocks.push(memberBlock);
- delete childMap[key];
});
- // Sanity check that all members have been included.
- if (Object.keys(childMap).length !== 0) {
- throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`);
- }
// Associate member blocks with Set block.
block.setMembers(memberCalldataBlocks);
return block;
}
- protected _computeSignatureOfMembers(): string {
+ protected _computeSignatureOfMembers(isDetailed?: boolean): string {
// Compute signature of members
let signature = `(`;
_.each(this._members, (member: DataType, i: number) => {
- signature += member.getSignature();
+ signature += member.getSignature(isDetailed);
if (i < this._members.length - 1) {
signature += ',';
}
@@ -179,17 +175,27 @@ export abstract class AbstractSetDataType extends DataType {
// Create one member for each component of `dataItem`
const members: DataType[] = [];
const memberIndexByName: MemberIndexByName = {};
+ const memberNames: string[] = [];
_.each(dataItem.components, (memberItem: DataItem) => {
+ // If a component with `name` already exists then
+ // rename to `name_nameIdx` to avoid naming conflicts.
+ let memberName = memberItem.name;
+ let nameIdx = 0;
+ while (_.includes(memberNames, memberName) || _.isEmpty(memberName)) {
+ nameIdx++;
+ memberName = `${memberItem.name}_${nameIdx}`;
+ }
+ memberNames.push(memberName);
const childDataItem: DataItem = {
type: memberItem.type,
- name: `${dataItem.name}.${memberItem.name}`,
+ name: `${dataItem.name}.${memberName}`,
};
const components = memberItem.components;
if (!_.isUndefined(components)) {
childDataItem.components = components;
}
const child = this.getFactory().create(childDataItem, this);
- memberIndexByName[memberItem.name] = members.length;
+ memberIndexByName[memberName] = members.length;
members.push(child);
});
return [members, memberIndexByName];
diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts
index 4cc124e0a..268649148 100644
--- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts
+++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts
@@ -2,6 +2,8 @@
import { DataItem, MethodAbi } from 'ethereum-types';
import * as _ from 'lodash';
+import { generateDataItemsFromSignature } from './utils/signature_parser';
+
import { DataType } from './abstract_data_types/data_type';
import { DataTypeFactory } from './abstract_data_types/interfaces';
import { AddressDataType } from './evm_data_types/address';
@@ -129,4 +131,35 @@ export class EvmDataTypeFactory implements DataTypeFactory {
private constructor() {}
}
+
+/**
+ * Convenience function for creating a DataType from different inputs.
+ * @param input A single or set of DataItem or a DataType signature.
+ * A signature in the form of '<type>' is interpreted as a `DataItem`
+ * For example, 'string' is interpreted as {type: 'string'}
+ * A signature in the form '(<type1>, <type2>, ..., <typen>)' is interpreted as `DataItem[]`
+ * For eaxmple, '(string, uint256)' is interpreted as [{type: 'string'}, {type: 'uint256'}]
+ * @return DataType corresponding to input.
+ */
+export function create(input: DataItem | DataItem[] | string): DataType {
+ // Handle different types of input
+ const isSignature = typeof input === 'string';
+ const isTupleSignature = isSignature && (input as string).startsWith('(');
+ const shouldParseAsTuple = isTupleSignature || _.isArray(input);
+ // Create input `dataItem`
+ let dataItem: DataItem;
+ if (shouldParseAsTuple) {
+ const dataItems = isSignature ? generateDataItemsFromSignature(input as string) : (input as DataItem[]);
+ dataItem = {
+ name: '',
+ type: 'tuple',
+ components: dataItems,
+ };
+ } else {
+ dataItem = isSignature ? generateDataItemsFromSignature(input as string)[0] : (input as DataItem);
+ }
+ // Create data type
+ const dataType = EvmDataTypeFactory.getInstance().create(dataItem);
+ return dataType;
+}
/* tslint:enable no-construct */
diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts
index 2e3a206c6..2278830eb 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/address.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts
@@ -39,10 +39,11 @@ export class AddressDataType extends AbstractBlobDataType {
const valueBufPadded = calldata.popWord();
const valueBuf = valueBufPadded.slice(AddressDataType._DECODED_ADDRESS_OFFSET_IN_BYTES);
const value = ethUtil.bufferToHex(valueBuf);
- return value;
+ const valueLowercase = _.toLower(value);
+ return valueLowercase;
}
- public getSignature(): string {
+ public getSignatureType(): string {
return SolidityTypes.Address;
}
/* tslint:enable prefer-function-over-method */
diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts
index 7595cb667..d9607f47e 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/array.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts
@@ -7,7 +7,6 @@ import { constants } from '../utils/constants';
export class ArrayDataType extends AbstractSetDataType {
private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$');
- private readonly _arraySignature: string;
private readonly _elementType: string;
public static matchType(type: string): boolean {
@@ -35,25 +34,36 @@ export class ArrayDataType extends AbstractSetDataType {
super(dataItem, dataTypeFactory, isArray, arrayLength, arrayElementType);
// Set array properties
this._elementType = arrayElementType;
- this._arraySignature = this._computeSignature();
}
- public getSignature(): string {
- return this._arraySignature;
+ public getSignatureType(): string {
+ return this._computeSignature(false);
}
- private _computeSignature(): string {
+ public getSignature(isDetailed?: boolean): string {
+ if (_.isEmpty(this.getDataItem().name) || !isDetailed) {
+ return this.getSignatureType();
+ }
+ const name = this.getDataItem().name;
+ const lastIndexOfScopeDelimiter = name.lastIndexOf('.');
+ const isScopedName = !_.isUndefined(lastIndexOfScopeDelimiter) && lastIndexOfScopeDelimiter > 0;
+ const shortName = isScopedName ? name.substr((lastIndexOfScopeDelimiter as number) + 1) : name;
+ const detailedSignature = `${shortName} ${this._computeSignature(isDetailed)}`;
+ return detailedSignature;
+ }
+
+ private _computeSignature(isDetailed?: boolean): string {
// Compute signature for a single array element
const elementDataItem: DataItem = {
type: this._elementType,
- name: 'N/A',
+ name: '',
};
const elementComponents = this.getDataItem().components;
if (!_.isUndefined(elementComponents)) {
elementDataItem.components = elementComponents;
}
const elementDataType = this.getFactory().create(elementDataItem);
- const elementSignature = elementDataType.getSignature();
+ const elementSignature = elementDataType.getSignature(isDetailed);
// Construct signature for array of type `element`
if (_.isUndefined(this._arrayLength)) {
return `${elementSignature}[]`;
diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts
index d713d5a94..7f91f34e6 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts
@@ -46,7 +46,7 @@ export class BoolDataType extends AbstractBlobDataType {
return value;
}
- public getSignature(): string {
+ public getSignatureType(): string {
return SolidityTypes.Bool;
}
/* tslint:enable prefer-function-over-method */
diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
index 5277efd6c..fa38b63c0 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
@@ -65,7 +65,7 @@ export class DynamicBytesDataType extends AbstractBlobDataType {
return value;
}
- public getSignature(): string {
+ public getSignatureType(): string {
return SolidityTypes.Bytes;
}
/* tslint:enable prefer-function-over-method */
diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts
index f1dcf5ea1..8d98e195b 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/int.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts
@@ -47,13 +47,17 @@ export class IntDataType extends AbstractBlobDataType {
return encodedValue;
}
- public decodeValue(calldata: RawCalldata): BigNumber {
+ public decodeValue(calldata: RawCalldata): BigNumber | number {
const valueBuf = calldata.popWord();
const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue);
+ const numberOfBytesInUint8 = 8;
+ if (this._width === numberOfBytesInUint8) {
+ return value.toNumber();
+ }
return value;
}
- public getSignature(): string {
+ public getSignatureType(): string {
return `${SolidityTypes.Int}${this._width}`;
}
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts
index b1cd1377f..c852a0fdf 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/method.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts
@@ -44,7 +44,20 @@ export class MethodDataType extends AbstractSetDataType {
return returnValues;
}
- public getSignature(): string {
+ public strictDecodeReturnValue<T>(returndata: string, rules?: DecodingRules): T {
+ const returnValues = this._returnDataType.decode(returndata, rules);
+ const returnValuesAsArray: any = _.isObject(returnValues) ? _.values(returnValues) : [returnValues];
+ switch (returnValuesAsArray.length) {
+ case 0:
+ return undefined as any;
+ case 1:
+ return returnValuesAsArray[0];
+ default:
+ return returnValuesAsArray;
+ }
+ }
+
+ public getSignatureType(): string {
return this._methodSignature;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
index 389e75927..250db7c64 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
@@ -11,7 +11,11 @@ export class PointerDataType extends AbstractPointerDataType {
super(dataItem, dataTypeFactory, destDataType, parentDataType);
}
- public getSignature(): string {
- return this._destination.getSignature();
+ public getSignatureType(): string {
+ return this._destination.getSignature(false);
+ }
+
+ public getSignature(isDetailed?: boolean): string {
+ return this._destination.getSignature(isDetailed);
}
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
index 2e371c505..cbf1957d7 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
@@ -36,7 +36,7 @@ export class StaticBytesDataType extends AbstractBlobDataType {
this._width = StaticBytesDataType._decodeWidthFromType(dataItem.type);
}
- public getSignature(): string {
+ public getSignatureType(): string {
// Note that `byte` reduces to `bytes1`
return `${SolidityTypes.Bytes}${this._width}`;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts
index 91a72ad3f..97ac46442 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/string.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts
@@ -52,7 +52,7 @@ export class StringDataType extends AbstractBlobDataType {
return value;
}
- public getSignature(): string {
+ public getSignatureType(): string {
return SolidityTypes.String;
}
/* tslint:enable prefer-function-over-method */
diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
index 31593c882..5000c85e8 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
@@ -1,11 +1,10 @@
import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as _ from 'lodash';
import { DataTypeFactory } from '../abstract_data_types/interfaces';
import { AbstractSetDataType } from '../abstract_data_types/types/set';
export class TupleDataType extends AbstractSetDataType {
- private readonly _signature: string;
-
public static matchType(type: string): boolean {
return type === SolidityTypes.Tuple;
}
@@ -15,10 +14,21 @@ export class TupleDataType extends AbstractSetDataType {
if (!TupleDataType.matchType(dataItem.type)) {
throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`);
}
- this._signature = this._computeSignatureOfMembers();
}
- public getSignature(): string {
- return this._signature;
+ public getSignatureType(): string {
+ return this._computeSignatureOfMembers(false);
+ }
+
+ public getSignature(isDetailed?: boolean): string {
+ if (_.isEmpty(this.getDataItem().name) || !isDetailed) {
+ return this.getSignatureType();
+ }
+ const name = this.getDataItem().name;
+ const lastIndexOfScopeDelimiter = name.lastIndexOf('.');
+ const isScopedName = !_.isUndefined(lastIndexOfScopeDelimiter) && lastIndexOfScopeDelimiter > 0;
+ const shortName = isScopedName ? name.substr((lastIndexOfScopeDelimiter as number) + 1) : name;
+ const detailedSignature = `${shortName} ${this._computeSignatureOfMembers(isDetailed)}`;
+ return detailedSignature;
}
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts
index 5180f0cf3..8e382e8dc 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts
@@ -46,13 +46,17 @@ export class UIntDataType extends AbstractBlobDataType {
return encodedValue;
}
- public decodeValue(calldata: RawCalldata): BigNumber {
+ public decodeValue(calldata: RawCalldata): BigNumber | number {
const valueBuf = calldata.popWord();
const value = EncoderMath.safeDecodeNumericValue(valueBuf, UIntDataType._MIN_VALUE, this._maxValue);
+ const numberOfBytesInUint8 = 8;
+ if (this._width === numberOfBytesInUint8) {
+ return value.toNumber();
+ }
return value;
}
- public getSignature(): string {
+ public getSignatureType(): string {
return `${SolidityTypes.Uint}${this._width}`;
}
}
diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts
index baf844ac6..cfacfe075 100644
--- a/packages/utils/src/abi_encoder/index.ts
+++ b/packages/utils/src/abi_encoder/index.ts
@@ -11,4 +11,6 @@ export {
String,
Tuple,
UInt,
+ create,
} from './evm_data_type_factory';
+export { DataType } from './abstract_data_types/data_type';
diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts
index 36de2dd4f..fc586f295 100644
--- a/packages/utils/src/abi_encoder/utils/constants.ts
+++ b/packages/utils/src/abi_encoder/utils/constants.ts
@@ -11,7 +11,7 @@ export const constants = {
HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0,
// Disable no-object-literal-type-assertion so we can enforce cast
/* tslint:disable no-object-literal-type-assertion */
- DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: false } as DecodingRules,
+ DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: true } as DecodingRules,
DEFAULT_ENCODING_RULES: { shouldOptimize: true, shouldAnnotate: false } as EncodingRules,
/* tslint:enable no-object-literal-type-assertion */
};
diff --git a/packages/utils/src/abi_encoder/utils/signature_parser.ts b/packages/utils/src/abi_encoder/utils/signature_parser.ts
new file mode 100644
index 000000000..315784cea
--- /dev/null
+++ b/packages/utils/src/abi_encoder/utils/signature_parser.ts
@@ -0,0 +1,101 @@
+import { DataItem } from 'ethereum-types';
+import * as _ from 'lodash';
+
+/**
+ * Returns an array of DataItem's corresponding to the input signature.
+ * A signature can be in two forms: '<DataItem.type>' or '(<DataItem1.type>, <DataItem2.type>, ...)
+ * An example of the first form would be 'address' or 'uint256'
+ * An example of the second form would be '(address, uint256)'
+ * Signatures can also include a name field, for example: 'foo address' or '(foo address, bar uint256)'
+ * @param signature of input DataItems
+ * @return DataItems derived from input signature
+ */
+export function generateDataItemsFromSignature(signature: string): DataItem[] {
+ let trimmedSignature = signature;
+ if (signature.startsWith('(')) {
+ if (!signature.endsWith(')')) {
+ throw new Error(`Failed to generate data item. Must end with ')'`);
+ }
+ trimmedSignature = signature.substr(1, signature.length - 2);
+ }
+ trimmedSignature += ',';
+ let isCurrTokenArray = false;
+ let currTokenArrayModifier = '';
+ let isParsingArrayModifier = false;
+ let currToken = '';
+ let parenCount = 0;
+ let currTokenName = '';
+ const dataItems: DataItem[] = [];
+ for (const char of trimmedSignature) {
+ // Tokenize the type string while keeping track of parentheses.
+ switch (char) {
+ case '(':
+ parenCount += 1;
+ currToken += char;
+ break;
+ case ')':
+ parenCount -= 1;
+ currToken += char;
+ break;
+ case '[':
+ if (parenCount === 0) {
+ isParsingArrayModifier = true;
+ isCurrTokenArray = true;
+ currTokenArrayModifier += '[';
+ } else {
+ currToken += char;
+ }
+ break;
+ case ']':
+ if (parenCount === 0) {
+ isParsingArrayModifier = false;
+ currTokenArrayModifier += ']';
+ } else {
+ currToken += char;
+ }
+ break;
+ case ' ':
+ if (parenCount === 0) {
+ currTokenName = currToken;
+ currToken = '';
+ } else {
+ currToken += char;
+ }
+ break;
+ case ',':
+ if (parenCount === 0) {
+ // Generate new DataItem from token
+ const components = currToken.startsWith('(') ? generateDataItemsFromSignature(currToken) : [];
+ const isTuple = !_.isEmpty(components);
+ const dataItem: DataItem = { name: currTokenName, type: '' };
+ if (isTuple) {
+ dataItem.type = 'tuple';
+ dataItem.components = components;
+ } else {
+ dataItem.type = currToken;
+ }
+ if (isCurrTokenArray) {
+ dataItem.type += currTokenArrayModifier;
+ }
+ dataItems.push(dataItem);
+ // reset token state
+ currTokenName = '';
+ currToken = '';
+ isCurrTokenArray = false;
+ currTokenArrayModifier = '';
+ break;
+ } else {
+ currToken += char;
+ break;
+ }
+ default:
+ if (isParsingArrayModifier) {
+ currTokenArrayModifier += char;
+ } else {
+ currToken += char;
+ }
+ break;
+ }
+ }
+ return dataItems;
+}