diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index dd36afead..2156b8af1 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -107,13 +107,25 @@ export default class Contract { }); } - deploy (options, values, statecb = () => {}) { - statecb(null, { state: 'estimateGas' }); + deploy (options, values, statecb = () => {}, skipGasEstimate = false) { + let gasEstPromise; - return this - .deployEstimateGas(options, values) - .then(([gasEst, gas]) => { - options.gas = gas.toFixed(0); + if (skipGasEstimate) { + gasEstPromise = Promise.resolve(null); + } else { + statecb(null, { state: 'estimateGas' }); + + gasEstPromise = this.deployEstimateGas(options, values) + .then(([gasEst, gas]) => gas); + } + + return gasEstPromise + .then((_gas) => { + if (_gas) { + options.gas = _gas.toFixed(0); + } + + const gas = _gas || options.gas; statecb(null, { state: 'postTransaction', gas }); diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 3600c13dd..4086b08a3 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -16,12 +16,16 @@ import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; -import { MenuItem } from 'material-ui'; +import { Checkbox, MenuItem } from 'material-ui'; import { AddressSelect, Form, Input, Select } from '~/ui'; import { validateAbi } from '~/util/validation'; import { parseAbiType } from '~/util/abi'; +const CHECK_STYLE = { + marginTop: '1em' +}; + export default class DetailsStep extends Component { static contextTypes = { api: PropTypes.object.isRequired @@ -30,8 +34,10 @@ export default class DetailsStep extends Component { static propTypes = { accounts: PropTypes.object.isRequired, onAbiChange: PropTypes.func.isRequired, + onAmountChange: PropTypes.func.isRequired, onCodeChange: PropTypes.func.isRequired, onDescriptionChange: PropTypes.func.isRequired, + onExtrasChange: PropTypes.func.isRequired, onFromAddressChange: PropTypes.func.isRequired, onInputsChange: PropTypes.func.isRequired, onNameChange: PropTypes.func.isRequired, @@ -39,11 +45,14 @@ export default class DetailsStep extends Component { abi: PropTypes.string, abiError: PropTypes.string, + amount: PropTypes.string, + amountError: PropTypes.string, balances: PropTypes.object, code: PropTypes.string, codeError: PropTypes.string, description: PropTypes.string, descriptionError: PropTypes.string, + extras: PropTypes.bool, fromAddress: PropTypes.string, fromAddressError: PropTypes.string, name: PropTypes.string, @@ -52,6 +61,7 @@ export default class DetailsStep extends Component { }; static defaultProps = { + extras: false, readOnly: false }; @@ -83,7 +93,7 @@ export default class DetailsStep extends Component { fromAddress, fromAddressError, name, nameError, description, descriptionError, - abiError, + abiError, extras, code, codeError } = this.props; @@ -189,10 +199,70 @@ export default class DetailsStep extends Component { value={ code } /> + { this.renderValueInput() } + +
+ + } + onCheck={ this.onCheckExtras } + style={ CHECK_STYLE } + /> +
+ ); } + renderValueInput () { + const { abi, amount, amountError } = this.props; + + let payable = false; + + try { + const parsedAbi = JSON.parse(abi); + + payable = parsedAbi.find((method) => method.type === 'constructor' && method.payable); + } catch (error) { + return null; + } + + if (!payable) { + return null; + } + + return ( + + } + label={ + + } + min={ 0 } + step={ 0.1 } + type='number' + onChange={ this.onAmountChange } + value={ amount } + /> + ); + } + renderContractSelect () { const { contracts } = this.state; @@ -295,6 +365,16 @@ export default class DetailsStep extends Component { onDescriptionChange(description); } + onAmountChange = (event, value) => { + const { onAmountChange } = this.props; + + onAmountChange(value); + } + + onCheckExtras = () => { + this.props.onExtrasChange(!this.props.extras); + } + onAbiChange = (abi) => { const { api } = this.context; const { onAbiChange, onParamsChange, onInputsChange } = this.props; diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 802de64fb..3e7c83d6d 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import BigNumber from 'bignumber.js'; import { pick } from 'lodash'; import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; @@ -22,12 +23,13 @@ import { connect } from 'react-redux'; import { BusyStep, Button, CompletedStep, CopyToClipboard, GasPriceEditor, IdentityIcon, Portal, TxHash, Warning } from '~/ui'; import { CancelIcon, DoneIcon } from '~/ui/Icons'; -import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation'; +import { ERRORS, validateAbi, validateCode, validateName, validatePositiveNumber } from '~/util/validation'; import { deploy, deployEstimateGas } from '~/util/tx'; import DetailsStep from './DetailsStep'; import ParametersStep from './ParametersStep'; import ErrorStep from './ErrorStep'; +import Extras from '../Transfer/Extras'; import styles from './deployContract.css'; @@ -50,6 +52,14 @@ const STEPS = { /> ) }, + EXTRAS: { + title: ( + + ) + }, DEPLOYMENT: { waiting: true, title: ( @@ -97,12 +107,16 @@ class DeployContract extends Component { state = { abi: '', abiError: ERRORS.invalidAbi, + amount: '0', + amountValue: new BigNumber(0), + amountError: '', code: '', codeError: ERRORS.invalidCode, deployState: '', deployError: null, description: '', descriptionError: null, + extras: false, fromAddress: Object.keys(this.props.accounts)[0], fromAddressError: null, name: '', @@ -144,7 +158,19 @@ class DeployContract extends Component { const realStepKeys = deployError || rejected ? [] - : Object.keys(STEPS).filter((k) => k !== 'CONTRACT_PARAMETERS' || inputs.length > 0); + : Object.keys(STEPS) + .filter((k) => { + if (k === 'CONTRACT_PARAMETERS') { + return inputs.length > 0; + } + + if (k === 'EXTRAS') { + return this.state.extras; + } + + return true; + }); + const realStep = realStepKeys.findIndex((k) => k === step); const realSteps = realStepKeys.length ? realStepKeys.map((k) => STEPS[k]) @@ -207,8 +233,8 @@ class DeployContract extends Component { } renderDialogActions () { - const { deployError, abiError, codeError, nameError, descriptionError, fromAddressError, fromAddress, step } = this.state; - const isValid = !nameError && !fromAddressError && !descriptionError && !abiError && !codeError; + const { deployError, abiError, amountError, codeError, nameError, descriptionError, fromAddressError, fromAddress, step } = this.state; + const isValid = !nameError && !fromAddressError && !descriptionError && !abiError && !codeError && !amountError; const cancelBtn = (