Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Feature) Multiple outputs fields for contract calls #219

Merged
merged 24 commits into from
Dec 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
898672b
multiple outputs support
vbaranov Dec 10, 2018
20736b2
ESLinter: temporarily disable unused functions and constants for disa…
vbaranov Dec 11, 2018
a15e103
e2e:fix for contract-call-multiple-outputs pr
dennis00010011b Dec 11, 2018
be6df9c
e2e:lint fix
dennis00010011b Dec 11, 2018
54ecaf3
fix test-deps security audit test
dennis00010011b Dec 12, 2018
cac9962
audit fix
dennis00010011b Dec 12, 2018
bfba634
Merge remote-tracking branch 'origin/develop' into contract-call-mult…
vbaranov Dec 12, 2018
2cb0507
e2e:sync with upstream
dennis00010011b Dec 12, 2018
3add7fc
e2e:remove pakage-lock.json
dennis00010011b Dec 12, 2018
30ebe9b
e2e: sync package-json.lock
dennis00010011b Dec 12, 2018
439c6f9
e2e:improve stability
dennis00010011b Dec 12, 2018
3e2a3e0
e2e:lint fix
dennis00010011b Dec 12, 2018
96612fd
Merge pull request #221 from dennis00010011b/e2e-contract-multiply-ou…
vbaranov Dec 12, 2018
e35cfed
package-lock.json update
vbaranov Dec 14, 2018
71eaa04
Support of various output types fixes
vbaranov Dec 14, 2018
267807f
copy icons for output fields
vbaranov Dec 14, 2018
fed6cee
FF scroll appearance fix
vbaranov Dec 14, 2018
f5b5652
Handle bool input
vbaranov Dec 18, 2018
f46bb26
handle bignumbers
vbaranov Dec 18, 2018
ea9b548
eslint fix and int type support
vbaranov Dec 18, 2018
1263a0b
e2e:outputs type address
dennis00010011b Dec 18, 2018
361bfa4
e2e: execute method check bool, uint, int, bytes
dennis00010011b Dec 19, 2018
99c79e0
Merge pull request #225 from dennis00010011b/e2e-outputs
vbaranov Dec 19, 2018
1e2cf39
Merge branch 'develop' into contract-call-multiple-outputs
vbaranov Dec 20, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 121 additions & 67 deletions old-ui/app/components/send/send-contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,23 @@ import abiEncoder from 'web3-eth-abi'
import Web3 from 'web3'
import copyToClipboard from 'copy-to-clipboard'

class SendTransactionInput extends Component {
class SendTransactionField extends Component {
constructor (props) {
super(props)
this.state = {
inputVal: props.defaultValue,
val: props.defaultValue,
}
this.timerID = null
}

static propTypes = {
placeholder: PropTypes.string,
defaultValue: PropTypes.string,
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]),
disabled: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func,
}
Expand All @@ -31,38 +36,41 @@ class SendTransactionInput extends Component {
return (
<input
type="text"
className="input large-input"
className="input large-input output"
placeholder={this.props.placeholder}
value={this.state.inputVal}
value={this.state.val}
disabled={this.props.disabled}
onChange={e => {
this.setState({
inputVal: e.target.value,
})
this.props.onChange(e)
}
}
this.setState({
val: e.target.value,
})
this.props.onChange(e)
}}
style={{ marginTop: '5px' }}
/>
)
)
}
}

class SendTransactionScreen extends PersistentForm {
constructor (props) {
super(props)
this.state = {
web3: new Web3(global.ethereumProvider),
options: [],
abi: [],
methodSelected: props.methodSelected,
methodABI: props.methodABI,
methodInputs: [],
methodOutputs: [],
methodInputsView: [],
methodOutput: null,
methodOutputsView: [],
isConstantMethod: false,
inputValues: props.inputValues || {},
output: '',
outputValues: props.outputValues || {},
copyDisabled: true,
}

PersistentForm.call(this)
}

Expand Down Expand Up @@ -101,15 +109,16 @@ class SendTransactionScreen extends PersistentForm {
methodSelected: opt.value,
isConstantMethod: opt.metadata.constant,
methodABI: opt.metadata,
output: '',
outputValues: {},
inputValues: {},
}, () => {
this.generateMethodFieldsView(opt.metadata)
})
this.generateMethodInputsView(opt.metadata)
}}
/>
<div style={{ overflow: 'auto', maxHeight: this.state.isConstantMethod ? '120px' : '210px' }}>
{this.state.methodInputsView}
</div>
</div>
<div style={{ padding: '0 30px', overflow: 'auto', 'maxHeight': '280px' }}>
{this.state.methodInputsView}
{this.state.isConstantMethod && this.methodOutput()}
{this.buttonsSection()}
</div>
Expand All @@ -119,14 +128,14 @@ class SendTransactionScreen extends PersistentForm {

componentDidMount () {
if (this.props.methodSelected) {
this.generateMethodInputsView(this.props.methodABI)
this.generateMethodFieldsView(this.props.methodABI)
}
}

async getContractMethods () {
const contractProps = await this.props.getContract(this.props.address)
const abi = contractProps && contractProps.abi
const options = abi && abi.reduce((filtered, obj) => {
const options = abi && Array.isArray(abi) && abi.reduce((filtered, obj) => {
if (obj.type === 'function') {
filtered.push({ label: obj.name, value: obj.name, metadata: obj })
}
Expand All @@ -139,37 +148,59 @@ class SendTransactionScreen extends PersistentForm {
})
}

generateMethodInput (params, ind) {
generateMethodField (params, ind, isInput) {
const { inputValues, outputValues, web3 } = this.state
const paramName = isInput ? 'Input' : 'Output'
const defaultInputValue = (inputValues && inputValues[ind]) || ''
const defaultOutputValue = params.type === 'bool' ? outputValues && outputValues[ind] : (outputValues && outputValues[ind]) || ''
let defaultValue = isInput ? defaultInputValue : defaultOutputValue
if (Array.isArray(defaultValue)) {
defaultValue = defaultValue.join(', ')
} else if ((params.type.startsWith('uint') || params.type.startsWith('int')) && !isNaN(Number(defaultValue)) && Number(defaultValue) > 0) {
defaultValue = web3.toBigNumber(defaultValue).toFixed()
} else if (defaultValue) {
defaultValue = defaultValue.toString()
}
const label = (
<h3
key={`method_label_${ind}`}
style={{ marginTop: '10px' }}
>
{params.name || `Input ${ind + 1}`}
{params.name || `${paramName} ${ind + 1}`}
{!isInput ? <i
className="clipboard cursor-pointer"
style={{ marginLeft: '10px' }}
onClick={(e) => { copyToClipboard(defaultValue) }}
/> : null}
</h3>
)
const input = (
<SendTransactionInput
const field = (
<SendTransactionField
key={Math.random()}
ind={ind}
disabled={!isInput}
placeholder={params.type}
defaultValue={(this.props.inputValues && this.props.inputValues[ind]) || ''}
onChange={e => this.handleInputChange(e, ind)}
defaultValue={defaultValue}
onChange={e => isInput ? this.handleInputChange(e.target.value, params.type, ind) : null}
/>
)
const inputObj = (
const fieldObj = (
<div key={`method_label_container_${ind}`}>
{label}
{input}
{field}
</div>
)
return inputObj
return fieldObj
}

handleInputChange (e, ind) {
handleInputChange (val, type, ind) {
const { inputValues } = this.state
if (e.target.value) {
inputValues[ind] = e.target.value
if (val) {
if (type === 'bool') {
inputValues[ind] = (val === 'true')
} else {
inputValues[ind] = val
}
} else {
delete inputValues[ind]
}
Expand All @@ -178,53 +209,51 @@ class SendTransactionScreen extends PersistentForm {
})
}

generateMethodInputsView (metadata) {
generateMethodFieldsView (metadata) {
this.setState({
methodInputs: [],
methodInputsView: [],
methodOutputs: [],
methodOutputsView: [],
})
const methodInputsView = []
const methodInputs = metadata && metadata.inputs
const methodOutputsView = []
const methodOutputs = metadata && metadata.outputs
methodInputs.forEach((input, ind) => {
methodInputsView.push(this.generateMethodInput(input, ind))
methodInputsView.push(this.generateMethodField(input, ind, true))
})
methodOutputs.forEach((output, ind) => {
methodOutputsView.push(this.generateMethodField(output, ind, false))
})
this.setState({
methodInputs,
methodInputsView,
methodOutputs,
methodOutputsView,
})
}

updateOutputsView () {
const methodOutputsView = []
this.state.methodOutputs.forEach((output, ind) => {
methodOutputsView.push(this.generateMethodField(output, ind, false))
})
this.setState({
methodOutputsView,
})
}

methodOutput () {
const label = (
<h2
key="method_output_label"
style={{
marginTop: '10px',
}}
>
Output
</h2>
)
const output = (
<textarea
key="method_output_value"
className="input large-input"
disabled={true}
value={this.state.output}
style={{
marginTop: '5px',
width: '100%',
height: '50px',
}}
/>
)
const outputObj = (
return (
<div>
{label}
{output}
<h3
className="flex-center"
style={{ marginTop: '10px' }}
>Output data</h3>
{this.state.methodOutputsView}
</div>
)
return outputObj
}

buttonsSection () {
Expand Down Expand Up @@ -265,9 +294,8 @@ class SendTransactionScreen extends PersistentForm {

callData = () => {
this.props.showLoadingIndication()
const { abi, methodSelected, inputValues } = this.state
const { abi, methodSelected, inputValues, methodOutputs, methodOutputsView, web3 } = this.state
const { address } = this.props
const web3 = new Web3(global.ethereumProvider)

const inputValuesArray = Object.keys(inputValues).map(key => inputValues[key])
try {
Expand All @@ -277,18 +305,44 @@ class SendTransactionScreen extends PersistentForm {
this.props.hideToast()
return this.props.displayWarning(err)
}
if (output) {
this.setState({
output,
const outputValues = {}
if (methodOutputsView.length > 1) {
output.forEach((val, ind) => {
const type = methodOutputs && methodOutputs[ind] && methodOutputs[ind].type
outputValues[ind] = this.setOutputValue(val, type)
})
} else {
const type = methodOutputs && methodOutputs[0] && methodOutputs[0].type
outputValues[0] = this.setOutputValue(output, type)
}
this.setState({
outputValues,
})
this.updateOutputsView()
})
} catch (e) {
this.props.hideToast()
return this.props.displayWarning(e)
}
}

setOutputValue = (val, type) => {
console.log(val)
if (!type) {
return val || ''
}
if (!val) {
if (type === 'bool') {
return val
}
return ''
}
if ((type.startsWith('uint') || type.startsWith('int')) && !type.endsWith('[]')) {
return val.toFixed().toString()
}
return val
}

encodeFunctionCall = () => {
const { inputValues, methodABI } = this.state
const inputValuesArray = Object.keys(inputValues).map(key => inputValues[key])
Expand Down
Loading