diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 68c0371a116..eacb21fd1e1 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -89,9 +89,15 @@ export default class Contract { return this; } - deploy (options, values, statecb) { - let gas; + deployEstimateGas (options, values) { + return this._api.eth + .estimateGas(this._encodeOptions(this.constructors[0], options, values)) + .then((gasEst) => { + return [gasEst, gasEst.mul(1.2)]; + }); + } + deploy (options, values, statecb) { const setState = (state) => { if (!statecb) { return; @@ -102,31 +108,32 @@ export default class Contract { setState({ state: 'estimateGas' }); - return this._api.eth - .estimateGas(this._encodeOptions(this.constructors[0], options, values)) - .then((_gas) => { - gas = _gas.mul(1.2); + return this + .deployEstimateGas(options, values) + .then(([gasEst, gas]) => { options.gas = gas.toFixed(0); setState({ state: 'postTransaction', gas }); - return this._api.parity.postTransaction(this._encodeOptions(this.constructors[0], options, values)); - }) - .then((requestId) => { - setState({ state: 'checkRequest', requestId }); - return this._pollCheckRequest(requestId); - }) - .then((txhash) => { - setState({ state: 'getTransactionReceipt', txhash }); - return this._pollTransactionReceipt(txhash, gas); - }) - .then((receipt) => { - if (receipt.gasUsed.eq(gas)) { - throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`); - } - setState({ state: 'hasReceipt', receipt }); - this._address = receipt.contractAddress; - return this._address; + return this._api.parity + .postTransaction(this._encodeOptions(this.constructors[0], options, values)) + .then((requestId) => { + setState({ state: 'checkRequest', requestId }); + return this._pollCheckRequest(requestId); + }) + .then((txhash) => { + setState({ state: 'getTransactionReceipt', txhash }); + return this._pollTransactionReceipt(txhash, gas); + }) + .then((receipt) => { + if (receipt.gasUsed.eq(gas)) { + throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`); + } + + setState({ state: 'hasReceipt', receipt }); + this._address = receipt.contractAddress; + return this._address; + }); }) .then((address) => { setState({ state: 'getCode' }); diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index d2fa869a7a8..cfb8aa40f52 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -15,6 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import { MenuItem } from 'material-ui'; import { AddressSelect, Form, Input, Select } from '~/ui'; @@ -92,27 +93,51 @@ export default class DetailsStep extends Component { return (
+ } + label={ + + } onChange={ this.onFromAddressChange } value={ fromAddress } /> + } + label={ + + } onChange={ this.onNameChange } value={ name || '' } /> + } + label={ + + } onChange={ this.onDescriptionChange } value={ description } /> @@ -120,18 +145,34 @@ export default class DetailsStep extends Component { { this.renderContractSelect() } + } + label={ + + } onChange={ this.onSolcChange } onSubmit={ this.onSolcSubmit } readOnly={ readOnly } value={ solcOutput } /> + } + label={ + + } onSubmit={ this.onCodeChange } readOnly={ readOnly || solc } value={ code } @@ -149,22 +190,26 @@ export default class DetailsStep extends Component { } const { selectedContractIndex } = this.state; - const contractsItems = Object.keys(contracts).map((name, index) => ( - - { name } - - )); + const contractsItems = Object + .keys(contracts) + .map((name, index) => ( + + { name } + + )); return ( ); @@ -180,7 +225,9 @@ export default class DetailsStep extends Component { } const { abi, bin } = contract; - const code = /^0x/.test(bin) ? bin : `0x${bin}`; + const code = /^0x/.test(bin) + ? bin + : `0x${bin}`; this.setState({ selectedContractIndex: index }, () => { this.onAbiChange(abi); diff --git a/js/src/modals/DeployContract/ParametersStep/parametersStep.js b/js/src/modals/DeployContract/ParametersStep/parametersStep.js index 0ec62fbd528..8e557a95a53 100644 --- a/js/src/modals/DeployContract/ParametersStep/parametersStep.js +++ b/js/src/modals/DeployContract/ParametersStep/parametersStep.js @@ -30,6 +30,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import { Form, TypedInput } from '~/ui'; import { parseAbiType } from '~/util/abi'; @@ -77,21 +78,24 @@ export default class ParametersStep extends Component { return (
+ value={ value } />
); }); return (
-

Choose the contract parameters

+

+ +

{ inputsComponents }
); diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index aaf5d36aad4..3df992855d4 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -14,13 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { pick } from 'lodash'; +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; -import ContentClear from 'material-ui/svg-icons/content/clear'; -import { pick } from 'lodash'; -import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, TxHash } from '~/ui'; +import { BusyStep, Button, CompletedStep, CopyToClipboard, GasPriceEditor, IdentityIcon, Modal, TxHash, Warning } from '~/ui'; +import { CancelIcon, DoneIcon } from '~/ui/Icons'; import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation'; import DetailsStep from './DetailsStep'; @@ -32,12 +33,34 @@ import styles from './deployContract.css'; import { ERROR_CODES } from '~/api/transport/error'; const STEPS = { - CONTRACT_DETAILS: { title: 'contract details' }, - CONTRACT_PARAMETERS: { title: 'contract parameters' }, - DEPLOYMENT: { title: 'deployment', waiting: true }, - COMPLETED: { title: 'completed' } + CONTRACT_DETAILS: { + title: + + }, + CONTRACT_PARAMETERS: { + title: + + }, + DEPLOYMENT: { + waiting: true, + title: + + }, + COMPLETED: { + title: + + } }; +@observer class DeployContract extends Component { static contextTypes = { api: PropTypes.object.isRequired, @@ -46,10 +69,11 @@ class DeployContract extends Component { static propTypes = { accounts: PropTypes.object.isRequired, - onClose: PropTypes.func.isRequired, - balances: PropTypes.object, abi: PropTypes.string, + balances: PropTypes.object, code: PropTypes.string, + gasLimit: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired, readOnly: PropTypes.bool, source: PropTypes.string }; @@ -59,11 +83,15 @@ class DeployContract extends Component { source: '' }; + gasStore = new GasPriceEditor.Store(this.context.api, { gasLimit: this.props.gasLimit }); + state = { abi: '', abiError: ERRORS.invalidAbi, code: '', codeError: ERRORS.invalidCode, + deployState: '', + deployError: null, description: '', descriptionError: null, fromAddress: Object.keys(this.props.accounts)[0], @@ -73,9 +101,6 @@ class DeployContract extends Component { params: [], paramsError: [], inputs: [], - - deployState: '', - deployError: null, rejected: false, step: 'CONTRACT_DETAILS' } @@ -117,7 +142,14 @@ class DeployContract extends Component { const title = realSteps ? null - : (deployError ? 'deployment failed' : 'rejected'); + : (deployError + ? + : + ); const waiting = realSteps ? realSteps.map((s, i) => s.waiting ? i : false).filter((v) => v !== false) @@ -127,38 +159,69 @@ class DeployContract extends Component { s.title) : null } + steps={ + realSteps + ? realSteps.map((s) => s.title) + : null + } title={ title } - waiting={ waiting } visible - > + waiting={ waiting }> + { this.renderExceptionWarning() } { this.renderStep() } ); } + renderExceptionWarning () { + const { step } = this.state; + const { errorEstimated } = this.gasStore; + const realStep = Object.keys(STEPS).findIndex((k) => k === step); + + if (!errorEstimated || realStep >= 2) { + return null; + } + + return ( + + ); + } + renderDialogActions () { const { deployError, abiError, codeError, nameError, descriptionError, fromAddressError, fromAddress, step } = this.state; const isValid = !nameError && !fromAddressError && !descriptionError && !abiError && !codeError; const cancelBtn = (