diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 6e23f79c9..4fd2bdcf5 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -16,11 +16,7 @@ import React, { Component, PropTypes } from 'react'; -import { AddressSelect, Form, Input, TypedInput } from '../../../ui'; -import { validateAbi } from '../../../util/validation'; -import { parseAbiType } from '../../../util/abi'; - -import styles from '../deployContract.css'; +import { AddressSelect, Form, Input, RadioButtons } from '../../../ui'; export default class DetailsStep extends Component { static contextTypes = { @@ -29,24 +25,17 @@ export default class DetailsStep extends Component { static propTypes = { accounts: PropTypes.object.isRequired, - abi: PropTypes.string, - abiError: PropTypes.string, - code: PropTypes.string, - codeError: PropTypes.string, - description: PropTypes.string, - descriptionError: PropTypes.string, + inputTypeValues: PropTypes.array.isRequired, + + onFromAddressChange: PropTypes.func.isRequired, + onNameChange: PropTypes.func.isRequired, + onInputTypeChange: PropTypes.func.isRequired, + fromAddress: PropTypes.string, fromAddressError: PropTypes.string, name: PropTypes.string, nameError: PropTypes.string, - params: PropTypes.array, - paramsError: PropTypes.array, - onAbiChange: PropTypes.func.isRequired, - onCodeChange: PropTypes.func.isRequired, - onFromAddressChange: PropTypes.func.isRequired, - onDescriptionChange: PropTypes.func.isRequired, - onNameChange: PropTypes.func.isRequired, - onParamsChange: PropTypes.func.isRequired, + inputType: PropTypes.object, readOnly: PropTypes.bool }; @@ -54,25 +43,9 @@ export default class DetailsStep extends Component { readOnly: false }; - state = { - inputs: [] - } - - componentDidMount () { - const { abi, code } = this.props; - - if (abi) { - this.onAbiChange(abi); - } - - if (code) { - this.onCodeChange(code); - } - } - render () { const { accounts } = this.props; - const { abi, abiError, code, codeError, fromAddress, fromAddressError, name, nameError, readOnly } = this.props; + const { fromAddress, fromAddressError, name, nameError } = this.props; return (
@@ -83,61 +56,40 @@ export default class DetailsStep extends Component { error={ fromAddressError } accounts={ accounts } onChange={ this.onFromAddressChange } /> + - - - { this.renderConstructorInputs() } + { this.renderChooseInputType() }
); } - renderConstructorInputs () { - const { accounts, params, paramsError } = this.props; - const { inputs } = this.state; + renderChooseInputType () { + const { readOnly } = this.props; - if (!inputs || !inputs.length) { + if (readOnly) { return null; } - return inputs.map((input, index) => { - const onChange = (value) => this.onParamChange(index, value); + const { inputTypeValues, inputType } = this.props; - const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; - const value = params[index]; - const error = paramsError[index]; - const param = parseAbiType(input.type); - - return ( -
- -
- ); - }); + return ( +
+
+

Choose how ABI and Bytecode will be entered

+ +
+ ); } onFromAddressChange = (event, fromAddress) => { @@ -152,42 +104,8 @@ export default class DetailsStep extends Component { onNameChange(name); } - onParamChange = (index, value) => { - const { params, onParamsChange } = this.props; - - params[index] = value; - onParamsChange(params); - } - - onAbiChange = (abi) => { - const { api } = this.context; - const { onAbiChange, onParamsChange } = this.props; - const { abiError, abiParsed } = validateAbi(abi, api); - - if (!abiError) { - const { inputs } = abiParsed - .find((method) => method.type === 'constructor') || { inputs: [] }; - - const params = []; - - inputs.forEach((input) => { - const param = parseAbiType(input.type); - params.push(param.default); - }); - - onParamsChange(params); - this.setState({ inputs }); - } else { - onParamsChange([]); - this.setState({ inputs: [] }); - } - - onAbiChange(abi); - } - - onCodeChange = (code) => { - const { onCodeChange } = this.props; - - onCodeChange(code); + onInputTypeChange = (inputType, index) => { + const { onInputTypeChange } = this.props; + onInputTypeChange(inputType, index); } } diff --git a/js/src/modals/DeployContract/ParametersStep/index.js b/js/src/modals/DeployContract/ParametersStep/index.js new file mode 100644 index 000000000..77545b406 --- /dev/null +++ b/js/src/modals/DeployContract/ParametersStep/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './parametersStep'; diff --git a/js/src/modals/DeployContract/ParametersStep/parametersStep.js b/js/src/modals/DeployContract/ParametersStep/parametersStep.js new file mode 100644 index 000000000..3e407f482 --- /dev/null +++ b/js/src/modals/DeployContract/ParametersStep/parametersStep.js @@ -0,0 +1,278 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import React, { Component, PropTypes } from 'react'; +import { MenuItem } from 'material-ui'; + +import { Form, Input, TypedInput, Select } from '../../../ui'; +import { validateAbi } from '../../../util/validation'; +import { parseAbiType } from '../../../util/abi'; + +import styles from '../deployContract.css'; + +export default class ParametersStep extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + + static propTypes = { + accounts: PropTypes.object.isRequired, + inputType: PropTypes.object.isRequired, + + onAbiChange: PropTypes.func.isRequired, + onCodeChange: PropTypes.func.isRequired, + onParamsChange: PropTypes.func.isRequired, + + abi: PropTypes.string, + abiError: PropTypes.string, + code: PropTypes.string, + codeError: PropTypes.string, + params: PropTypes.array, + paramsError: PropTypes.array, + + readOnly: PropTypes.bool + }; + + static defaultProps = { + readOnly: false + }; + + state = { + inputs: [], + solcOutput: '', + contracts: {}, + selectedContractIndex: 0 + } + + componentDidMount () { + const { abi, code } = this.props; + + if (abi) { + this.onAbiChange(abi); + } + + if (code) { + this.onCodeChange(code); + } + } + + render () { + const { abi, abiError, code, codeError, readOnly, inputType } = this.props; + + const manualInput = inputType.key === 'MANUAL'; + + return ( +
+ { this.renderFromSOLC() } + + + + { this.renderConstructorInputs() } +
+ ); + } + + renderFromSOLC () { + const { inputType } = this.props; + + if (inputType.key !== 'SOLC') { + return null; + } + + const { solcOutput, contracts } = this.state; + const error = contracts && Object.keys(contracts).length + ? null + : 'enter a valid solc output'; + + return ( +
+ + { this.renderContractSelect() } +
+ ); + } + + renderContractSelect () { + const { contracts } = this.state; + + if (!contracts || Object.keys(contracts).length === 0) { + return null; + } + + const { selectedContractIndex } = this.state; + const contractsItems = Object.keys(contracts).map((name, index) => ( + + { name } + + )); + + return ( + + ); + } + + renderConstructorInputs () { + const { accounts, params, paramsError } = this.props; + const { inputs } = this.state; + + if (!inputs || !inputs.length) { + return null; + } + + const inputsComponents = inputs.map((input, index) => { + const onChange = (value) => this.onParamChange(index, value); + + const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; + const value = params[index]; + const error = paramsError[index]; + const param = parseAbiType(input.type); + + return ( +
+ +
+ ); + }); + + return ( +
+

Choose the contract parameters

+ { inputsComponents } +
+ ); + } + + onContractChange = (event, index) => { + const { contracts } = this.state; + const contractName = Object.keys(contracts)[index]; + const contract = contracts[contractName]; + + const { abi, bin } = contract; + const code = /^0x/.test(bin) ? bin : `0x${bin}`; + + this.setState({ selectedContractIndex: index }, () => { + this.onAbiChange(abi); + this.onCodeChange(code); + }); + } + + onSolcChange = (event, value) => { + try { + const solcParsed = JSON.parse(value); + + if (!solcParsed && !solcParsed.contracts) { + throw new Error('Wrong solc output'); + } + + this.setState({ contracts: solcParsed.contracts }, () => { + this.onContractChange(null, 0); + }); + } catch (e) { + this.setState({ contracts: null }); + } + + this.setState({ solcOutput: value }); + } + + onParamChange = (index, value) => { + const { params, onParamsChange } = this.props; + + params[index] = value; + onParamsChange(params); + } + + onAbiChange = (abi) => { + const { api } = this.context; + const { onAbiChange, onParamsChange } = this.props; + const { abiError, abiParsed } = validateAbi(abi, api); + + if (!abiError) { + const { inputs } = abiParsed + .find((method) => method.type === 'constructor') || { inputs: [] }; + + const params = []; + + inputs.forEach((input) => { + const param = parseAbiType(input.type); + params.push(param.default); + }); + + onParamsChange(params); + this.setState({ inputs }); + } else { + onParamsChange([]); + this.setState({ inputs: [] }); + } + + onAbiChange(abi); + } + + onCodeChange = (code) => { + const { onCodeChange } = this.props; + + onCodeChange(code); + } +} diff --git a/js/src/modals/DeployContract/deployContract.css b/js/src/modals/DeployContract/deployContract.css index 45fd7a852..90097df81 100644 --- a/js/src/modals/DeployContract/deployContract.css +++ b/js/src/modals/DeployContract/deployContract.css @@ -31,3 +31,7 @@ .funcparams { padding-left: 3em; } + +p { + color: rgba(255, 255, 255, 0.498039); +} diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 996948092..db12cf911 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -22,13 +22,32 @@ import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, import { ERRORS, validateAbi, validateCode, validateName } from '../../util/validation'; import DetailsStep from './DetailsStep'; +import ParametersStep from './ParametersStep'; import ErrorStep from './ErrorStep'; import styles from './deployContract.css'; import { ERROR_CODES } from '../../api/transport/error'; -const steps = ['contract details', 'deployment', 'completed']; +const STEPS = { + CONTRACT_DETAILS: { title: 'contract details' }, + CONTRACT_PARAMETERS: { title: 'contract parameters' }, + DEPLOYMENT: { title: 'deployment' }, + COMPLETED: { title: 'completed' } +}; + +const CONTRACT_INPUT_TYPES = [ + { + key: 'MANUAL', + label: 'Manually', + description: 'Manual input of the ABI and the bytecode' + }, + { + key: 'SOLC', + label: 'From solc', + description: 'Parse the ABI and the bytecode from solc output' + } +]; export default class DeployContract extends Component { static contextTypes = { @@ -55,7 +74,6 @@ export default class DeployContract extends Component { abiError: ERRORS.invalidAbi, code: '', codeError: ERRORS.invalidCode, - deployState: '', description: '', descriptionError: null, fromAddress: Object.keys(this.props.accounts)[0], @@ -64,9 +82,12 @@ export default class DeployContract extends Component { nameError: ERRORS.invalidName, params: [], paramsError: [], - step: 0, + inputType: CONTRACT_INPUT_TYPES[0], + + deployState: '', deployError: null, - rejected: false + rejected: false, + step: 'CONTRACT_DETAILS' } componentWillMount () { @@ -97,7 +118,11 @@ export default class DeployContract extends Component { render () { const { step, deployError, rejected } = this.state; - const realSteps = deployError || rejected ? null : steps; + const realStep = Object.keys(STEPS).findIndex((k) => k === step); + const realSteps = deployError || rejected + ? null + : Object.values(STEPS).map((s) => s.title); + const title = realSteps ? null : (deployError ? 'deployment failed' : 'rejected'); @@ -105,10 +130,10 @@ export default class DeployContract extends Component { return ( { this.renderStep() } @@ -118,7 +143,8 @@ export default class DeployContract extends Component { renderDialogActions () { const { deployError, abiError, codeError, nameError, descriptionError, fromAddressError, fromAddress, step } = this.state; - const isValid = !nameError && !fromAddressError && !descriptionError && !abiError && !codeError; + const isDetailsValid = !nameError && !fromAddressError && !descriptionError; + const isParametersValid = !abiError && !codeError; const cancelBtn = (