diff --git a/images/bch-logo.png b/images/bch-logo.png deleted file mode 100644 index 9590c12e..00000000 Binary files a/images/bch-logo.png and /dev/null differ diff --git a/images/btc-logo.png b/images/btc-logo.png deleted file mode 100644 index 5b77226c..00000000 Binary files a/images/btc-logo.png and /dev/null differ diff --git a/images/btg-logo.png b/images/btg-logo.png deleted file mode 100644 index 5eda4570..00000000 Binary files a/images/btg-logo.png and /dev/null differ diff --git a/images/case.png b/images/case.png deleted file mode 100644 index eee16f1d..00000000 Binary files a/images/case.png and /dev/null differ diff --git a/images/dash-logo.png b/images/dash-logo.png deleted file mode 100644 index 5c3a113d..00000000 Binary files a/images/dash-logo.png and /dev/null differ diff --git a/images/etc-logo.png b/images/etc-logo.png deleted file mode 100644 index e868d6ea..00000000 Binary files a/images/etc-logo.png and /dev/null differ diff --git a/images/eth-logo.png b/images/eth-logo.png deleted file mode 100644 index 5b7c1a8a..00000000 Binary files a/images/eth-logo.png and /dev/null differ diff --git a/images/icontrezor.png b/images/icontrezor.png deleted file mode 100644 index 8c35659b..00000000 Binary files a/images/icontrezor.png and /dev/null differ diff --git a/images/ltc-logo.png b/images/ltc-logo.png deleted file mode 100644 index 201f2350..00000000 Binary files a/images/ltc-logo.png and /dev/null differ diff --git a/images/zec-logo.png b/images/zec-logo.png deleted file mode 100644 index 134ba792..00000000 Binary files a/images/zec-logo.png and /dev/null differ diff --git a/package.json b/package.json index 55bc64da..62795433 100644 --- a/package.json +++ b/package.json @@ -21,29 +21,32 @@ }, "dependencies": { "babel-preset-react": "^6.24.1", + "bowser": "^1.9.2", "color-hash": "^1.0.3", "ethereumjs-tx": "^1.3.3", "ethereumjs-units": "^0.2.0", - "ethereumjs-util": "^5.1.2", - "hdkey": "0.7.1", + "ethereumjs-util": "^5.1.4", + "hdkey": "^0.8.0", "path-to-regexp": "^2.1.0", "raf": "^3.4.0", + "raven-js": "^3.22.3", "rc-tooltip": "^3.7.0", - "react": "^16.1.1", + "react": "^16.2.0", "react-blockies": "^1.2.2", "react-css-transition": "^0.7.4", - "react-dom": "^16.1.1", + "react-dom": "^16.2.0", "react-ellipsis-text": "^1.0.0", "react-hot-loader": "^3.1.3", "react-qr-svg": "^2.1.0", - "react-redux": "^5.0.6", + "react-redux": "^5.0.7", "react-router-dom": "^4.2.2", "react-router-redux": "next", "react-scale-text": "^1.2.2", - "react-select": "^1.1.0", + "react-select": "^1.2.1", "react-transition-group": "^2.2.1", "redux": "^3.7.2", "redux-logger": "^3.0.6", + "redux-raven-middleware": "^1.2.0", "redux-thunk": "^2.2.0", "web3": "^0.19.0" }, @@ -56,22 +59,22 @@ "babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-webpack-alias": "^2.1.2", "babel-preset-env": "^1.6.0", - "copy-webpack-plugin": "^4.0.1", - "css-loader": "^0.28.4", + "copy-webpack-plugin": "^4.4.1", + "css-loader": "^0.28.9", "express": "^4.15.3", "extract-text-webpack-plugin": "^3.0.0", - "file-loader": "^0.11.2", + "file-loader": "^1.1.9", "html-webpack-plugin": "^2.29.0", "json-loader": "^0.5.7", - "less": "^2.7.2", + "less": "^3.0.1", "less-loader": "^4.0.5", "open": "^0.0.5", - "style-loader": "^0.18.2", - "webpack": "^3.4.1", - "webpack-dev-middleware": "^1.11.0", - "webpack-hot-middleware": "^2.18.0", + "style-loader": "^0.20.2", + "webpack": "3.11.0", + "webpack-dev-middleware": "2.0.5", + "webpack-hot-middleware": "2.21.0", "webpack-merge": "^4.1.1", "whatwg-fetch": "^2.0.3", - "yargs": "^10.0.3" + "yargs": "11.0.0" } } diff --git a/src/data/appConfig.json b/src/data/appConfig.json index 2c9ce85b..3285f8a9 100644 --- a/src/data/appConfig.json +++ b/src/data/appConfig.json @@ -25,9 +25,8 @@ "coins": [ { "name": "Ethereum Ropsten", - "symbol": "eth", - "network": "ropsten-eth", - "shortcut": "eth", + "symbol": "tETH", + "network": "ropsten", "bip44": "m/44'/60'/0'/0", "defaultGasPrice": 64, "defaultGasLimit": 21000, @@ -38,16 +37,15 @@ "urls": [ "https://ropsten.infura.io/QGyVKozSUEh2YhL4s2G4", "http://10.34.2.5:8545" - ], - "explorer": "https://blockexplorer.com" + ] } - ] + ], + "explorer": "https://ropsten.etherscan.io" }, { "name": "Ethereum Rinkeby", - "symbol": "etc", - "network": "ropsten-eth", - "shortcut": "etc", + "symbol": "tETH", + "network": "rinkeby", "bip44": "m/44'/61'/0'/0", "defaultGasPrice": 64, "defaultGasLimit": 21000, @@ -56,19 +54,28 @@ { "name": "TREZOR Wallet - Ethereum", "urls": [ - "https://rinkeby.infura.io/QGyVKozSUEh2YhL4s2G4", - "http://10.34.2.5:8545" - ], - "explorer": "https://blockexplorer.com" + "https://rinkeby.infura.io/QGyVKozSUEh2YhL4s2G4" + ] } - ] + ], + "explorer": "https://rinkeby.etherscan.io" } ], "fiatValueTickers": [ - + { + "network": "ropsten", + "url": "https://api.coinmarketcap.com/v1/ticker/ethereum/" + }, + { + "network": "rinkeby", + "url": "https://api.coinmarketcap.com/v1/ticker/ethereum-classic/" + } ], + + + "bridge": { "url": "https://localback.net:21324", "configUrl": "data/config_signed.bin", @@ -76,6 +83,20 @@ }, "extensionId": "jcjjhjgimijdkoamemaghajlhegmoclj", "storageVersion": "1.1.0", - "metadataVersion": "1.0.0" + "metadataVersion": "1.0.0", + + + "supportedBrowsers": { + "chrome": { + "version": 59, + "download": "https://www.google.com/chrome/", + "update": "https://support.google.com/chrome/answer/95414" + }, + "firefox": { + "version": 54, + "download": "https://www.mozilla.org/en-US/firefox/new/", + "update": "https://support.mozilla.org/en-US/kb/update-firefox-latest-version" + } + } } \ No newline at end of file diff --git a/src/fonts/glyphicons.eot b/src/fonts/glyphicons.eot deleted file mode 100755 index 56792f69..00000000 Binary files a/src/fonts/glyphicons.eot and /dev/null differ diff --git a/src/fonts/glyphicons.svg b/src/fonts/glyphicons.svg deleted file mode 100755 index 48cddc5a..00000000 --- a/src/fonts/glyphicons.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/fonts/glyphicons.ttf b/src/fonts/glyphicons.ttf deleted file mode 100755 index f1c30440..00000000 Binary files a/src/fonts/glyphicons.ttf and /dev/null differ diff --git a/src/fonts/glyphicons.woff b/src/fonts/glyphicons.woff deleted file mode 100755 index a0fa4cb6..00000000 Binary files a/src/fonts/glyphicons.woff and /dev/null differ diff --git a/src/fonts/icomoon.eot b/src/fonts/icomoon.eot index 153870a4..d2e31941 100755 Binary files a/src/fonts/icomoon.eot and b/src/fonts/icomoon.eot differ diff --git a/src/fonts/icomoon.svg b/src/fonts/icomoon.svg index 05e7fc9c..d4bb26cc 100755 --- a/src/fonts/icomoon.svg +++ b/src/fonts/icomoon.svg @@ -18,24 +18,25 @@ - - - - + + + + - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/icomoon.ttf b/src/fonts/icomoon.ttf index 1e3aa3a8..3e7342b4 100755 Binary files a/src/fonts/icomoon.ttf and b/src/fonts/icomoon.ttf differ diff --git a/src/fonts/icomoon.woff b/src/fonts/icomoon.woff index 65c1cc49..17be113b 100755 Binary files a/src/fonts/icomoon.woff and b/src/fonts/icomoon.woff differ diff --git a/src/fonts/pass.ttf b/src/fonts/pass.ttf deleted file mode 100644 index c0f50b1b..00000000 Binary files a/src/fonts/pass.ttf and /dev/null differ diff --git a/src/images/T.png b/src/images/T.png new file mode 100644 index 00000000..54fdab30 Binary files /dev/null and b/src/images/T.png differ diff --git a/src/images/bth-logo.png b/src/images/bth-logo.png deleted file mode 100644 index dcc596e2..00000000 Binary files a/src/images/bth-logo.png and /dev/null differ diff --git a/src/images/case.png b/src/images/case.png index eee16f1d..93b69b18 100644 Binary files a/src/images/case.png and b/src/images/case.png differ diff --git a/src/images/icons-spritesheet.svg b/src/images/icons-spritesheet.svg deleted file mode 100644 index 337b1466..00000000 --- a/src/images/icons-spritesheet.svg +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/images/landingpage.png b/src/images/landingpage.png deleted file mode 100644 index 43e64752..00000000 Binary files a/src/images/landingpage.png and /dev/null differ diff --git a/src/index.html b/src/index.html index 257d119f..53f5fb92 100644 --- a/src/index.html +++ b/src/index.html @@ -4,7 +4,7 @@ - Ethereum Wallet| TREZOR + Ethereum Wallet | TREZOR diff --git a/src/js/actions/LocalStorageActions.js b/src/js/actions/LocalStorageActions.js index e6d2203c..bfe8a594 100644 --- a/src/js/actions/LocalStorageActions.js +++ b/src/js/actions/LocalStorageActions.js @@ -35,7 +35,6 @@ export function loadTokensFromJSON(): any { const ethERC20 = await httpRequest('data/ethERC20.json', 'json'); const devices: ?string = get('devices'); - console.log("GET23", JSON.parse(devices)) if (devices) { dispatch({ type: CONNECT.DEVICE_FROM_STORAGE, @@ -59,6 +58,14 @@ export function loadTokensFromJSON(): any { }) } + const pending: ?string = get('pending'); + if (pending) { + dispatch({ + type: 'PENDING.FROM_STORAGE', + payload: JSON.parse(pending) + }) + } + const discovery: ?string = get('discovery'); if (discovery) { dispatch({ @@ -88,12 +95,11 @@ export function loadTokensFromJSON(): any { export const save = (key: string, value: string): any => { return (dispatch, getState) => { if (typeof window.localStorage !== 'undefined') { - //console.log("SAVEE!!!!", key, value) try { window.localStorage.setItem(key, value); } catch (error) { // available = false; - console.error("ERROR: " + error) + console.error("Local Storage ERROR: " + error) } } } @@ -102,7 +108,6 @@ export const save = (key: string, value: string): any => { export const get = (key: string): ?string => { if (typeof window.localStorage !== 'undefined') { try { - console.log("GETTT", JSON.parse(window.localStorage.getItem(key))) return window.localStorage.getItem(key); } catch (error) { // available = false; diff --git a/src/js/actions/LogActions.js b/src/js/actions/LogActions.js new file mode 100644 index 00000000..216f88e6 --- /dev/null +++ b/src/js/actions/LogActions.js @@ -0,0 +1,15 @@ +/* @flow */ +'use strict'; + +export const toggle = (): any => { + return (dispatch, getState) => { + + if (!getState().log.opened) { + window.scrollTo(0, 0); + } + + dispatch({ + type: getState().log.opened ? 'log__close' : 'log__open' + }); + } +} diff --git a/src/js/actions/ReceiveActions.js b/src/js/actions/ReceiveActions.js index 4ee0ee7c..33a7043e 100644 --- a/src/js/actions/ReceiveActions.js +++ b/src/js/actions/ReceiveActions.js @@ -65,6 +65,7 @@ export const showAddress = (address_n: string): any => { return async (dispatch, getState) => { const selected = findSelectedDevice(getState().connect); + if (!selected) return; if (selected && !selected.connected) { dispatch({ diff --git a/src/js/actions/SendFormActions.js b/src/js/actions/SendFormActions.js index f923ce7c..7b9eecf3 100644 --- a/src/js/actions/SendFormActions.js +++ b/src/js/actions/SendFormActions.js @@ -4,7 +4,7 @@ import * as SEND from './constants/SendForm'; import * as NOTIFICATION from './constants/notification'; -import { getNonce, estimateGas, getGasPrice, pushTx } from './Web3Actions'; +import { estimateGas, getGasPrice, pushTx } from './Web3Actions'; import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUnits from 'ethereumjs-units'; @@ -18,7 +18,8 @@ import { initialState } from '../reducers/SendFormReducer'; import type { State, FeeLevel } from '../reducers/SendFormReducer'; import { findSelectedDevice } from '../reducers/TrezorConnectReducer'; -const numberRegExp = new RegExp('^([0-9]{0,10}\\.)?[0-9]{1,18}$'); +//const numberRegExp = new RegExp('^([0-9]{0,10}\\.)?[0-9]{1,18}$'); +const numberRegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9]+\\.?([0-9]+)?|\\.[0-9]+)$'); const calculateFee = (gasPrice: string, gasLimit: string): string => { return EthereumjsUnits.convert( new BigNumber(gasPrice).times(gasLimit), 'gwei', 'ether'); @@ -50,7 +51,6 @@ export const getFeeLevels = (coin: string, gasPrice: BigNumber | string, gasLimi const quarter: BigNumber = gasPrice.dividedBy(4); const high: string = gasPrice.plus(quarter.times(2)).toString(); const low: string = gasPrice.minus(quarter.times(2)).toString(); - coin = coin.toUpperCase(); return [ { @@ -106,11 +106,11 @@ export const init = (): any => { // TODO: check if there are some unfinished tx in localStorage const { config } = getState().localStorage; - const coin = config.coins.find(c => c.symbol === urlParams.coin); + const coin = config.coins.find(c => c.network === urlParams.coin); const gasPrice: BigNumber = new BigNumber( EthereumjsUnits.convert(web3instance.gasPrice, 'wei', 'gwei') ) || new BigNumber(coin.defaultGasPrice); const gasLimit: string = coin.defaultGasLimit.toString(); - const feeLevels: Array = getFeeLevels(urlParams.coin, gasPrice, gasLimit); + const feeLevels: Array = getFeeLevels(coin.symbol, gasPrice, gasLimit); // TODO: get nonce @@ -119,6 +119,7 @@ export const init = (): any => { checksum: selected.checksum, accountIndex: parseInt(urlParams.address), coin: urlParams.coin, + coinSymbol: coin.symbol, token: urlParams.coin, location: location.pathname, @@ -204,15 +205,32 @@ export const validation = (): any => { errors.amount = 'Amount is not a number'; } else { const account = getState().accounts.find(a => a.checksum === state.checksum && a.index === state.accountIndex && a.coin === state.coin); + let decimalRegExp; + if (state.token !== state.coin) { - const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token).balance; - if (new BigNumber(state.total).greaterThan(account.balance)) { - errors.amount = `Not enough ${ state.coin.toUpperCase() } to cover transaction fee`; - } else if (new BigNumber(state.amount).greaterThan(tokenBalance)) { + const token: any = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token); + + if (parseInt(token.decimals) > 0) { + decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$'); + } else { + // decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$'); + decimalRegExp = new RegExp('^[0-9]+$'); + } + + if (!state.amount.match(decimalRegExp)) { + errors.amount = `Maximum ${ token.decimals} decimals allowed`; + } else if (new BigNumber(state.total).greaterThan(account.balance)) { + errors.amount = `Not enough ${ state.coinSymbol.toUpperCase() } to cover transaction fee`; + } else if (new BigNumber(state.amount).greaterThan(token.balance)) { errors.amount = 'Not enough funds'; + } else if (new BigNumber(state.amount).lessThanOrEqualTo('0')) { + errors.amount = 'Amount is too low'; } } else { - if (new BigNumber(state.total).greaterThan(account.balance)) { + decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9]+\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$'); + if (!state.amount.match(decimalRegExp)) { + errors.amount = `Maximum 18 decimals allowed`; + } else if (new BigNumber(state.total).greaterThan(account.balance)) { errors.amount = 'Not enough funds'; } } @@ -245,7 +263,7 @@ export const validation = (): any => { const gp: BigNumber = new BigNumber(state.gasPrice); if (gp.greaterThan(100)) { errors.gasPrice = 'Gas price is too high'; - } else if (gp.lessThan(1)) { + } else if (gp.lessThanOrEqualTo('0')) { errors.gasPrice = 'Gas price is too low'; } } @@ -334,7 +352,7 @@ export const onCurrencyChange = (currency: any): any => { } const { config } = getState().localStorage; - const coin = config.coins.find(c => c.symbol === currentState.coin); + const coin = config.coins.find(c => c.network === currentState.coin); let gasLimit: string = ''; let amount: string = currentState.amount; @@ -355,7 +373,7 @@ export const onCurrencyChange = (currency: any): any => { total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit); } - const feeLevels: Array = getFeeLevels(currentState.coin, currentState.gasPrice, gasLimit); + const feeLevels: Array = getFeeLevels(currentState.coinSymbol, currentState.gasPrice, gasLimit); const state: State = { ...currentState, @@ -433,7 +451,7 @@ export const onFeeLevelChange = (feeLevel: any): any => { // TODO: update value for custom fee state.advanced = true; feeLevel.gasPrice = state.gasPrice; - feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coin.toUpperCase() }`; + feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coinSymbol }`; } else { const customLevel = state.feeLevels.find(f => f.value === 'Custom'); customLevel.label = ''; @@ -462,12 +480,14 @@ export const onFeeLevelChange = (feeLevel: any): any => { export const updateFeeLevels = (): any => { return (dispatch, getState): void => { const currentState = getState().sendForm; - const feeLevels: Array = getFeeLevels(currentState.coin, currentState.recommendedGasPrice, currentState.gasLimit); + const feeLevels: Array = getFeeLevels(currentState.coinSymbol, currentState.recommendedGasPrice, currentState.gasLimit); + const selectedFeeLevel: ?FeeLevel = feeLevels.find(f => f.value === currentState.selectedFeeLevel.value) const state: State = { ...currentState, feeLevels, - selectedFeeLevel: feeLevels.find(f => f.value === currentState.selectedFeeLevel.value), - gasPrice: currentState.recommendedGasPrice, + selectedFeeLevel, + //gasPrice: currentState.recommendedGasPrice, // TODO HERE! + gasPrice: selectedFeeLevel.gasPrice, // TODO HERE! gasPriceNeedsUpdate: false, }; @@ -507,7 +527,7 @@ export const onGasPriceChange = (gasPrice: string): any => { if (gasPrice.match(numberRegExp) && state.gasLimit.match(numberRegExp)) { const customLevel = currentState.feeLevels.find(f => f.value === 'Custom'); customLevel.gasPrice = gasPrice; - customLevel.label = `${ calculateFee(gasPrice, state.gasLimit) } ${ state.coin.toUpperCase() }`; + customLevel.label = `${ calculateFee(gasPrice, state.gasLimit) } ${ state.coinSymbol }`; state.selectedFeeLevel = customLevel; @@ -547,7 +567,7 @@ export const onGasLimitChange = (gasLimit: string): any => { if (gasLimit.match(numberRegExp) && state.gasPrice.match(numberRegExp)) { const customLevel = currentState.feeLevels.find(f => f.value === 'Custom'); - customLevel.label = `${ calculateFee(state.gasPrice, gasLimit) } ${ state.coin.toUpperCase() }`; + customLevel.label = `${ calculateFee(state.gasPrice, gasLimit) } ${ state.coinSymbol }`; state.selectedFeeLevel = customLevel; @@ -640,9 +660,6 @@ export const onSend = (): any => { v: '' } - //const nonce = await getNonce(web3, currentAddress.address); - //txData.nonce = web3.toHex(nonce); - // const gasOptions = { // to: txData.to, @@ -660,6 +677,7 @@ export const onSend = (): any => { // console.log("---->GASSS", txData, gasLimit, gasPrice, EthereumjsUnits.convert(gasPrice, 'gwei', 'wei')); const selected = findSelectedDevice(getState().connect); + if (!selected) return; let signedTransaction = await TrezorConnect.ethereumSignTransaction({ device: { @@ -697,8 +715,11 @@ export const onSend = (): any => { txData.s = '0x' + signedTransaction.data.s; txData.v = web3.toHex(signedTransaction.data.v); - const gasLimit2 = await estimateGas(web3, txData); - console.log("---->GASSS", txData, gasLimit2.toString() ); + // const gasLimit2 = await estimateGas(web3, txData); + // console.log("---->GASSS", txData, gasLimit2.toString() ); + + const { config } = getState().localStorage; + const selectedCoin = config.coins.find(c => c.network === state.coin); try { const tx = new EthereumjsTx(txData); @@ -708,6 +729,8 @@ export const onSend = (): any => { dispatch({ type: SEND.TX_COMPLETE, address: account, + token: state.token, + amount: state.amount, txid, txData, }); @@ -717,7 +740,7 @@ export const onSend = (): any => { payload: { type: 'success', title: 'Transaction success', - message: `detail`, + message: `See transaction detail`, cancelable: true, actions: [] } @@ -736,23 +759,5 @@ export const onSend = (): any => { } }); } - - // const tx = new EthereumjsTx(txData); - // console.log("2222", tx, tx.toJSON(), tx.from, tx.to); - // const serializedTx = '0x' + tx.serialize().toString('hex'); - - // console.log("----> PUSZ TX", web3, currentAddress, serializedTx) - // const txid = await pushTx(web3, serializedTx); - // console.log("----> PUSZ TX2", web3, serializedTx) - - // dispatch({ - // type: SEND.TX_COMPLETE, - // address: currentAddress, - // txid, - // txData, - // }) - - // const [ url ] = getState().router.location.pathname.split('/send'); - // dispatch( push(url) ); } } diff --git a/src/js/actions/SummaryActions.js b/src/js/actions/SummaryActions.js index 6a2ad931..b9c0f492 100644 --- a/src/js/actions/SummaryActions.js +++ b/src/js/actions/SummaryActions.js @@ -106,11 +106,12 @@ export const onDetailsToggle = (): any => { -export const loadTokens = (input: string): any => { +export const loadTokens = (input: string, account: any): any => { return async (dispatch, getState): Promise => { if (input.length < 1) return null; + // TODO (eth tokens, etc tokens, ropsten tokens ...) const { ethTokens } = getState().localStorage; const value = input.toLowerCase(); @@ -121,18 +122,14 @@ export const loadTokens = (input: string): any => { ); //const result = ethTokens.filter(t => t.symbol.toLowerCase().indexOf(lower) >= 0); - console.log("RESULT!", result.length, result) - if (result.length > 0) { return { options: result }; } else { - const web3instance = getState().web3.find(w3 => w3.coin === 'eth'); + const web3instance = getState().web3.find(w3 => w3.coin === account.coin); const info = await getTokenInfoAsync(web3instance.erc20, input); info.address = input; - console.log("FETCH", info) - if (info) { return { options: [ info ] @@ -143,13 +140,9 @@ export const loadTokens = (input: string): any => { } } - //await resolveAfter(300000); //await resolveAfter(3000); - - - } } @@ -158,10 +151,7 @@ export const loadTokens = (input: string): any => { export const selectToken = (token: any, account: any): any => { return async (dispatch, getState): Promise => { - console.warn("ADD", token, account) - const web3instance = getState().web3.find(w3 => w3.coin === account.coin); - dispatch({ type: TOKEN.ADD, payload: { @@ -171,7 +161,6 @@ export const selectToken = (token: any, account: any): any => { } }); - // TODO: load token balance const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, token.address, account.address); dispatch({ type: TOKEN.SET_BALANCE, @@ -185,6 +174,13 @@ export const selectToken = (token: any, account: any): any => { } } +export const removeToken = (token: any): any => { + return { + type: TOKEN.REMOVE, + token + } +} + export const onTokenSearch = (search: string): any => { diff --git a/src/js/actions/TrezorConnectActions.js b/src/js/actions/TrezorConnectActions.js index d12f2a7f..a725f742 100644 --- a/src/js/actions/TrezorConnectActions.js +++ b/src/js/actions/TrezorConnectActions.js @@ -19,7 +19,7 @@ import { getTransactionHistory } from '../services/EtherscanService'; import { push } from 'react-router-redux'; -import { init as initWeb3, getNonce, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions'; +import { init as initWeb3, getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions'; import type { Discovery } from '../reducers/DiscoveryReducer'; import { resolveAfter } from '../utils/promiseUtils'; @@ -53,6 +53,10 @@ export const init = (): any => { try { await TrezorConnect.init({ transport_reconnect: true, + coins_src: './data/coins.json', + firmware_releases_src: './data/releases-1.json', + transport_config_src: './data/config_signed.bin', + latest_bridge_src: './data/latest.txt' }); setTimeout(() => { @@ -116,12 +120,19 @@ export const postInit = (): any => { export const initConnectedDevice = (device: any): any => { return (dispatch, getState): void => { + //dispatch( onSelectDevice(device) ); + const selected = findSelectedDevice(getState().connect); - if (device.unacquired && selected && selected.path !== device.path && !selected.connected) { + if (selected && selected.checksum) { dispatch( onSelectDevice(device) ); } else if (!selected) { dispatch( onSelectDevice(device) ); } + // if (device.unacquired && selected && selected.path !== device.path && !selected.connected) { + // dispatch( onSelectDevice(device) ); + // } else if (!selected) { + // dispatch( onSelectDevice(device) ); + // } } } @@ -235,47 +246,27 @@ export const getSelectedDeviceState = (): any => { export const deviceDisconnect = (device: any): any => { return async (dispatch, getState): Promise => { - if (!device || !device.features) return null; - const selected = findSelectedDevice(getState().connect); - if (selected && selected.features.device_id === device.features.device_id) { - stopDiscoveryProcess(selected); - } - - const affected = getState().connect.devices.filter(d => d.features && d.checksum && !d.remember && d.features.device_id === device.features.device_id); - if (affected.length > 0) { - dispatch({ - type: CONNECT.REMEMBER_REQUEST, - device, - allInstances: affected - }); - } - - - // if (selected && selected.checksum) { - // if (device.features && device.features.device_id === selected.features.device_id) { - // stopDiscoveryProcess(selected); - // } - // } - - - // // stop running discovery process on this device - // if (selected && selected.path === device.path){ - // if (selected.checksum) { - // stopDiscoveryProcess(selected); - // } - // } + if (device && device.features) { + if (selected && selected.features.device_id === device.features.device_id) { + stopDiscoveryProcess(selected); + } - // // check if disconnected device was remembered before. - // // request modal if not - // const affected = getState().connect.devices.filter(d => d.path === device.path && d.checksum && !device.remember); - + const affected = getState().connect.devices.filter(d => d.features && d.checksum && !d.remember && d.features.device_id === device.features.device_id); + if (affected.length > 0) { + dispatch({ + type: CONNECT.REMEMBER_REQUEST, + device, + allInstances: affected + }); + } + } - // check if reload is needed if (!selected) { dispatch( switchToFirstAvailableDevice() ); } + } } @@ -397,7 +388,7 @@ export const beginDiscoveryProcess = (device: any, coin: string): any => { return async (dispatch, getState) => { const { config } = getState().localStorage; - const coinToDiscover = config.coins.find(c => c.symbol === coin); + const coinToDiscover = config.coins.find(c => c.network === coin); // TODO: validate device checksum // const checksum = await __acquire(device.path, device.instance); @@ -455,7 +446,7 @@ export const beginDiscoveryProcess = (device: any, coin: string): any => { // send data to reducer dispatch({ type: DISCOVERY.START, - coin: coinToDiscover.shortcut, + coin: coinToDiscover.network, device, xpub: response.data.publicKey, basePath, @@ -574,7 +565,7 @@ export const discoverAddress = (device: any, discoveryProcess: Discovery): any = }) } - const nonce = await getNonce(web3instance.web3, ethAddress); + const nonce = await getNonceAsync(web3instance.web3, ethAddress); if (discoveryProcess.interrupted) return; dispatch({ type: ADDRESS.SET_NONCE, diff --git a/src/js/actions/Web3Actions.js b/src/js/actions/Web3Actions.js index dd98876f..3efb1833 100644 --- a/src/js/actions/Web3Actions.js +++ b/src/js/actions/Web3Actions.js @@ -15,6 +15,41 @@ import { httpRequest } from '../utils/networkUtils'; type ActionMethod = (dispatch: any, getState: any) => Promise; +type Web3Payload = +| { + name: string; + instance: Web3; + chainId: number; + erc20abi: any; +} +| { + network: string; + blockHash: string; +} +| { + network: string; + gasPrice: string; +} +| { + network: string; + address: string; + balance: string; +} +| { + network: string; + address: string; + nonce: string; +} +| { + network: string; + blockHash: string; +}; + +type Web3Action = { + type: string, + payload?: Web3Payload +}; + export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod { return async (dispatch, getState) => { @@ -30,7 +65,7 @@ export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod { return; } - const coinName = coin.shortcut; + const coinName = coin.network; const urls = coin.backends[0].urls; let web3host: string = urls[0]; @@ -106,36 +141,42 @@ export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod { //const shh = instance.shh.newIdentity(); const latestBlockFilter = instance.eth.filter('latest'); - latestBlockFilter.watch(async (error, blockHash) => { + const onBlockMined = async (error, blockHash) => { if (error) { - console.warn("ERROR!", error); - // setInterval(() => { - // dispatch( getGasPrice(coinName) ); - // }, 5000); + window.setTimeout(() => { + // try again + onBlockMined("manually_triggered_error", undefined); + }, 30000); + } + + if (blockHash) { + dispatch({ + type: WEB3.BLOCK_UPDATED, + name: coinName, + blockHash + }); } - - dispatch({ - type: WEB3.BLOCK_UPDATED, - name: coinName, - blockHash - }); // TODO: filter only current device const accounts = getState().accounts.filter(a => a.coin === coinName); for (const addr of accounts) { dispatch( getBalance(addr) ); + dispatch( getNonce(addr) ); } dispatch( getGasPrice(coinName) ); - // if (pendingTxs.length > 0) { - // for (const tx of pendingTxs) { - // dispatch( getTransactionReceipt(tx) ); - // } - // } - }); + const pending = getState().pending.filter(p => p.coin === coinName); + for (const tx of pending) { + dispatch( getTransactionReceipt(tx) ); + } + + } + + latestBlockFilter.watch(onBlockMined); + // init next coin dispatch( init(instance, coinIndex + 1) ); @@ -238,18 +279,46 @@ export function getBalance(addr: Address): ActionMethod { } } -export function getTransactionReceipt(txid: string): any { +export function getNonce(addr: Address) { + return async (dispatch, getState) => { - const { web3 } = getState().web3; + + const web3instance = getState().web3.filter(w3 => w3.coin === addr.coin)[0]; + const web3 = web3instance.web3; + + web3.eth.getTransactionCount(addr.address, (error, result) => { + if (!error) { + if (addr.nonce !== result) { + dispatch({ + type: ADDRESS.SET_NONCE, + address: addr.address, + nonce: result + }); + } + + + } + }); + } +} + +export function getTransactionReceipt(tx: any): any { + return async (dispatch, getState) => { + + const web3instance = getState().web3.filter(w3 => w3.coin === tx.coin)[0]; + const web3 = web3instance.web3; + //web3.eth.getTransactionReceipt(txid, (error, tx) => { - web3.eth.getTransaction(txid, (error, tx) => { - if (tx && tx.blockNumber) { - web3.eth.getBlock(tx.blockHash, (error, block) => { - console.log("---MAMM BLOCK", error, block, tx, tx.blockHash) + web3.eth.getTransaction(tx.id, (error, receipt) => { + console.log("RECEIP", receipt) + if (receipt && receipt.blockNumber) { + web3.eth.getBlock(receipt.blockHash, (error, block) => { + console.log("---MAMM BLOCK", error, block, receipt, receipt.blockHash) dispatch({ - type: ACTIONS.TX_CONFIRMED, - txid, + //type: ACTIONS.TX_CONFIRMED, + type: WEB3.PENDING_TX_RESOLVED, tx, + receipt, block }) }); @@ -306,7 +375,7 @@ export const getTokenBalanceAsync = (erc20: any, token: any, address: any): Prom }); } -export function getNonce(web3, address) { +export function getNonceAsync(web3, address) { return new Promise((resolve, reject) => { web3.eth.getTransactionCount(address, (error, result) => { if (error) { @@ -389,81 +458,6 @@ export function pushTx(web3, tx) { }) } -export function composeTransaction() { - return async function (dispatch, getState) { - const { web3 } = getState().web3; - const { address, amount } = getState().sendForm; - - const resp = await TrezorConnect.getPublicKey({ path: "m/44'/60'/0'/0", confirmation: false }); - - const hdk = new HDKey(); - hdk.publicKey = new Buffer(resp.data.publicKey, 'hex'); - hdk.chainCode = new Buffer(resp.data.chainCode, 'hex'); - - const derivedKey = hdk.derive("m/0"); - const myAddress = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true); - - const txData = { - address_n: [ - (44 | 0x80000000) >>> 0, - (60 | 0x80000000) >>> 0, - (0 | 0x80000000) >>> 0, - 0, 0 - ], - to: address, - value: web3.toHex(web3.toWei(amount, 'ether')), - data, - chainId: 3 - } - - console.log("NONCE", myAddress) - const nonce = await getNonce(web3, '0x' + myAddress.toString('hex') ); - console.log("NONCE", nonce) - - const gasOptions = { - to: txData.to, - data: txData.data - } - const gasLimit = await estimateGas(web3, gasOptions); - const gasPrice = await getGasPrice(web3); - - txData.nonce = web3.toHex(nonce); - txData.gasLimit = web3.toHex(gasLimit); - txData.gasPrice = web3.toHex(gasPrice); - - console.log("NONCE", nonce, gasLimit, gasPrice) - - let signedTransaction = await TrezorConnect.ethereumSignTransaction({ - //path: "m/44'/60'/0'/0/0", - address_n: txData.address_n, - nonce: strip(txData.nonce), - gas_price: strip(txData.gasPrice), - gas_limit: strip(txData.gasLimit), - to: strip(txData.to), - value: strip(txData.value), - data: txData.data, - chain_id: txData.chainId - }); - - txData.r = '0x' + signedTransaction.data.r; - txData.s = '0x' + signedTransaction.data.s; - txData.v = web3.toHex(signedTransaction.data.v); - - const tx = new EthereumjsTx(txData); - const serializedTx = '0x' + tx.serialize().toString('hex'); - - const txid = await pushTx(web3, serializedTx); - - dispatch({ - type: 'tx_complete', - txid - }) - - console.log("TXID", txid); - } -} - - diff --git a/src/js/actions/constants/Web3.js b/src/js/actions/constants/Web3.js index 6334e1a0..f1e3eee8 100644 --- a/src/js/actions/constants/Web3.js +++ b/src/js/actions/constants/Web3.js @@ -6,4 +6,5 @@ export const STOP: string = 'web3__stop'; export const CREATE: string = 'web3__create'; export const READY: string = 'web3__ready'; export const BLOCK_UPDATED: string = 'web3__block_updated'; -export const GAS_PRICE_UPDATED: string = 'web3__gas_price_updated'; \ No newline at end of file +export const GAS_PRICE_UPDATED: string = 'web3__gas_price_updated'; +export const PENDING_TX_RESOLVED: string = 'web3__pending_tx_resolved'; \ No newline at end of file diff --git a/src/js/actions/index.js b/src/js/actions/index.js index 45a72ee2..c0e59632 100644 --- a/src/js/actions/index.js +++ b/src/js/actions/index.js @@ -15,7 +15,6 @@ export const ON_GAS_PRICE_CHANGE: string = 'send__on_gas_price_change'; export const ON_GAS_LIMIT_CHANGE: string = 'send__on_gas_limit_change'; export const ON_TX_DATA_CHANGE: string = 'send__on_data_change'; export const ON_TX_SEND: string = 'send__on_send'; -export const ON_TX_COMPLETE: string = 'send__on_tx_complete'; export const ON_GAS_PRICE_UPDATE: string = 'send__on_gas_price_update'; diff --git a/src/js/components/common/Footer.js b/src/js/components/common/Footer.js index d69f8dc7..552d68f3 100644 --- a/src/js/components/common/Footer.js +++ b/src/js/components/common/Footer.js @@ -2,16 +2,31 @@ 'use strict'; import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +import * as LogActions from '../../actions/LogActions'; const Footer = (props: any): any => { return ( ); } -export default Footer; +export default connect( + (state) => { + return { + + } + }, + (dispatch) => { + return { + toggle: bindActionCreators(LogActions.toggle, dispatch), + }; + } +)(Footer); diff --git a/src/js/components/common/Header.js b/src/js/components/common/Header.js index 7ffe5b0b..9e8295d9 100644 --- a/src/js/components/common/Header.js +++ b/src/js/components/common/Header.js @@ -8,9 +8,26 @@ export default class Header extends Component { return (
- + + + + + + + + + +
+ TREZOR + Docs + Blog + Support +
); } } + + + diff --git a/src/js/components/common/Log.js b/src/js/components/common/Log.js index 62ac4e18..40aaedc8 100644 --- a/src/js/components/common/Log.js +++ b/src/js/components/common/Log.js @@ -5,36 +5,32 @@ import React from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import * as SendFormActions from '../../actions/SendFormActions'; -import { getAddress } from '../../actions/TrezorConnectActions'; - +import * as LogActions from '../../actions/LogActions'; const Log = (props: any) => { - return ( -
- Log -
- ) -} -function mapStateToProps(state, own) { - -} - -function mapDispatchToProps(dispatch) { + if (!props.log.opened) + return null; + return ( +
+ +

Log

+

Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.

+ +
+ ) } export default connect( (state) => { return { - accounts: state.accounts, - receive: state.receive + log: state.log }; }, (dispatch) => { return { - getAddress: bindActionCreators(getAddress, dispatch), + toggle: bindActionCreators(LogActions.toggle, dispatch), }; } )(Log); \ No newline at end of file diff --git a/src/js/components/landing/ConnectDevice.js b/src/js/components/landing/ConnectDevice.js index 32f25e01..f5dcb671 100644 --- a/src/js/components/landing/ConnectDevice.js +++ b/src/js/components/landing/ConnectDevice.js @@ -25,10 +25,10 @@ export default (props: any): any => { Connect TREZOR to continue

- {/*

Don't have TREZOR? Get one

*/} + {/*

Don't have TREZOR? Get one

*/}
-

Don't have TREZOR? Get one

+

Don't have TREZOR? Get one