From 7144da5d7ee5bae8a7be467b8ad2b2b71dcb9913 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 16 Nov 2016 18:55:53 +0100 Subject: [PATCH] Handle Signer Rejection // Real Custom Errors #3153 --- js/package.json | 1 + js/src/api/transport/error.js | 53 +++++++++++++++++++ js/src/api/transport/http/http.js | 4 +- js/src/api/transport/index.js | 1 + js/src/api/transport/ws/ws.js | 5 +- .../modals/DeployContract/deployContract.js | 34 +++++++++--- .../modals/ExecuteContract/executeContract.js | 29 ++++++++-- js/src/modals/Transfer/transfer.js | 41 +++++++++++--- 8 files changed, 151 insertions(+), 17 deletions(-) create mode 100644 js/src/api/transport/error.js diff --git a/js/package.json b/js/package.json index 59b75c591..c90742c05 100644 --- a/js/package.json +++ b/js/package.json @@ -122,6 +122,7 @@ "brace": "^0.9.0", "bytes": "^2.4.0", "chart.js": "^2.3.0", + "es6-error": "^4.0.0", "es6-promise": "^3.2.1", "ethereumjs-tx": "^1.1.2", "file-saver": "^1.3.3", diff --git a/js/src/api/transport/error.js b/js/src/api/transport/error.js new file mode 100644 index 000000000..341839f69 --- /dev/null +++ b/js/src/api/transport/error.js @@ -0,0 +1,53 @@ +// 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 ExtendableError from 'es6-error'; + +export const ERROR_CODES = { + UNSUPPORTED_REQUEST: -32000, + NO_WORK: -32001, + NO_AUTHOR: -32002, + NO_NEW_WORK: -32003, + NOT_ENOUGH_DATA: -32006, + UNKNOWN_ERROR: -32009, + TRANSACTION_ERROR: -32010, + EXECUTION_ERROR: -32015, + ACCOUNT_LOCKED: -32020, + PASSWORD_INVALID: -32021, + ACCOUNT_ERROR: -32023, + SIGNER_DISABLED: -32030, + DAPPS_DISABLED: -32031, + NETWORK_DISABLED: -32035, + REQUEST_REJECTED: -32040, + REQUEST_REJECTED_LIMIT: -32041, + REQUEST_NOT_FOUND: -32042, + COMPILATION_ERROR: -32050, + ENCRYPTION_ERROR: -32055, + FETCH_ERROR: -32060 +}; + +export default class TransportError extends ExtendableError { + constructor (method, code, message) { + const m = `${method}: ${code}: ${message}`; + super(m); + + this.code = code; + this.type = Object.keys(ERROR_CODES).find((k) => ERROR_CODES[k] === code) || ''; + + this.method = method; + this.text = message; + } +} diff --git a/js/src/api/transport/http/http.js b/js/src/api/transport/http/http.js index 8ea59f0fb..591b9a627 100644 --- a/js/src/api/transport/http/http.js +++ b/js/src/api/transport/http/http.js @@ -16,6 +16,7 @@ import { Logging } from '../../subscriptions'; import JsonRpcBase from '../jsonRpcBase'; +import TransportError from '../error'; /* global fetch */ export default class Http extends JsonRpcBase { @@ -73,7 +74,8 @@ export default class Http extends JsonRpcBase { this.error(JSON.stringify(response)); console.error(`${method}(${JSON.stringify(params)}): ${response.error.code}: ${response.error.message}`); - throw new Error(`${method}: ${response.error.code}: ${response.error.message}`); + const error = new TransportError(method, response.error.code, response.error.message); + throw error; } this.log(JSON.stringify(response)); diff --git a/js/src/api/transport/index.js b/js/src/api/transport/index.js index 8f67fba4d..84fbac826 100644 --- a/js/src/api/transport/index.js +++ b/js/src/api/transport/index.js @@ -16,3 +16,4 @@ export Http from './http'; export Ws from './ws'; +export TransportError from './error.js'; diff --git a/js/src/api/transport/ws/ws.js b/js/src/api/transport/ws/ws.js index d608426b0..1cb1fb1c4 100644 --- a/js/src/api/transport/ws/ws.js +++ b/js/src/api/transport/ws/ws.js @@ -18,6 +18,7 @@ import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase import { Logging } from '../../subscriptions'; import JsonRpcBase from '../jsonRpcBase'; +import TransportError from '../error'; /* global WebSocket */ export default class Ws extends JsonRpcBase { @@ -109,7 +110,9 @@ export default class Ws extends JsonRpcBase { console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`); - reject(new Error(`${method}: ${result.error.code}: ${result.error.message}`)); + const error = new TransportError(method, result.error.code, result.error.message); + reject(error); + delete this._messages[result.id]; return; } diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index a99b49412..996948092 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -26,6 +26,8 @@ import ErrorStep from './ErrorStep'; import styles from './deployContract.css'; +import { ERROR_CODES } from '../../api/transport/error'; + const steps = ['contract details', 'deployment', 'completed']; export default class DeployContract extends Component { @@ -63,7 +65,8 @@ export default class DeployContract extends Component { params: [], paramsError: [], step: 0, - deployError: null + deployError: null, + rejected: false } componentWillMount () { @@ -92,15 +95,20 @@ export default class DeployContract extends Component { } render () { - const { step, deployError } = this.state; + const { step, deployError, rejected } = this.state; + + const realSteps = deployError || rejected ? null : steps; + const title = realSteps + ? null + : (deployError ? 'deployment failed' : 'rejected'); return ( { this.renderStep() } @@ -158,7 +166,7 @@ export default class DeployContract extends Component { renderStep () { const { accounts, readOnly } = this.props; - const { address, deployError, step, deployState, txhash } = this.state; + const { address, deployError, step, deployState, txhash, rejected } = this.state; if (deployError) { return ( @@ -166,6 +174,15 @@ export default class DeployContract extends Component { ); } + if (rejected) { + return ( + + ); + } + switch (step) { case 0: return ( @@ -273,6 +290,11 @@ export default class DeployContract extends Component { }); }) .catch((error) => { + if (error.code === ERROR_CODES.REQUEST_REJECTED) { + this.setState({ rejected: true }); + return false; + } + console.error('error deploying contract', error); this.setState({ deployError: error }); store.dispatch({ type: 'newError', error }); diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index b45cf6875..a57c18a1d 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -23,6 +23,8 @@ import { validateAddress, validateUint } from '../../util/validation'; import DetailsStep from './DetailsStep'; +import { ERROR_CODES } from '../../api/transport/error'; + export default class ExecuteContract extends Component { static contextTypes = { api: PropTypes.object.isRequired, @@ -49,7 +51,8 @@ export default class ExecuteContract extends Component { step: 0, sending: false, busyState: null, - txhash: null + txhash: null, + rejected: false } componentDidMount () { @@ -80,6 +83,7 @@ export default class ExecuteContract extends Component { const { onClose, fromAddress } = this.props; const { sending, step, fromAddressError, valuesError } = this.state; const hasError = fromAddressError || valuesError.find((error) => error); + const cancelBtn = (