diff --git a/electron/operations/runParity.js b/electron/operations/runParity.js index ff6796c426eb495b07c734464d210b4d24c1488e..d0f709b87569a7a849ee7dad4cc0122571d4e16d 100644 --- a/electron/operations/runParity.js +++ b/electron/operations/runParity.js @@ -24,7 +24,7 @@ let parity = null; // Will hold the running parity instance // parity-ui tries to launch another one. const catchableErrors = [ 'is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.', - 'Error(Msg("IO error: While lock file:' + 'IO error: While lock file:' ]; module.exports = { diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 88ca5039eaa0e553eb638006cf09065d31752314..0000000000000000000000000000000000000000 --- a/package-lock.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "light", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "case-sensitive-paths-webpack-plugin": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.1.2.tgz", - "integrity": "sha512-oEZgAFfEvKtjSRCu6VgYkuGxwrWXMnQzyBmlLPP7r6PWQVtHxP5Z5N6XsuJvtoVax78am/r7lr46bwo3IVEBOg==", - "dev": true - } - } -} diff --git a/package.json b/package.json index f96d9dd7569fe06560be4d934f01fff8e6898e78..4493707d16906c788c5a1d7cada2d338ea84009e 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,7 @@ "API", "Light", "Light Client", - "Parity", - "Promise" + "Parity" ], "homepage": "./", "parity": { @@ -42,7 +41,7 @@ }, "dependencies": { "@parity/api": "^2.1.22", - "@parity/light.js": "https://github.com/parity-js/light.js#c531c68b5268a1bf7aba1f43424f440b2bde7828", + "@parity/light.js": "https://github.com/parity-js/light.js#0c57c29de42122b0f07ff82dfaf38b48863d8d2e", "axios": "^0.18.0", "commander": "^2.15.1", "electron": "^2.0.0", @@ -59,6 +58,7 @@ "rxjs": "^6.1.0" }, "devDependencies": { + "@parity/shared": "^2.2.28", "babel-eslint": "^8.2.3", "cross-env": "^5.1.4", "node-sass": "^4.9.0", diff --git a/scripts/travis.sh b/scripts/travis.sh index 89ee713a828584326c3d463d412655386f22e67c..10408190c548d99e643511e1a0c5f6a0945cb826 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh @@ -1,7 +1,7 @@ #!/bin/bash # Copyright 2015-2018 Parity Technologies (UK) Ltd. # This file is part of Parity. - +# # SPDX-License-Identifier: MIT set -e diff --git a/scripts/updateTokens.js b/scripts/updateTokens.js new file mode 100644 index 0000000000000000000000000000000000000000..d9784d848a7683d63562e164029791c21f328262 --- /dev/null +++ b/scripts/updateTokens.js @@ -0,0 +1,81 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +const Api = require('@parity/api'); +const { bytesToHex } = require('@parity/api/lib/util'); +const Contracts = require('@parity/shared/lib/contracts').default; +const fs = require('fs'); + +// A node serving a HTTP server needs to be running at the following port. The +// chain this script is fetching the tokens is the chain which this node +// connects to. +const api = new Api(new Api.Provider.Http('http://127.0.0.1:8545')); + +const run = async () => { + const { tokenReg, githubHint } = new Contracts(api); + + const chainName = await api.parity.netChain(); + console.log(`Fetching all tokens on ${chainName}, please wait...`); + + // Create file to write tokens to + const filePath = `src/assets/tokens/${chainName}.json`; + const wstream = fs.createWriteStream(filePath); + + // The JSON file will be an array, so we start by opening a bracket + wstream.write('[\n'); + + const tokenRegContract = await tokenReg.getContract(); + const githubHintContract = await githubHint.getContract(); + + // Get tokenCount to loop through all tokens + const tokenCount = +await tokenRegContract.instance.tokenCount.call(); + for (let i = 0; i < tokenCount; i++) { + // Get token information + const token = await tokenRegContract.instance.token.call({}, [i]); + + // Some tokens are empty (unregistered), we skip them + // token[0] is the token address + if (+token[0] === 0) { + continue; + } + + // Get image hash of this token (stored inside the metadata) + const imageHash = bytesToHex( + await tokenRegContract.instance.meta.call({}, [i, 'IMG']) + ); + // Variable result will contain the line to be added to our final JSON file + const result = { + address: token[0], + decimals: token[2].e, // token[2] gives a BigNumber(100000000), the number of decimals is the exponent + name: token[3], + symbol: token[1] + }; + + // If there is an imageHash, then we fetch on GithubHint the url of that + // image + if (+imageHash !== 0) { + const [logo] = await githubHintContract.instance.entries.call({}, [ + imageHash + ]); + result.logo = logo; + } + + // Add this line to the buffer + wstream.write( + `${JSON.stringify(result)}${i === tokenCount - 1 ? '' : ','}\n` + ); + } + + // Close the opening bracket, and then exit + wstream.write(']', () => { + wstream.close(); + + // Finished, we can exit + console.log(`Wrote ${tokenCount} tokens to ${filePath}.`); + process.exit(0); + }); +}; + +run(); diff --git a/src/Accounts/Accounts.js b/src/Accounts/Accounts.js index 265b2ec1bcd98d128045a26de6a1b8c031138c9f..9e215bf1e545c89cb09fe0f14d265266b4a2d9c5 100644 --- a/src/Accounts/Accounts.js +++ b/src/Accounts/Accounts.js @@ -3,42 +3,47 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; -import { allAccountsInfo$, setDefaultAccount$ } from '@parity/light.js'; +import React, { PureComponent } from 'react'; +import { accountsInfo$ } from '@parity/light.js'; import Blockies from 'react-blockies'; -import { Link } from 'react-router-dom'; +import { inject, observer } from 'mobx-react'; +import { Link, Route, Switch } from 'react-router-dom'; +import CreateAccount from './CreateAccount/CreateAccount'; import light from '../hoc'; +@inject('parityStore') +@observer @light({ - allAccountsInfo: allAccountsInfo$ + accountsInfo: accountsInfo$ }) -class Accounts extends Component { - componentWillUnmount () { - if (this.subscription) { - this.subscription.unsubscribe(); - } - } - - handleClick = ({ - currentTarget: { - dataset: { address } - } - }) => { +class Accounts extends PureComponent { + handleClick = ({ currentTarget: { dataset: { address } } }) => { + const { parityStore: { api } } = this.props; // Set default account to the clicked one, and go to Tokens on complete - this.subscription = setDefaultAccount$(address).subscribe(null, null, () => - this.props.history.push('/tokens') - ); + this.subscription = api.parity + .setNewDappsDefaultAddress(address) + .then(() => this.props.history.push('/tokens')); }; render () { - const { allAccountsInfo } = this.props; + return ( + + + + + ); + } + + // TODO We can put this into a separate Component + renderAccounts = () => { + const { accountsInfo } = this.props; return (
); - } + }; } export default Accounts; diff --git a/src/Accounts/CreateAccount/CreateAccount.js b/src/Accounts/CreateAccount/CreateAccount.js index 22707c18063ce59193b44c39308375ac0a67c783..d77196e49d2957a56a5b9c29a95d8025b07db314 100644 --- a/src/Accounts/CreateAccount/CreateAccount.js +++ b/src/Accounts/CreateAccount/CreateAccount.js @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Route, Link } from 'react-router-dom'; import Step1 from './CreateAccountStep1'; @@ -12,12 +12,12 @@ import Step3 from './CreateAccountStep3'; import Step4 from './CreateAccountStep4'; import Step5 from './CreateAccountStep5'; -class CreateAccount extends Component { +class CreateAccount extends PureComponent { render () { return (
diff --git a/src/App/ProtectedRoute/ProtectedRoute.js b/src/App/ProtectedRoute/ProtectedRoute.js index f6d84a9b823da624b54f544e987a64df94fc0965..83584655bfd93b09d0498eb9011c2a33b6205384 100644 --- a/src/App/ProtectedRoute/ProtectedRoute.js +++ b/src/App/ProtectedRoute/ProtectedRoute.js @@ -3,34 +3,35 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { inject, observer } from 'mobx-react'; import { Redirect, Route, withRouter } from 'react-router-dom'; -@withRouter // https://github.com/mobxjs/mobx-react/issues/210 +/** + * Protected routes are routes that cannot be access if parity Api is not + * connected yet. + */ +// https://github.com/mobxjs/mobx-react/issues/210 +@withRouter @inject('parityStore') @observer -class ProtectedRoute extends Component { +class ProtectedRoute extends PureComponent { render () { + const { component, parityStore, ...rest } = this.props; + + return ; + } + + renderComponent = props => { const { component: RoutedComponent, - parityStore: { isApiConnected }, - ...rest + parityStore: { isApiConnected } } = this.props; - return ( - - isApiConnected ? ( - - ) : ( - - ) - } - /> - ); - } + return isApiConnected + ? + : ; + }; } export default ProtectedRoute; diff --git a/src/Health/Health.js b/src/Health/Health.js index f526d543550e0b5ed282ecfd7960c45a4842f786..a4793bd2d984aa4da4cbe32202872f259c4e536a 100644 --- a/src/Health/Health.js +++ b/src/Health/Health.js @@ -3,9 +3,9 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; -class Health extends Component { +class Health extends PureComponent { render () { return (
diff --git a/src/Loading/Loading.js b/src/Loading/Loading.js index ec143333228ee6e06108b4ebe54121cc0b9142e6..fafa064b84a6f883f9ee147b9f44de590abc3d94 100644 --- a/src/Loading/Loading.js +++ b/src/Loading/Loading.js @@ -3,17 +3,15 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { inject, observer } from 'mobx-react'; import { Redirect } from 'react-router-dom'; @inject('parityStore') @observer -class Loading extends Component { +class Loading extends PureComponent { render () { - const { - parityStore: { downloadProgress, isApiConnected } - } = this.props; + const { parityStore: { downloadProgress, isApiConnected } } = this.props; if (isApiConnected) { return ; @@ -38,9 +36,7 @@ class Loading extends Component { } renderStatus = () => { - const { - parityStore: { downloadProgress, isParityRunning } - } = this.props; + const { parityStore: { downloadProgress, isParityRunning } } = this.props; if (isParityRunning) { return 'Connecting to API...'; diff --git a/src/Receive/Receive.js b/src/Receive/Receive.js index 5df6c69406bbe3a74a37544fe0e207d7a78cfd18..107774ba8a6c6d19d231299e2b1bee5967454341 100644 --- a/src/Receive/Receive.js +++ b/src/Receive/Receive.js @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { defaultAccount$ } from '@parity/light.js'; import { Link } from 'react-router-dom'; @@ -12,7 +12,7 @@ import light from '../hoc'; @light({ me: defaultAccount$ }) -class Receive extends Component { +class Receive extends PureComponent { render () { const { me } = this.props; return ( @@ -21,9 +21,7 @@ class Receive extends Component { x - - Accounts - + Accounts
@@ -32,7 +30,9 @@ class Receive extends Component {
- {me} + + {me} +
diff --git a/src/Send/Send.js b/src/Send/Send.js index a2b50507004d03f27ca77e75d62fc0864457ab5e..2a8899a0884502e14168574a22a56bff732a52af 100644 --- a/src/Send/Send.js +++ b/src/Send/Send.js @@ -3,9 +3,9 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; -import { balanceOf$, defaultAccount$ } from '@parity/light.js'; -import { map, switchMap } from 'rxjs/operators'; +import React, { PureComponent } from 'react'; +import { defaultAccount$, myBalance$ } from '@parity/light.js'; +import { map } from 'rxjs/operators'; import { fromWei, toWei } from '@parity/api/lib/util/wei'; import { Link } from 'react-router-dom'; @@ -13,14 +13,10 @@ import ethereumIcon from '../assets/img/tokens/ethereum.png'; import light from '../hoc'; @light({ - balance: () => - defaultAccount$().pipe( - switchMap(balanceOf$), - map(value => +fromWei(value.toString())) - ), + balance: () => myBalance$().pipe(map(value => +fromWei(value.toString()))), me: defaultAccount$ }) -class Send extends Component { +class Send extends PureComponent { state = { amount: 0.01, // In Ether gas: 21000, diff --git a/src/Send/Signer/Signer.js b/src/Send/Signer/Signer.js index b1ffcf1b4ca4087d1d4106c3654899c7b0371099..e20e6b5d83c3e435b956c1e128382ad4610454c5 100644 --- a/src/Send/Signer/Signer.js +++ b/src/Send/Signer/Signer.js @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { fromWei } from '@parity/api/lib/util/wei'; import { inject, observer } from 'mobx-react'; import { post$ } from '@parity/light.js'; @inject('signerStore') @observer -class Signer extends Component { +class Signer extends PureComponent { state = { password: '', status: null @@ -57,22 +57,30 @@ class Signer extends Component { }; render () { - const { - location: { state: tx } - } = this.props; + const { location: { state: tx } } = this.props; const { password, status } = this.state; return (
-

Request number {this.requestId}

-

From: {tx.from}

-

To: {tx.to}

-

Amount: {+fromWei(tx.value)}ETH

-

Gas: {+tx.gas}

- {status && status.requested ? ( -
+

+ Request number {this.requestId} +

+

+ From: {tx.from} +

+

+ To: {tx.to} +

+

+ Amount: {+fromWei(tx.value)}ETH +

+

+ Gas: {+tx.gas} +

+ {status && status.requested + ?
- ) : ( -

Loading...

- )} + :

Loading...

}

-

Status: {JSON.stringify(status)}

+

+ Status: {JSON.stringify(status)} +

); } diff --git a/src/Settings/Settings.js b/src/Settings/Settings.js index 1f3022774e0e1589d63f77f94a975a165cefff75..a92506d595582a3e85b9579d2f3de0df626b3549 100644 --- a/src/Settings/Settings.js +++ b/src/Settings/Settings.js @@ -3,12 +3,12 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Link } from 'react-router-dom'; import Health from '../Health'; -class Settings extends Component { +class Settings extends PureComponent { render () { return (
diff --git a/src/Tokens/NewToken/NewToken.js b/src/Tokens/NewToken/NewToken.js new file mode 100644 index 0000000000000000000000000000000000000000..785c47d2a74517ad5131a5fb3d3942048e838dd6 --- /dev/null +++ b/src/Tokens/NewToken/NewToken.js @@ -0,0 +1,109 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +import React, { PureComponent } from 'react'; +import { chainName$ } from '@parity/light.js'; +import debounce from 'lodash/debounce'; +import { inject, observer } from 'mobx-react'; + +import light from '../../hoc'; +import NewTokenItem from './NewTokenItem'; + +@inject('tokensStore') +@observer +@light({ + chainName: chainName$ +}) +class NewToken extends PureComponent { + state = { + db: null, + dbMap: null, + matches: this.props.tokensStore.tokensArrayWithoutEth, + search: '' + }; + + calculateMatches = debounce(() => { + const { tokensStore: { tokensArrayWithoutEth } } = this.props; + const { db, search } = this.state; + + if (search.length <= 1) { + this.setState({ matches: tokensArrayWithoutEth }); + return; + } + + const matches = db + ? db.filter( + ({ name, symbol }) => + name.toLowerCase().includes(search.toLowerCase()) || + symbol.toLowerCase().includes(search.toLowerCase()) + ) + : []; + this.setState({ matches }); + }, 500); + + componentDidMount () { + if (this.props.chainName) { + this.fetchTokensDb(); + } + } + + componentDidUpdate (prevProps) { + if (this.props.chainName && !prevProps.chainName) { + this.fetchTokensDb(); + } + } + + fetchTokensDb = async () => { + if (this.state.db) { + // Don't fetch again if it's already fetched + return; + } + + // We only fetch this huge json if the user visits this NewToken page. We + // try to avoid it as much as possible. All other tokens info (used in the + // homepage) are stored in localStorage. + let db; + try { + db = await import(`../../assets/tokens/${this.props.chainName}.json`); + } catch (e) { + db = await import(`../../assets/tokens/foundation.json`); + } + + // We create a address=>token mapping here + const dbMap = {}; + db.forEach(token => (dbMap[token.address] = token)); + + // Commit this into the state + this.setState({ db, dbMap }); + }; + + handleSearch = ({ target: { value } }) => { + this.setState({ search: value }); + this.calculateMatches(); + }; + + render () { + const { matches, search } = this.state; + + return ( +
+ +
+
    + {matches.map(token => + + )} +
+
+
+ ); + } +} + +export default NewToken; diff --git a/src/Tokens/NewToken/NewTokenItem/NewTokenItem.js b/src/Tokens/NewToken/NewTokenItem/NewTokenItem.js new file mode 100644 index 0000000000000000000000000000000000000000..42d7c3043364aa70361bda553442760082f0b85e --- /dev/null +++ b/src/Tokens/NewToken/NewTokenItem/NewTokenItem.js @@ -0,0 +1,52 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +import React, { PureComponent } from 'react'; +import { inject, observer } from 'mobx-react'; +import { withRouter } from 'react-router-dom'; + +@inject('tokensStore') +@observer +@withRouter +class NewTokenItem extends PureComponent { + handleAddToken = () => { + const { history, token, tokensStore } = this.props; + tokensStore.addToken(token.address, token); + history.goBack(); + }; + + handleRemoveToken = () => { + const { history, token, tokensStore } = this.props; + tokensStore.removeToken(token.address); + history.goBack(); + }; + + render () { + const { token, tokensStore: { tokens } } = this.props; + + return ( +
  • +
    +
    + {token.symbol} +
    +
    + {token.name} +
    +
    + + {token.symbol} + +
    + {tokens.has(token.address) + ? + : } +
    +
  • + ); + } +} + +export default NewTokenItem; diff --git a/src/Tokens/NewToken/NewTokenItem/index.js b/src/Tokens/NewToken/NewTokenItem/index.js new file mode 100644 index 0000000000000000000000000000000000000000..0e444fba76ab25468a7311393bfe68f683577747 --- /dev/null +++ b/src/Tokens/NewToken/NewTokenItem/index.js @@ -0,0 +1,8 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +import NewTokenItem from './NewTokenItem'; + +export default NewTokenItem; diff --git a/src/Tokens/NewToken/index.js b/src/Tokens/NewToken/index.js new file mode 100644 index 0000000000000000000000000000000000000000..59270b252fc3fb7f5ffa6963e56bd12deece95d8 --- /dev/null +++ b/src/Tokens/NewToken/index.js @@ -0,0 +1,8 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +import NewToken from './NewToken'; + +export default NewToken; diff --git a/src/Tokens/Tokens.js b/src/Tokens/Tokens.js index d0ab03dff8f4a0d09ade922c37ef662f3be9f037..0aa2f5599ff16a15b9ba25ef16650f72658ac325 100644 --- a/src/Tokens/Tokens.js +++ b/src/Tokens/Tokens.js @@ -3,31 +3,19 @@ // // SPDX-License-Identifier: MIT -import React, { Component } from 'react'; -import { inject, observer } from 'mobx-react'; -import { Redirect, Link } from 'react-router-dom'; +import React, { PureComponent } from 'react'; +import { Link, Route, Switch } from 'react-router-dom'; import Health from '../Health'; -import EthBalance from './EthBalance'; -import TokenBalance from './TokenBalance'; +import NewToken from './NewToken'; +import TokensList from './TokensList'; -@inject('parityStore', 'tokensStore') -@observer -class Tokens extends Component { +class Tokens extends PureComponent { render () { - const { - parityStore: { isApiConnected }, - tokensStore: { tokens } - } = this.props; - - if (!isApiConnected) { - return ; - } - return (
    -
    -
    -
      - {Array.from(tokens.keys()).map(key => ( -
    • - {key === 'ETH' ? ( - - ) : ( - - )} -
    • - ))} -
    -
    -
    + + + +