diff options
Diffstat (limited to 'packages/utils/src/abi_encoder/utils/signature_parser.ts')
-rw-r--r-- | packages/utils/src/abi_encoder/utils/signature_parser.ts | 151 |
1 files changed, 69 insertions, 82 deletions
diff --git a/packages/utils/src/abi_encoder/utils/signature_parser.ts b/packages/utils/src/abi_encoder/utils/signature_parser.ts index 315784cea..d3996bf8e 100644 --- a/packages/utils/src/abi_encoder/utils/signature_parser.ts +++ b/packages/utils/src/abi_encoder/utils/signature_parser.ts @@ -1,101 +1,88 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; +interface Node { + name: string; + value: string; + children: Node[]; + parent?: Node; +} + +function parseNode(node: Node): DataItem { + const components: DataItem[] = []; + _.each(node.children, (child: Node) => { + const component = parseNode(child); + components.push(component); + }); + const dataItem: DataItem = { + name: node.name, + type: node.value, + }; + if (!_.isEmpty(components)) { + dataItem.components = components; + } + return dataItem; +} + /** - * 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 + * Returns a DataItem corresponding to the input signature. + * A signature can be in two forms: `type` or `(type_1,type_2,...,type_n)` + * An example of the first form would be 'address' or 'uint256[]' or 'bytes[5][]' + * An example of the second form would be '(address,uint256)' or '(address,uint256)[]' + * @param signature of input DataItem. + * @return DataItem 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); +export function generateDataItemFromSignature(signature: string): DataItem { + // No data item corresponds to an empty signature + if (_.isEmpty(signature)) { + throw new Error(`Cannot parse data item from empty signature, ''`); } - 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. + // Create a parse tree for data item + let node: Node = { + name: '', + value: '', + children: [], + }; + for (const char of signature) { switch (char) { case '(': - parenCount += 1; - currToken += char; + const child = { + name: '', + value: '', + children: [], + parent: node, + }; + node.value = 'tuple'; + node.children.push(child); + node = child; break; + case ')': - parenCount -= 1; - currToken += char; + node = node.parent as Node; break; - case '[': - if (parenCount === 0) { - isParsingArrayModifier = true; - isCurrTokenArray = true; - currTokenArrayModifier += '['; - } else { - currToken += char; - } - break; - case ']': - if (parenCount === 0) { - isParsingArrayModifier = false; - currTokenArrayModifier += ']'; - } else { - currToken += char; - } + + case ',': + const sibling = { + name: '', + value: '', + children: [], + parent: node.parent, + }; + (node.parent as Node).children.push(sibling); + node = sibling; break; + case ' ': - if (parenCount === 0) { - currTokenName = currToken; - currToken = ''; - } else { - currToken += char; - } + node.name = node.value; + node.value = ''; 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; - } + node.value += char; break; } } - return dataItems; + // Interpret data item from parse tree + const dataItem = parseNode(node); + return dataItem; } |