diff --git a/Cargo.lock b/Cargo.lock index af4103bd5..ad1cc15a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,7 +1249,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#985a6d9cf9aa4621172fcb8e4bf6955f33d5e2a3" +source = "git+https://github.com/ethcore/js-precompiled.git#957c5a66c33f3b06a7ae804ac5edc59c20e4535b" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ethstore/src/dir/disk.rs b/ethstore/src/dir/disk.rs index 6616ec15d..56b2c1ccb 100644 --- a/ethstore/src/dir/disk.rs +++ b/ethstore/src/dir/disk.rs @@ -20,6 +20,7 @@ use std::collections::HashMap; use time; use ethkey::Address; use {json, SafeAccount, Error}; +use json::UUID; use super::KeyDirectory; const IGNORED_FILES: &'static [&'static str] = &["thumbs.db", "address_book.json"]; @@ -112,7 +113,7 @@ impl KeyDirectory for DiskDirectory { // build file path let filename = account.filename.as_ref().cloned().unwrap_or_else(|| { let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid."); - format!("UTC--{}Z--{:?}", timestamp, account.address) + format!("UTC--{}Z--{}", timestamp, UUID::from(account.id)) }); // update account filename diff --git a/js/package.json b/js/package.json index bf8db47ab..2a2b6d590 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.48", + "version": "0.2.50", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", @@ -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/scripts/release.sh b/js/scripts/release.sh index 5e631cf98..3ff4a577c 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -68,11 +68,13 @@ if [ "$BRANCH" == "master" ]; then fi echo "*** Updating cargo parity-ui-precompiled#$PRECOMPILED_HASH" +git submodule update cargo update -p parity-ui-precompiled # --precise "$PRECOMPILED_HASH" echo "*** Committing updated files" -git add . +git add js +git add Cargo.lock git commit -m "[ci skip] js-precompiled $UTCDATE" git push origin HEAD:refs/heads/$BRANCH 2>$GITLOG 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/api/util/format.js b/js/src/api/util/format.js index 1b68e1138..198e456ee 100644 --- a/js/src/api/util/format.js +++ b/js/src/api/util/format.js @@ -17,3 +17,15 @@ export function bytesToHex (bytes) { return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join(''); } + +export function hex2Ascii (_hex) { + const hex = /^(?:0x)?(.*)$/.exec(_hex.toString())[1]; + + let str = ''; + + for (let i = 0; i < hex.length; i += 2) { + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + + return str; +} diff --git a/js/src/api/util/index.js b/js/src/api/util/index.js index fb0f79076..55cf008c5 100644 --- a/js/src/api/util/index.js +++ b/js/src/api/util/index.js @@ -16,7 +16,7 @@ import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address'; import { decodeCallData, decodeMethodInput, methodToAbi } from './decode'; -import { bytesToHex } from './format'; +import { bytesToHex, hex2Ascii } from './format'; import { fromWei, toWei } from './wei'; import { sha3 } from './sha3'; import { isArray, isFunction, isHex, isInstanceOf, isString } from './types'; @@ -30,6 +30,7 @@ export default { isInstanceOf, isString, bytesToHex, + hex2Ascii, createIdentityImg, decodeCallData, decodeMethodInput, diff --git a/js/src/contracts/contracts.js b/js/src/contracts/contracts.js index c9d1c2390..a04321c7b 100644 --- a/js/src/contracts/contracts.js +++ b/js/src/contracts/contracts.js @@ -18,6 +18,7 @@ import DappReg from './dappreg'; import Registry from './registry'; import SignatureReg from './signaturereg'; import TokenReg from './tokenreg'; +import GithubHint from './githubhint'; let instance = null; @@ -30,6 +31,7 @@ export default class Contracts { this._dappreg = new DappReg(api, this._registry); this._signaturereg = new SignatureReg(api, this._registry); this._tokenreg = new TokenReg(api, this._registry); + this._githubhint = new GithubHint(api, this._registry); } get registry () { @@ -48,6 +50,10 @@ export default class Contracts { return this._tokenreg; } + get githubHint () { + return this._githubhint; + } + static create (api) { return new Contracts(api); } diff --git a/js/src/contracts/githubhint.js b/js/src/contracts/githubhint.js new file mode 100644 index 000000000..47d7eca6e --- /dev/null +++ b/js/src/contracts/githubhint.js @@ -0,0 +1,32 @@ +// 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 . + +export default class GithubHint { + constructor (api, registry) { + this._api = api; + this._registry = registry; + + this.getInstance(); + } + + getContract () { + return this._registry.getContract('githubhint'); + } + + getInstance () { + return this.getContract().instance; + } +} diff --git a/js/src/contracts/registry.js b/js/src/contracts/registry.js index 9853c0df9..d52b20718 100644 --- a/js/src/contracts/registry.js +++ b/js/src/contracts/registry.js @@ -42,7 +42,7 @@ export default class Registry { }); } - getContractInstance (_name) { + getContract (_name) { const name = _name.toLowerCase(); return new Promise((resolve, reject) => { @@ -54,13 +54,19 @@ export default class Registry { this .lookupAddress(name) .then((address) => { - this._contracts[name] = this._api.newContract(abis[name], address).instance; + this._contracts[name] = this._api.newContract(abis[name], address); resolve(this._contracts[name]); }) .catch(reject); }); } + getContractInstance (_name) { + return this + .getContract(_name) + .then((contract) => contract.instance); + } + lookupAddress (_name) { const name = _name.toLowerCase(); const sha3 = this._api.util.sha3(name); diff --git a/js/src/contracts/tokenreg.js b/js/src/contracts/tokenreg.js index 78bd5e4d9..5e317880b 100644 --- a/js/src/contracts/tokenreg.js +++ b/js/src/contracts/tokenreg.js @@ -22,8 +22,12 @@ export default class TokenReg { this.getInstance(); } + getContract () { + return this._registry.getContract('tokenreg'); + } + getInstance () { - return this._registry.getContractInstance('tokenreg'); + return this.getContract().instance; } tokenCount () { diff --git a/js/src/dapps/tokenreg/Accounts/actions.js b/js/src/dapps/tokenreg/Accounts/actions.js index a310baf7d..5f92280be 100644 --- a/js/src/dapps/tokenreg/Accounts/actions.js +++ b/js/src/dapps/tokenreg/Accounts/actions.js @@ -46,8 +46,6 @@ export const loadAccounts = () => (dispatch) => { address })); - console.log('accounts', accountsList); - dispatch(setAccounts(accountsList)); dispatch(setAccountsInfo(accountsInfo)); dispatch(setSelectedAccount(accountsList[0].address)); diff --git a/js/src/dapps/tokenreg/Actions/Query/query.js b/js/src/dapps/tokenreg/Actions/Query/query.js index 16d13f2f0..5a3c7d5f6 100644 --- a/js/src/dapps/tokenreg/Actions/Query/query.js +++ b/js/src/dapps/tokenreg/Actions/Query/query.js @@ -42,12 +42,9 @@ export default class QueryAction extends Component { onClose: PropTypes.func.isRequired, handleQueryToken: PropTypes.func.isRequired, - handleQueryMetaLookup: PropTypes.func.isRequired, data: PropTypes.object, - notFound: PropTypes.bool, - metaLoading: PropTypes.bool, - metaData: PropTypes.object + notFound: PropTypes.bool } state = initState; @@ -131,11 +128,8 @@ export default class QueryAction extends Component { return ( + tla={ data.tla } + /> ); } diff --git a/js/src/dapps/tokenreg/Actions/actions.js b/js/src/dapps/tokenreg/Actions/actions.js index 1c6703f77..df5b41e6b 100644 --- a/js/src/dapps/tokenreg/Actions/actions.js +++ b/js/src/dapps/tokenreg/Actions/actions.js @@ -16,8 +16,6 @@ import { getTokenTotalSupply } from '../utils'; -const { sha3, bytesToHex } = window.parity.api.util; - export const SET_REGISTER_SENDING = 'SET_REGISTER_SENDING'; export const setRegisterSending = (isSending) => ({ type: SET_REGISTER_SENDING, @@ -41,8 +39,6 @@ export const registerCompleted = () => ({ }); export const registerToken = (tokenData) => (dispatch, getState) => { - console.log('registering token', tokenData); - const state = getState(); const contractInstance = state.status.contract.instance; const fee = state.status.contract.fee; @@ -83,8 +79,6 @@ export const registerToken = (tokenData) => (dispatch, getState) => { }) .then((gasEstimate) => { options.gas = gasEstimate.mul(1.2).toFixed(0); - console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`); - return contractInstance.register.postTransaction(options, values); }) .then((result) => { @@ -183,34 +177,3 @@ export const queryToken = (key, query) => (dispatch, getState) => { dispatch(setQueryLoading(false)); }); }; - -export const queryTokenMeta = (id, query) => (dispatch, getState) => { - console.log('loading token meta', query); - - const state = getState(); - const contractInstance = state.status.contract.instance; - - const key = sha3(query); - - const startDate = Date.now(); - dispatch(setQueryMetaLoading(true)); - - contractInstance - .meta - .call({}, [ id, key ]) - .then((value) => { - const meta = { - key, query, - value: value.find(v => v !== 0) ? bytesToHex(value) : null - }; - - dispatch(setQueryMeta(meta)); - - setTimeout(() => { - dispatch(setQueryMetaLoading(false)); - }, 500 - (Date.now() - startDate)); - }) - .catch((e) => { - console.error('load meta query error', e); - }); -}; diff --git a/js/src/dapps/tokenreg/Actions/component.js b/js/src/dapps/tokenreg/Actions/component.js index 3e7ef0d64..43acc27ab 100644 --- a/js/src/dapps/tokenreg/Actions/component.js +++ b/js/src/dapps/tokenreg/Actions/component.js @@ -37,7 +37,6 @@ export default class Actions extends Component { handleQueryToken: PropTypes.func.isRequired, handleQueryClose: PropTypes.func.isRequired, - handleQueryMetaLookup: PropTypes.func.isRequired, query: PropTypes.object.isRequired }; @@ -82,7 +81,6 @@ export default class Actions extends Component { show={ this.state.show[ QUERY_ACTION ] } onClose={ this.onQueryClose } handleQueryToken={ this.props.handleQueryToken } - handleQueryMetaLookup={ this.props.handleQueryMetaLookup } { ...this.props.query } /> ); diff --git a/js/src/dapps/tokenreg/Actions/container.js b/js/src/dapps/tokenreg/Actions/container.js index 2c3773122..1d3d8fe31 100644 --- a/js/src/dapps/tokenreg/Actions/container.js +++ b/js/src/dapps/tokenreg/Actions/container.js @@ -19,7 +19,7 @@ import { connect } from 'react-redux'; import Actions from './component'; -import { registerToken, registerReset, queryToken, queryReset, queryTokenMeta } from './actions'; +import { registerToken, registerReset, queryToken, queryReset } from './actions'; class TokensContainer extends Component { @@ -49,9 +49,6 @@ const mapDispatchToProps = (dispatch) => { }, handleQueryClose: () => { dispatch(queryReset()); - }, - handleQueryMetaLookup: (id, query) => { - dispatch(queryTokenMeta(id, query)); } }; }; diff --git a/js/src/dapps/tokenreg/Application/application.css b/js/src/dapps/tokenreg/Application/application.css index 033147ae3..94ca6302e 100644 --- a/js/src/dapps/tokenreg/Application/application.css +++ b/js/src/dapps/tokenreg/Application/application.css @@ -19,6 +19,7 @@ display: flex; flex-direction: column; align-items: center; + padding-bottom: 10em; } .warning { diff --git a/js/src/dapps/tokenreg/Status/actions.js b/js/src/dapps/tokenreg/Status/actions.js index e9e217d6a..b07949a28 100644 --- a/js/src/dapps/tokenreg/Status/actions.js +++ b/js/src/dapps/tokenreg/Status/actions.js @@ -14,11 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { - registry as registryAbi, - tokenreg as tokenregAbi, - githubhint as githubhintAbi -} from '../../../contracts/abi'; +import Contracts from '../../../contracts'; import { loadToken, setTokenPending, deleteToken, setTokenData } from '../Tokens/actions'; @@ -34,43 +30,31 @@ export const FIND_CONTRACT = 'FIND_CONTRACT'; export const loadContract = () => (dispatch) => { dispatch(setLoading(true)); - api.parity - .registryAddress() - .then((registryAddress) => { - console.log(`registry found at ${registryAddress}`); - const registry = api.newContract(registryAbi, registryAddress).instance; - - return Promise.all([ - registry.getAddress.call({}, [api.util.sha3('tokenreg'), 'A']), - registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A']) - ]); - }) - .then(([ tokenregAddress, githubhintAddress ]) => { - console.log(`tokenreg was found at ${tokenregAddress}`); - - const tokenregContract = api - .newContract(tokenregAbi, tokenregAddress); - - const githubhintContract = api - .newContract(githubhintAbi, githubhintAddress); + const { tokenReg, githubHint } = new Contracts(api); + return Promise + .all([ + tokenReg.getContract(), + githubHint.getContract() + ]) + .then(([ tokenRegContract, githubHintContract ]) => { dispatch(setContractDetails({ - address: tokenregAddress, - instance: tokenregContract.instance, - raw: tokenregContract + address: tokenRegContract.address, + instance: tokenRegContract.instance, + raw: tokenRegContract })); dispatch(setGithubhintDetails({ - address: githubhintAddress, - instance: githubhintContract.instance, - raw: githubhintContract + address: githubHintContract.address, + instance: githubHintContract.instance, + raw: githubHintContract })); dispatch(loadContractDetails()); dispatch(subscribeEvents()); }) .catch((error) => { - console.error('loadContract error', error); + throw error; }); }; @@ -78,7 +62,7 @@ export const LOAD_CONTRACT_DETAILS = 'LOAD_CONTRACT_DETAILS'; export const loadContractDetails = () => (dispatch, getState) => { const state = getState(); - const instance = state.status.contract.instance; + const { instance } = state.status.contract; Promise .all([ @@ -87,8 +71,6 @@ export const loadContractDetails = () => (dispatch, getState) => { instance.fee.call() ]) .then(([accounts, owner, fee]) => { - console.log(`owner as ${owner}, fee set at ${fee.toFormat()}`); - const isOwner = accounts.filter(a => a === owner).length > 0; dispatch(setContractDetails({ @@ -119,14 +101,14 @@ export const setGithubhintDetails = (details) => ({ export const subscribeEvents = () => (dispatch, getState) => { const state = getState(); - const contract = state.status.contract.raw; + const { raw } = state.status.contract; const previousSubscriptionId = state.status.subscriptionId; if (previousSubscriptionId) { - contract.unsubscribe(previousSubscriptionId); + raw.unsubscribe(previousSubscriptionId); } - contract + raw .subscribe(null, { fromBlock: 'latest', toBlock: 'pending', @@ -187,7 +169,7 @@ export const subscribeEvents = () => (dispatch, getState) => { )); } - console.log('new log event', log); + console.warn('unknown log event', log); }); }) .then((subscriptionId) => { diff --git a/js/src/dapps/tokenreg/Status/reducer.js b/js/src/dapps/tokenreg/Status/reducer.js index 357cb2244..aee16fbe7 100644 --- a/js/src/dapps/tokenreg/Status/reducer.js +++ b/js/src/dapps/tokenreg/Status/reducer.js @@ -27,15 +27,13 @@ const initialState = { contract: { address: null, instance: null, - raw: null, owner: null, isOwner: false, fee: null }, githubhint: { address: null, - instance: null, - raw: null + instance: null } }; diff --git a/js/src/dapps/tokenreg/Tokens/Token/index.js b/js/src/dapps/tokenreg/Tokens/Token/index.js index 4b822b4bd..30ad8896f 100644 --- a/js/src/dapps/tokenreg/Tokens/Token/index.js +++ b/js/src/dapps/tokenreg/Tokens/Token/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -export default from './token'; +export default from './tokenContainer'; diff --git a/js/src/dapps/tokenreg/Tokens/Token/token.js b/js/src/dapps/tokenreg/Tokens/Token/token.js index 660b21b97..be14cec84 100644 --- a/js/src/dapps/tokenreg/Tokens/Token/token.js +++ b/js/src/dapps/tokenreg/Tokens/Token/token.js @@ -57,15 +57,28 @@ export default class Token extends Component { isLoading: PropTypes.bool, isPending: PropTypes.bool, isTokenOwner: PropTypes.bool.isRequired, - isContractOwner: PropTypes.bool.isRequired, + isContractOwner: PropTypes.bool, fullWidth: PropTypes.bool }; - state = { - metaKeyIndex: 0 + static defaultProps = { + isContractOwner: false }; + state = { + metaKeyIndex: 0, + showMeta: false + }; + + shouldComponentUpdate (nextProps) { + if (nextProps.isLoading && this.props.isLoading) { + return false; + } + + return true; + } + render () { const { isLoading, fullWidth } = this.props; @@ -237,7 +250,12 @@ export default class Token extends Component { } renderMeta (meta) { - const isMetaLoading = this.props.isMetaLoading; + const { isMetaLoading } = this.props; + const { showMeta } = this.state; + + if (!showMeta) { + return null; + } if (isMetaLoading) { return (
@@ -331,6 +349,7 @@ export default class Token extends Component { const key = metaDataKeys[keyIndex].value; const index = this.props.index; + this.setState({ showMeta: true }); this.props.handleMetaLookup(index, key); } diff --git a/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js b/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js new file mode 100644 index 000000000..7351da524 --- /dev/null +++ b/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js @@ -0,0 +1,73 @@ +// 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 React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; + +import Token from './token'; + +import { queryTokenMeta, unregisterToken, addTokenMeta } from '../actions'; + +class TokenContainer extends Component { + static propTypes = { + handleMetaLookup: PropTypes.func.isRequired, + handleUnregister: PropTypes.func.isRequired, + handleAddMeta: PropTypes.func.isRequired, + + tla: PropTypes.string.isRequired + }; + + render () { + return ( + + ); + } +} + +const mapStateToProps = (_, initProps) => { + const { tla } = initProps; + + return (state) => { + const { isOwner } = state.status.contract; + const { tokens } = state.tokens; + const token = tokens.find((t) => t.tla === tla); + + return { ...token, isContractOwner: isOwner }; + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + handleMetaLookup: (index, query) => { + dispatch(queryTokenMeta(index, query)); + }, + + handleUnregister: (index) => { + dispatch(unregisterToken(index)); + }, + + handleAddMeta: (index, key, value) => { + dispatch(addTokenMeta(index, key, value)); + } + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(TokenContainer); diff --git a/js/src/dapps/tokenreg/Tokens/actions.js b/js/src/dapps/tokenreg/Tokens/actions.js index bd4413163..d1e13c1dc 100644 --- a/js/src/dapps/tokenreg/Tokens/actions.js +++ b/js/src/dapps/tokenreg/Tokens/actions.js @@ -67,8 +67,6 @@ export const deleteToken = (index) => ({ }); export const loadTokens = () => (dispatch, getState) => { - console.log('loading tokens...'); - const state = getState(); const contractInstance = state.status.contract.instance; @@ -79,7 +77,6 @@ export const loadTokens = () => (dispatch, getState) => { .call() .then((count) => { const tokenCount = parseInt(count); - console.log(`token count: ${tokenCount}`); dispatch(setTokenCount(tokenCount)); for (let i = 0; i < tokenCount; i++) { @@ -94,8 +91,6 @@ export const loadTokens = () => (dispatch, getState) => { }; export const loadToken = (index) => (dispatch, getState) => { - console.log('loading token', index); - const state = getState(); const contractInstance = state.status.contract.instance; @@ -144,7 +139,7 @@ export const loadToken = (index) => (dispatch, getState) => { } data.totalSupply = data.totalSupply.toNumber(); - console.log(`token loaded: #${index}`, data); + dispatch(setTokenData(index, data)); dispatch(setTokenLoading(index, false)); }) @@ -159,8 +154,6 @@ export const loadToken = (index) => (dispatch, getState) => { }; export const queryTokenMeta = (index, query) => (dispatch, getState) => { - console.log('loading token meta', index, query); - const state = getState(); const contractInstance = state.status.contract.instance; @@ -176,7 +169,6 @@ export const queryTokenMeta = (index, query) => (dispatch, getState) => { value: value.find(v => v !== 0) ? bytesToHex(value) : null }; - console.log(`token meta loaded: #${index}`, value); dispatch(setTokenMeta(index, meta)); setTimeout(() => { @@ -189,8 +181,6 @@ export const queryTokenMeta = (index, query) => (dispatch, getState) => { }; export const addTokenMeta = (index, key, value) => (dispatch, getState) => { - console.log('add token meta', index, key, value); - const state = getState(); const contractInstance = state.status.contract.instance; const token = state.tokens.tokens.find(t => t.index === index); @@ -203,8 +193,6 @@ export const addTokenMeta = (index, key, value) => (dispatch, getState) => { .estimateGas(options, values) .then((gasEstimate) => { options.gas = gasEstimate.mul(1.2).toFixed(0); - console.log(`addTokenMeta: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`); - return contractInstance.setMeta.postTransaction(options, values); }) .catch((e) => { @@ -213,8 +201,6 @@ export const addTokenMeta = (index, key, value) => (dispatch, getState) => { }; export const addGithubhintURL = (from, key, url) => (dispatch, getState) => { - console.log('add githubhint url', key, url); - const state = getState(); const contractInstance = state.status.githubhint.instance; @@ -227,8 +213,6 @@ export const addGithubhintURL = (from, key, url) => (dispatch, getState) => { .estimateGas(options, values) .then((gasEstimate) => { options.gas = gasEstimate.mul(1.2).toFixed(0); - console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`); - return contractInstance.hintURL.postTransaction(options, values); }) .catch((e) => { @@ -237,8 +221,6 @@ export const addGithubhintURL = (from, key, url) => (dispatch, getState) => { }; export const unregisterToken = (index) => (dispatch, getState) => { - console.log('unregistering token', index); - const { contract } = getState().status; const { instance, owner } = contract; @@ -252,8 +234,6 @@ export const unregisterToken = (index) => (dispatch, getState) => { .estimateGas(options, values) .then((gasEstimate) => { options.gas = gasEstimate.mul(1.2).toFixed(0); - console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`); - return instance.unregister.postTransaction(options, values); }) .catch((e) => { diff --git a/js/src/dapps/tokenreg/Tokens/container.js b/js/src/dapps/tokenreg/Tokens/container.js index b6171c2cc..33b2de659 100644 --- a/js/src/dapps/tokenreg/Tokens/container.js +++ b/js/src/dapps/tokenreg/Tokens/container.js @@ -19,16 +19,13 @@ import { connect } from 'react-redux'; import Tokens from './tokens'; -import { loadTokens, queryTokenMeta, unregisterToken, addTokenMeta } from './actions'; +import { loadTokens } from './actions'; class TokensContainer extends Component { static propTypes = { - isOwner: PropTypes.bool, isLoading: PropTypes.bool, tokens: PropTypes.array, - tokenCount: PropTypes.number, - onLoadTokens: PropTypes.func, - accounts: PropTypes.array + onLoadTokens: PropTypes.func }; componentDidMount () { @@ -36,7 +33,6 @@ class TokensContainer extends Component { } render () { - console.log(this.props); return ( { - const { list } = state.accounts; - const { isLoading, tokens, tokenCount } = state.tokens; + const { isLoading, tokens } = state.tokens; - const { isOwner } = state.status.contract; + const filteredTokens = tokens + .filter((token) => token && token.tla) + .map((token) => ({ tla: token.tla, owner: token.owner })); - return { isLoading, tokens, tokenCount, isOwner, accounts: list }; + return { isLoading, tokens: filteredTokens }; }; const mapDispatchToProps = (dispatch) => { return { onLoadTokens: () => { dispatch(loadTokens()); - }, - - handleMetaLookup: (index, query) => { - dispatch(queryTokenMeta(index, query)); - }, - - handleUnregister: (index) => { - dispatch(unregisterToken(index)); - }, - - handleAddMeta: (index, key, value) => { - dispatch(addTokenMeta(index, key, value)); } }; }; diff --git a/js/src/dapps/tokenreg/Tokens/tokens.js b/js/src/dapps/tokenreg/Tokens/tokens.js index 43766a8a8..48bc88a74 100644 --- a/js/src/dapps/tokenreg/Tokens/tokens.js +++ b/js/src/dapps/tokenreg/Tokens/tokens.js @@ -23,13 +23,8 @@ import styles from './tokens.css'; export default class Tokens extends Component { static propTypes = { - handleAddMeta: PropTypes.func.isRequired, - handleUnregister: PropTypes.func.isRequired, - handleMetaLookup: PropTypes.func.isRequired, - isOwner: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired, - tokens: PropTypes.array, - accounts: PropTypes.array + tokens: PropTypes.array }; render () { @@ -45,24 +40,12 @@ export default class Tokens extends Component { } renderTokens (tokens) { - const { accounts, isOwner } = this.props; - - return tokens.map((token, index) => { - if (!token || !token.tla) { - return null; - } - - const isTokenOwner = !!accounts.find((account) => account.address === token.owner); - + return tokens.map((token) => { return ( + key={ token.tla } + tla={ token.tla } + /> ); }); } diff --git a/js/src/index.js b/js/src/index.js index c0f4f94ad..fda785842 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -25,6 +25,7 @@ import ReactDOM from 'react-dom'; import injectTapEventPlugin from 'react-tap-event-plugin'; import { createHashHistory } from 'history'; import { Redirect, Router, Route, useRouterHistory } from 'react-router'; +import qs from 'querystring'; import SecureApi from './secureApi'; import ContractInstances from './contracts'; @@ -45,6 +46,7 @@ import './index.html'; injectTapEventPlugin(); +const AUTH_HASH = '#/auth?'; const parityUrl = process.env.PARITY_URL || ( process.env.NODE_ENV === 'production' @@ -52,7 +54,12 @@ const parityUrl = process.env.PARITY_URL || : '127.0.0.1:8180' ); -const api = new SecureApi(`ws://${parityUrl}`); +let token = null; +if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { + token = qs.parse(window.location.hash.substr(AUTH_HASH.length)).token; +} + +const api = new SecureApi(`ws://${parityUrl}`, token); ContractInstances.create(api); const store = initStore(api); @@ -67,6 +74,7 @@ ReactDOM.render( + 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 = (