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 = (
+ }
+ key='create'
+ label={
+
+ }
+ onClick={ this.onDeployStart }
+ />
+ );
+
+ const nextButton = (
+
+ }
+ label={
+
+ }
+ onClick={ this.onNextStep }
+ />
+ );
+
+ const hasParameters = this.state.inputs.length > 0;
+ const showExtras = this.state.extras;
+
switch (step) {
case 'CONTRACT_DETAILS':
return [
cancelBtn,
-
- }
- label={
-
- }
- onClick={ this.onParametersStep }
- />
+ hasParameters || showExtras
+ ? nextButton
+ : createButton
];
case 'CONTRACT_PARAMETERS':
return [
cancelBtn,
-
- }
- key='create'
- label={
-
- }
- onClick={ this.onDeployStart }
- />
+ showExtras
+ ? nextButton
+ : createButton
+ ];
+
+ case 'EXTRAS':
+ return [
+ cancelBtn,
+ createButton
];
case 'DEPLOYMENT':
@@ -344,6 +391,8 @@ class DeployContract extends Component {
{ ...this.state }
accounts={ accounts }
balances={ balances }
+ onAmountChange={ this.onAmountChange }
+ onExtrasChange={ this.onExtrasChange }
onFromAddressChange={ this.onFromAddressChange }
onDescriptionChange={ this.onDescriptionChange }
onNameChange={ this.onNameChange }
@@ -365,6 +414,9 @@ class DeployContract extends Component {
/>
);
+ case 'EXTRAS':
+ return this.renderExtrasPage();
+
case 'DEPLOYMENT':
const body = txhash
?
@@ -411,17 +463,32 @@ class DeployContract extends Component {
}
}
+ renderExtrasPage () {
+ if (!this.gasStore.histogram) {
+ return null;
+ }
+
+ return (
+
+ );
+ }
+
estimateGas = () => {
const { api } = this.context;
- const { abiError, abiParsed, code, codeError, fromAddress, fromAddressError, params } = this.state;
+ const { abiError, abiParsed, amountValue, amountError, code, codeError, fromAddress, fromAddressError, params } = this.state;
- if (abiError || codeError || fromAddressError) {
+ if (abiError || codeError || fromAddressError || amountError) {
return;
}
const options = {
data: code,
- from: fromAddress
+ from: fromAddress,
+ value: amountValue
};
const contract = api.newContract(abiParsed);
@@ -437,6 +504,19 @@ class DeployContract extends Component {
});
}
+ onNextStep = () => {
+ switch (this.state.step) {
+ case 'CONTRACT_DETAILS':
+ return this.onParametersStep();
+
+ case 'CONTRACT_PARAMETERS':
+ return this.onExtrasStep();
+
+ default:
+ console.warn('wrong call of "onNextStep" from', this.state.step);
+ }
+ }
+
onParametersStep = () => {
const { inputs } = this.state;
@@ -444,6 +524,14 @@ class DeployContract extends Component {
return this.setState({ step: 'CONTRACT_PARAMETERS' });
}
+ return this.onExtrasStep();
+ }
+
+ onExtrasStep = () => {
+ if (this.state.extras) {
+ return this.setState({ step: 'EXTRAS' });
+ }
+
return this.onDeployStart();
}
@@ -488,10 +576,24 @@ class DeployContract extends Component {
this.setState(validateCode(code), this.estimateGas);
}
+ onAmountChange = (amount) => {
+ const { numberError } = validatePositiveNumber(amount);
+ const nextAmountValue = numberError
+ ? new BigNumber(0)
+ : this.context.api.util.toWei(amount);
+
+ this.gasStore.setEthValue(nextAmountValue);
+ this.setState({ amount, amountValue: nextAmountValue, amountError: numberError }, this.estimateGas);
+ }
+
+ onExtrasChange = (extras) => {
+ this.setState({ extras });
+ }
+
onDeployStart = () => {
const { api, store } = this.context;
const { source } = this.props;
- const { abiParsed, code, description, name, params, fromAddress } = this.state;
+ const { abiParsed, amountValue, code, description, name, params, fromAddress } = this.state;
const metadata = {
abi: abiParsed,
@@ -503,16 +605,17 @@ class DeployContract extends Component {
source
};
- const options = {
+ const options = this.gasStore.overrideTransaction({
data: code,
- from: fromAddress
- };
+ from: fromAddress,
+ value: amountValue
+ });
this.setState({ step: 'DEPLOYMENT' });
const contract = api.newContract(abiParsed);
- deploy(contract, options, params, metadata, this.onDeploymentState)
+ deploy(contract, options, params, metadata, this.onDeploymentState, true)
.then((address) => {
// No contract address given, might need some confirmations
// from the wallet owners...
diff --git a/js/src/modals/Transfer/Details/details.js b/js/src/modals/Transfer/Details/details.js
index 3dbcca03b..c4bc6a2d8 100644
--- a/js/src/modals/Transfer/Details/details.js
+++ b/js/src/modals/Transfer/Details/details.js
@@ -24,7 +24,7 @@ import { nullableProptype } from '~/util/proptypes';
import TokenSelect from './tokenSelect';
import styles from '../transfer.css';
-const CHECK_STYLE = {
+export const CHECK_STYLE = {
position: 'absolute',
top: '38px',
left: '1em'
diff --git a/js/src/modals/Transfer/Extras/extras.js b/js/src/modals/Transfer/Extras/extras.js
index 61b99b2fb..c3e2d1a8e 100644
--- a/js/src/modals/Transfer/Extras/extras.js
+++ b/js/src/modals/Transfer/Extras/extras.js
@@ -25,12 +25,17 @@ export default class Extras extends Component {
static propTypes = {
data: PropTypes.string,
dataError: PropTypes.string,
+ hideData: PropTypes.bool,
gasStore: PropTypes.object.isRequired,
isEth: PropTypes.bool,
- onChange: PropTypes.func.isRequired,
+ onChange: PropTypes.func,
total: PropTypes.string,
totalError: PropTypes.string
- }
+ };
+
+ static defaultProps = {
+ hideData: false
+ };
render () {
const { gasStore, onChange } = this.props;
@@ -49,9 +54,9 @@ export default class Extras extends Component {
}
renderData () {
- const { isEth, data, dataError } = this.props;
+ const { isEth, data, dataError, hideData } = this.props;
- if (!isEth) {
+ if (!isEth || hideData) {
return null;
}
diff --git a/js/src/ui/Form/Input/input.js b/js/src/ui/Form/Input/input.js
index 51cf8bd05..bf57c8e8d 100644
--- a/js/src/ui/Form/Input/input.js
+++ b/js/src/ui/Form/Input/input.js
@@ -81,6 +81,7 @@ export default class Input extends Component {
tabIndex: PropTypes.number,
type: PropTypes.string,
submitOnBlur: PropTypes.bool,
+ step: PropTypes.number,
style: PropTypes.object,
value: PropTypes.oneOfType([
PropTypes.number,
@@ -124,7 +125,7 @@ export default class Input extends Component {
render () {
const { value } = this.state;
const { autoFocus, children, className, hideUnderline, disabled, error, focused, label } = this.props;
- const { hint, onClick, multiLine, rows, type, min, max, style, tabIndex } = this.props;
+ const { hint, onClick, multiLine, rows, type, min, max, step, style, tabIndex } = this.props;
const readOnly = this.props.readOnly || disabled;
@@ -179,6 +180,7 @@ export default class Input extends Component {
readOnly={ readOnly }
ref='input'
rows={ rows }
+ step={ step }
style={ textFieldStyle }
tabIndex={ tabIndex }
type={ type || 'text' }
diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js
index 22fa30a06..0bf8cce74 100644
--- a/js/src/ui/MethodDecoding/methodDecoding.js
+++ b/js/src/ui/MethodDecoding/methodDecoding.js
@@ -107,7 +107,7 @@ class MethodDecoding extends Component {
renderGas () {
const { historic, transaction } = this.props;
- const { gas, gasPrice } = transaction;
+ const { gas, gasPrice, value } = transaction;
if (!gas || !gasPrice) {
return null;
@@ -126,9 +126,9 @@ class MethodDecoding extends Component {
/>
);
- const gasProvidedEth = (
+ const totalEthValue = (
- { this.renderEtherValue(gas.mul(gasPrice)) }
+ { this.renderEtherValue(gas.mul(gasPrice).plus(value || 0)) }
);
const gasUsed = transaction.gasUsed
@@ -149,12 +149,12 @@ class MethodDecoding extends Component {
{ this.renderMinBlock() }
@@ -349,6 +349,7 @@ class MethodDecoding extends Component {
renderDeploy () {
const { historic, transaction } = this.props;
const { methodInputs } = this.state;
+ const { value } = transaction;
if (!historic) {
return (
@@ -357,6 +358,19 @@ class MethodDecoding extends Component {
id='ui.methodDecoding.deploy.willDeploy'
defaultMessage='Will deploy a contract'
/>
+ {
+ value && value.gt(0)
+ ? (
+
+ )
+ : null
+ }
);
}
diff --git a/js/src/util/tx.js b/js/src/util/tx.js
index 8a48a3d26..3347747d4 100644
--- a/js/src/util/tx.js
+++ b/js/src/util/tx.js
@@ -73,7 +73,7 @@ export function postTransaction (_func, _options, _values = []) {
});
}
-export function deploy (contract, _options, values, metadata = {}, statecb = () => {}) {
+export function deploy (contract, _options, values, metadata = {}, statecb = () => {}, skipGasEstimate = false) {
const options = { ..._options };
const { api } = contract;
const address = options.from;
@@ -82,16 +82,27 @@ export function deploy (contract, _options, values, metadata = {}, statecb = ()
.isWallet(api, address)
.then((isWallet) => {
if (!isWallet) {
- return contract.deploy(options, values, statecb);
+ return contract.deploy(options, values, statecb, skipGasEstimate);
}
- statecb(null, { state: 'estimateGas' });
+ let gasEstPromise;
- return deployEstimateGas(contract, options, values)
- .then(([gasEst, gas]) => {
- options.gas = gas.toFixed(0);
+ if (skipGasEstimate) {
+ gasEstPromise = Promise.resolve(null);
+ } else {
+ statecb(null, { state: 'estimateGas' });
- statecb(null, { state: 'postTransaction', gas });
+ gasEstPromise = deployEstimateGas(contract, options, values)
+ .then(([gasEst, gas]) => gas);
+ }
+
+ return gasEstPromise
+ .then((gas) => {
+ if (gas) {
+ options.gas = gas.toFixed(0);
+ }
+
+ statecb(null, { state: 'postTransaction', gas: options.gas });
return WalletsUtils.getDeployArgs(contract, options, values);
})
diff --git a/js/src/views/WriteContract/writeContract.js b/js/src/views/WriteContract/writeContract.js
index ad7b37977..c4b08e3dd 100644
--- a/js/src/views/WriteContract/writeContract.js
+++ b/js/src/views/WriteContract/writeContract.js
@@ -105,7 +105,7 @@ class WriteContract extends Component {
className={ styles.editor }
style={ { flex: `${size}%` } }
>
- asd{ this.renderTitle() }
+ { this.renderTitle() }