Gas calculation & limit errors
This commit is contained in:
parent
3f2ba96e97
commit
e69b7f66a3
@ -36,6 +36,7 @@ export default class DetailsStep extends Component {
|
|||||||
onFuncChange: PropTypes.func,
|
onFuncChange: PropTypes.func,
|
||||||
values: PropTypes.array.isRequired,
|
values: PropTypes.array.isRequired,
|
||||||
valuesError: PropTypes.array.isRequired,
|
valuesError: PropTypes.array.isRequired,
|
||||||
|
warning: PropTypes.string,
|
||||||
onValueChange: PropTypes.func.isRequired
|
onValueChange: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ export default class DetailsStep extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
|
{ this.renderWarning() }
|
||||||
<AddressSelect
|
<AddressSelect
|
||||||
label='from account'
|
label='from account'
|
||||||
hint='the account to transact with'
|
hint='the account to transact with'
|
||||||
@ -178,6 +180,20 @@ export default class DetailsStep extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderWarning () {
|
||||||
|
const { warning } = this.props;
|
||||||
|
|
||||||
|
if (!warning) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.warning }>
|
||||||
|
{ warning }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
onFuncChange = (event, index, signature) => {
|
onFuncChange = (event, index, signature) => {
|
||||||
const { contract, onFuncChange } = this.props;
|
const { contract, onFuncChange } = this.props;
|
||||||
|
|
||||||
|
@ -33,3 +33,11 @@
|
|||||||
.txhash {
|
.txhash {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background: #f80;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
@ -15,17 +15,21 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||||
|
|
||||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
||||||
|
import { MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||||
import { validateAddress, validateUint } from '../../util/validation';
|
import { validateAddress, validateUint } from '../../util/validation';
|
||||||
|
|
||||||
import DetailsStep from './DetailsStep';
|
import DetailsStep from './DetailsStep';
|
||||||
|
|
||||||
|
import ERRORS from '../Transfer/errors';
|
||||||
import { ERROR_CODES } from '../../api/transport/error';
|
import { ERROR_CODES } from '../../api/transport/error';
|
||||||
|
|
||||||
export default class ExecuteContract extends Component {
|
class ExecuteContract extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired,
|
api: PropTypes.object.isRequired,
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired
|
||||||
@ -36,6 +40,7 @@ export default class ExecuteContract extends Component {
|
|||||||
fromAddress: PropTypes.string,
|
fromAddress: PropTypes.string,
|
||||||
accounts: PropTypes.object,
|
accounts: PropTypes.object,
|
||||||
contract: PropTypes.object,
|
contract: PropTypes.object,
|
||||||
|
gasLimit: PropTypes.object.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onFromAddressChange: PropTypes.func.isRequired
|
onFromAddressChange: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
@ -46,6 +51,8 @@ export default class ExecuteContract extends Component {
|
|||||||
fromAddressError: null,
|
fromAddressError: null,
|
||||||
func: null,
|
func: null,
|
||||||
funcError: null,
|
funcError: null,
|
||||||
|
gas: null,
|
||||||
|
gasLimitError: null,
|
||||||
values: [],
|
values: [],
|
||||||
valuesError: [],
|
valuesError: [],
|
||||||
step: 0,
|
step: 0,
|
||||||
@ -119,7 +126,7 @@ export default class ExecuteContract extends Component {
|
|||||||
|
|
||||||
renderStep () {
|
renderStep () {
|
||||||
const { onFromAddressChange } = this.props;
|
const { onFromAddressChange } = this.props;
|
||||||
const { step, busyState, txhash, rejected } = this.state;
|
const { step, busyState, gasLimitError, txhash, rejected } = this.state;
|
||||||
|
|
||||||
if (rejected) {
|
if (rejected) {
|
||||||
return (
|
return (
|
||||||
@ -135,6 +142,7 @@ export default class ExecuteContract extends Component {
|
|||||||
<DetailsStep
|
<DetailsStep
|
||||||
{ ...this.props }
|
{ ...this.props }
|
||||||
{ ...this.state }
|
{ ...this.state }
|
||||||
|
warning={ gasLimitError }
|
||||||
onAmountChange={ this.onAmountChange }
|
onAmountChange={ this.onAmountChange }
|
||||||
onFromAddressChange={ onFromAddressChange }
|
onFromAddressChange={ onFromAddressChange }
|
||||||
onFuncChange={ this.onFuncChange }
|
onFuncChange={ this.onFuncChange }
|
||||||
@ -156,6 +164,7 @@ export default class ExecuteContract extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAmountChange = (amount) => {
|
onAmountChange = (amount) => {
|
||||||
|
this.estimateGas();
|
||||||
this.setState({ amount });
|
this.setState({ amount });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +188,7 @@ export default class ExecuteContract extends Component {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.estimateGas();
|
||||||
this.setState({
|
this.setState({
|
||||||
func,
|
func,
|
||||||
values
|
values
|
||||||
@ -208,17 +218,54 @@ export default class ExecuteContract extends Component {
|
|||||||
values[index] = value;
|
values[index] = value;
|
||||||
valuesError[index] = valueError;
|
valuesError[index] = valueError;
|
||||||
|
|
||||||
|
if (!valueError) {
|
||||||
|
this.estimateGas();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
values: [].concat(values),
|
values: [].concat(values),
|
||||||
valuesError: [].concat(valuesError)
|
valuesError: [].concat(valuesError)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
estimateGas = () => {
|
||||||
|
const { api } = this.context;
|
||||||
|
const { fromAddress, gasLimit } = this.props;
|
||||||
|
const { amount, func, values } = this.state;
|
||||||
|
const options = {
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
|
from: fromAddress,
|
||||||
|
value: api.util.toWei(amount || 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
func
|
||||||
|
.estimateGas(options, values)
|
||||||
|
.then((gasEst) => {
|
||||||
|
const gas = gasEst.mul(1.2);
|
||||||
|
let gasLimitError = null;
|
||||||
|
|
||||||
|
if (gas.gte(MAX_GAS_ESTIMATION)) {
|
||||||
|
gasLimitError = ERRORS.gasException;
|
||||||
|
} else if (gas.gt(gasLimit)) {
|
||||||
|
gasLimitError = ERRORS.gasBlockLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
gas,
|
||||||
|
gasLimitError
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('estimateGas', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
postTransaction = () => {
|
postTransaction = () => {
|
||||||
const { api, store } = this.context;
|
const { api, store } = this.context;
|
||||||
const { fromAddress } = this.props;
|
const { fromAddress } = this.props;
|
||||||
const { amount, func, values } = this.state;
|
const { amount, func, values } = this.state;
|
||||||
const options = {
|
const options = {
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
from: fromAddress,
|
from: fromAddress,
|
||||||
value: api.util.toWei(amount || 0)
|
value: api.util.toWei(amount || 0)
|
||||||
};
|
};
|
||||||
@ -237,13 +284,13 @@ export default class ExecuteContract extends Component {
|
|||||||
|
|
||||||
return api
|
return api
|
||||||
.pollMethod('parity_checkRequest', requestId)
|
.pollMethod('parity_checkRequest', requestId)
|
||||||
.catch((e) => {
|
.catch((error) => {
|
||||||
if (e.code === ERROR_CODES.REQUEST_REJECTED) {
|
if (error.code === ERROR_CODES.REQUEST_REJECTED) {
|
||||||
this.setState({ rejected: true });
|
this.setState({ rejected: true });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw error;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((txhash) => {
|
.then((txhash) => {
|
||||||
@ -255,3 +302,18 @@ export default class ExecuteContract extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
const { gasLimit } = state.status;
|
||||||
|
|
||||||
|
return { gasLimit };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return bindActionCreators({}, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ExecuteContract);
|
||||||
|
@ -19,7 +19,9 @@ const ERRORS = {
|
|||||||
invalidAddress: 'the supplied address is an invalid network address',
|
invalidAddress: 'the supplied address is an invalid network address',
|
||||||
invalidAmount: 'the supplied amount should be a valid positive number',
|
invalidAmount: 'the supplied amount should be a valid positive number',
|
||||||
invalidDecimals: 'the supplied amount exceeds the allowed decimals',
|
invalidDecimals: 'the supplied amount exceeds the allowed decimals',
|
||||||
largeAmount: 'the transaction total is higher than the available balance'
|
largeAmount: 'the transaction total is higher than the available balance',
|
||||||
|
gasException: 'the transaction indicates a thrown exception',
|
||||||
|
gasBlockLimit: 'the transaction will exceed the block gas limit'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ERRORS;
|
export default ERRORS;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
/* You should have received a copy of the GNU General Public License
|
/* You should have received a copy of the GNU General Public License
|
||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
line-height: 1.618em;
|
line-height: 1.618em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -151,3 +152,11 @@
|
|||||||
.gasPriceDesc {
|
.gasPriceDesc {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background: #f80;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
@ -16,12 +16,15 @@
|
|||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||||
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
||||||
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||||
|
|
||||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
||||||
|
import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||||
|
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
import Extras from './Extras';
|
import Extras from './Extras';
|
||||||
@ -30,8 +33,6 @@ import styles from './transfer.css';
|
|||||||
|
|
||||||
import { ERROR_CODES } from '../../api/transport/error';
|
import { ERROR_CODES } from '../../api/transport/error';
|
||||||
|
|
||||||
const DEFAULT_GAS = '21000';
|
|
||||||
const DEFAULT_GASPRICE = '20000000000';
|
|
||||||
const TITLES = {
|
const TITLES = {
|
||||||
transfer: 'transfer details',
|
transfer: 'transfer details',
|
||||||
sending: 'sending',
|
sending: 'sending',
|
||||||
@ -42,7 +43,7 @@ const TITLES = {
|
|||||||
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
||||||
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
|
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
|
||||||
|
|
||||||
export default class Transfer extends Component {
|
class Transfer extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired,
|
api: PropTypes.object.isRequired,
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired
|
||||||
@ -52,6 +53,7 @@ export default class Transfer extends Component {
|
|||||||
account: PropTypes.object,
|
account: PropTypes.object,
|
||||||
balance: PropTypes.object,
|
balance: PropTypes.object,
|
||||||
balances: PropTypes.object,
|
balances: PropTypes.object,
|
||||||
|
gasLimit: PropTypes.object.isRequired,
|
||||||
images: PropTypes.object.isRequired,
|
images: PropTypes.object.isRequired,
|
||||||
onClose: PropTypes.func
|
onClose: PropTypes.func
|
||||||
}
|
}
|
||||||
@ -64,6 +66,7 @@ export default class Transfer extends Component {
|
|||||||
gas: DEFAULT_GAS,
|
gas: DEFAULT_GAS,
|
||||||
gasEst: '0',
|
gasEst: '0',
|
||||||
gasError: null,
|
gasError: null,
|
||||||
|
gasLimitError: null,
|
||||||
gasPrice: DEFAULT_GASPRICE,
|
gasPrice: DEFAULT_GASPRICE,
|
||||||
gasPriceHistogram: {},
|
gasPriceHistogram: {},
|
||||||
gasPriceError: null,
|
gasPriceError: null,
|
||||||
@ -103,6 +106,7 @@ export default class Transfer extends Component {
|
|||||||
visible
|
visible
|
||||||
scroll
|
scroll
|
||||||
>
|
>
|
||||||
|
{ this.renderWarning() }
|
||||||
{ this.renderPage() }
|
{ this.renderPage() }
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
@ -264,6 +268,20 @@ export default class Transfer extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderWarning () {
|
||||||
|
const { gasLimitError } = this.state;
|
||||||
|
|
||||||
|
if (!gasLimitError) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.warning }>
|
||||||
|
{ gasLimitError }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
isValid () {
|
isValid () {
|
||||||
const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError;
|
const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError;
|
||||||
const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError;
|
const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError;
|
||||||
@ -519,6 +537,7 @@ export default class Transfer extends Component {
|
|||||||
|
|
||||||
return token.contract.instance.transfer
|
return token.contract.instance.transfer
|
||||||
.estimateGas({
|
.estimateGas({
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
from: account.address,
|
from: account.address,
|
||||||
to: token.address
|
to: token.address
|
||||||
}, [
|
}, [
|
||||||
@ -532,6 +551,7 @@ export default class Transfer extends Component {
|
|||||||
const { account } = this.props;
|
const { account } = this.props;
|
||||||
const { data, recipient, value } = this.state;
|
const { data, recipient, value } = this.state;
|
||||||
const options = {
|
const options = {
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
from: account.address,
|
from: account.address,
|
||||||
to: recipient,
|
to: recipient,
|
||||||
value: api.util.toWei(value || 0)
|
value: api.util.toWei(value || 0)
|
||||||
@ -552,19 +572,29 @@ export default class Transfer extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { gasLimit } = this.props;
|
||||||
|
|
||||||
(this.state.isEth
|
(this.state.isEth
|
||||||
? this._estimateGasEth()
|
? this._estimateGasEth()
|
||||||
: this._estimateGasToken()
|
: this._estimateGasToken()
|
||||||
).then((_value) => {
|
).then((gasEst) => {
|
||||||
let gas = _value;
|
let gas = gasEst;
|
||||||
|
let gasLimitError = null;
|
||||||
|
|
||||||
if (gas.gt(DEFAULT_GAS)) {
|
if (gas.gt(DEFAULT_GAS)) {
|
||||||
gas = gas.mul(1.2);
|
gas = gas.mul(1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gas.gte(MAX_GAS_ESTIMATION)) {
|
||||||
|
gasLimitError = ERRORS.gasException;
|
||||||
|
} else if (gas.gt(gasLimit)) {
|
||||||
|
gasLimitError = ERRORS.gasBlockLimit;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
gas: gas.toFixed(0),
|
gas: gas.toFixed(0),
|
||||||
gasEst: _value.toFormat()
|
gasEst: gasEst.toFormat(),
|
||||||
|
gasLimitError
|
||||||
}, this.recalculate);
|
}, this.recalculate);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -649,3 +679,18 @@ export default class Transfer extends Component {
|
|||||||
store.dispatch({ type: 'newError', error });
|
store.dispatch({ type: 'newError', error });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
const { gasLimit } = state.status;
|
||||||
|
|
||||||
|
return { gasLimit };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return bindActionCreators({}, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Transfer);
|
||||||
|
26
js/src/util/constants.js
Normal file
26
js/src/util/constants.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const DEFAULT_GAS = '21000';
|
||||||
|
const DEFAULT_GASPRICE = '20000000000';
|
||||||
|
|
||||||
|
const MAX_GAS_ESTIMATION = '50000000';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
DEFAULT_GAS,
|
||||||
|
DEFAULT_GASPRICE,
|
||||||
|
MAX_GAS_ESTIMATION
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user