This repository has been archived by the owner on Jan 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 201
Add tuple support in interactive prompts #1333
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
cc4c392
Move getPlaceholder to utils
spalladino 52c2376
Add tuple to getPlaceholder
spalladino 8b77707
Parse tuples in interactive commands
spalladino 96371aa
Add test case in ImplV1 contract
spalladino d431c7f
Show tuple types in interactive prompt
spalladino cf380a6
Fix function name parsing when it has a tuple argument
spalladino 6d2924a
Use parens instead of brackets when parsing tuples
spalladino aa53001
Lint
spalladino ab91d3d
Remove duplicated call
spalladino 216bfd2
Fix tests in prompt
spalladino d179790
Implement suggestions by @frangio
spalladino 70afef8
Fix partial renaming
spalladino b0eeba0
Fix partial renaming V2
spalladino 20f5c19
Merge branch 'master' into feature/support-tuples-input-#1330
frangio c4e3105
Lint
spalladino File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pragma solidity ^0.5.0; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "./ImplV1.sol"; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
import BN from 'bignumber.js'; | ||
import flattenDeep from 'lodash.flattendeep'; | ||
import { encodeParams, Loggy, ZWeb3 } from '@openzeppelin/upgrades'; | ||
import { MethodArgType } from '../prompts/prompt'; | ||
import zipWith from 'lodash.zipwith'; | ||
|
||
// TODO: Deprecate in favor of a combination of parseArg and parseArray | ||
export function parseArgs(args: string): string[] | never { | ||
|
@@ -46,18 +48,24 @@ function quoteArguments(args: string) { | |
return args; | ||
} | ||
|
||
export function parseArg(input: string | string[], type: string): any { | ||
export function parseArg(input: string | string[], { type, components }: MethodArgType): any { | ||
const TRUE_VALUES = ['y', 'yes', 't', 'true', '1']; | ||
const FALSE_VALUES = ['n', 'no', 'f', 'false', '0']; | ||
const ARRAY_TYPE_REGEX = /(.+)\[\d*\]$/; // matches array type identifiers like uint[] or byte[4] | ||
|
||
// TODO: Handle tuples in ABI specification | ||
// Tuples: recursively parse | ||
if (type === 'tuple') { | ||
const inputs = typeof input === 'string' ? parseArray(stripParens(input), '(', ')') : input; | ||
if (inputs.length !== components.length) | ||
throw new Error(`Expected ${components.length} values but got ${input.length}`); | ||
return zipWith(inputs, components, parseArg); | ||
frangio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// Arrays: recursively parse | ||
if (type.match(ARRAY_TYPE_REGEX)) { | ||
else if (type.match(ARRAY_TYPE_REGEX)) { | ||
const arrayType = type.match(ARRAY_TYPE_REGEX)[1]; | ||
const inputs = typeof input === 'string' ? parseArray(stripBrackets(input)) : input; | ||
return inputs.map(input => parseArg(input, arrayType)); | ||
return inputs.map(input => parseArg(input, { type: arrayType })); | ||
} | ||
|
||
// Integers: passed via bignumber to handle signs and scientific notation | ||
|
@@ -110,6 +118,10 @@ export function stripBrackets(inputMaybeWithBrackets: string): string { | |
return `${inputMaybeWithBrackets.replace(/^\s*\[/, '').replace(/\]\s*$/, '')}`; | ||
} | ||
|
||
export function stripParens(inputMaybeWithParens: string): string { | ||
return `${inputMaybeWithParens.replace(/^\s*\(/, '').replace(/\)\s*$/, '')}`; | ||
} | ||
|
||
function requireInputString(arg: string | string[]): arg is string { | ||
if (typeof arg !== 'string') { | ||
throw new Error(`Expected ${flattenDeep(arg).join(',')} to be a scalar value but was an array`); | ||
|
@@ -126,7 +138,7 @@ function requireInputString(arg: string | string[]): arg is string { | |
* with arbitrarily nested string arrays, but ts has a hard time handling that, | ||
* so we're fooling it into thinking it's just one level deep. | ||
*/ | ||
export function parseArray(input: string): (string | string[])[] { | ||
export function parseArray(input: string, open = '[', close = ']'): (string | string[])[] { | ||
let i = 0; // index for traversing input | ||
|
||
function innerParseQuotedString(quoteChar: string): string { | ||
|
@@ -148,9 +160,9 @@ export function parseArray(input: string): (string | string[])[] { | |
return input.slice(start, i - 1); | ||
} else if (char === '"' || char === "'") { | ||
throw new Error(`Unexpected quote at position ${i}`); | ||
} else if (char === '[' || char === "'") { | ||
} else if (char === open || char === "'") { | ||
throw new Error(`Unexpected opening bracket at position ${i}`); | ||
} else if (char === ']') { | ||
} else if (char === close) { | ||
return input.slice(start, --i); | ||
} | ||
} | ||
|
@@ -164,7 +176,7 @@ export function parseArray(input: string): (string | string[])[] { | |
continue; | ||
} else if (char === ',') { | ||
return; | ||
} else if (char === ']') { | ||
} else if (char === close) { | ||
i--; | ||
return; | ||
} else { | ||
|
@@ -183,11 +195,11 @@ export function parseArray(input: string): (string | string[])[] { | |
} else if (char === '"' || char === "'") { | ||
result.push(innerParseQuotedString(char)); | ||
requireCommaOrClosing(); | ||
} else if (char === '[') { | ||
} else if (char === open) { | ||
const innerArray = innerParseArray(); | ||
result.push(innerArray); | ||
requireCommaOrClosing(); | ||
} else if (char === ']') { | ||
} else if (char === close) { | ||
if (!requireClosingBracket) throw new Error(`Unexpected closing array at position ${i + 1} in ${input}`); | ||
return result; | ||
} else { | ||
|
@@ -238,3 +250,35 @@ export function validateSalt(salt: string, required = false) { | |
throw new Error(`Invalid salt ${salt}, must be an uint256 value.`); | ||
} | ||
} | ||
|
||
export function getSampleInput(arg: MethodArgType): string | null { | ||
const ARRAY_TYPE_REGEX = /(.+)\[\d*\]$/; // matches array type identifiers like uint[] or byte[4] | ||
const { type, components } = arg; | ||
|
||
if (type.match(ARRAY_TYPE_REGEX)) { | ||
const arrayType = type.match(ARRAY_TYPE_REGEX)[1]; | ||
const itemPlaceholder = getSampleInput({ type: arrayType }); | ||
return `[${itemPlaceholder}, ${itemPlaceholder}]`; | ||
} else if ( | ||
type.startsWith('uint') || | ||
type.startsWith('int') || | ||
type.startsWith('fixed') || | ||
type.startsWith('ufixed') | ||
) { | ||
return '42'; | ||
} else if (type === 'bool') { | ||
return 'true'; | ||
} else if (type === 'bytes') { | ||
return '0xabcdef'; | ||
} else if (type === 'address') { | ||
return '0x1df62f291b2e969fb0849d99d9ce41e2f137006e'; | ||
} else if (type === 'string') { | ||
return 'Hello world'; | ||
} else if (type === 'tuple' && components) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there cases when |
||
return `(${components.map(c => getSampleInput(c)).join(', ')})`; | ||
} else if (type === 'tuple') { | ||
return `(Hello world, 42)`; | ||
} else { | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it weird that
parseArray
wouldn't handle the delimiters itself. What do you think about doing that?Also, why do we allow the input to not be surrounded by delimiters? In what cases is that used? Does that apply to tuples in the same way that it applies to arrays?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I think it could be refactored. That said, given that this routine is pretty isolated (and working!), I wouldn't risk changing it now.
When the user is prompted to enter an array, they can just enter a comma separated list, without the brackets, for simplicity. Same applies for tuples.