From afb578886134663506320e7462935d3431512a9a Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Wed, 30 May 2018 15:53:18 +0200 Subject: [PATCH 01/24] initial implementation --- app/scripts/controllers/transactions/index.js | 6 +++- .../lib/recipient-blacklist-checker.js | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index aff5db984..3d6f5beb5 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -10,6 +10,7 @@ const NonceTracker = require('./nonce-tracker') const txUtils = require('./lib/util') const cleanErrorStack = require('../../lib/cleanErrorStack') const log = require('loglevel') +const recipientBlackListChecker = require('./lib/recipient-blacklist-checker') /** Transaction Controller is an aggregate of sub-controllers and trackers @@ -157,8 +158,11 @@ class TransactionController extends EventEmitter { let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) this.addTx(txMeta) this.emit('newUnapprovedTx', txMeta) - // add default tx params + try { + // check whether recipient account is public + await recipientBlackListChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to) + // add default tx params txMeta = await this.addTxGasDefaults(txMeta) } catch (error) { console.log(error) diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js new file mode 100644 index 000000000..f6fbee678 --- /dev/null +++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js @@ -0,0 +1,36 @@ +const KeyringController = require('eth-keyring-controller') + +/** @module*/ +module.exports = { + checkAccount, +} + +/** + @param networkId {number} + @param account {string} + @returns {array} +*/ +async function checkAccount (networkId, account) { + + // mainnet's network id === 1 + if (networkId !== 1) { + return + } + + const damnedMnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' + const keyringController = new KeyringController({}) + const Keyring = keyringController.getKeyringClassForType('HD Key Tree') + const opts = { + mnemonic: damnedMnemonic, + numberOfAccounts: 10, + } + + const accountToCheck = account.toLowerCase() + const keyring = new Keyring(opts) + const damnedAccounts = await keyring.getAccounts() + for (let i = 0; i < damnedAccounts.length; i++) { + if (damnedAccounts[i].toLowerCase() === accountToCheck) { + throw new Error('this is a public account') + } + } +} \ No newline at end of file From 6affd8f9492e04cdc81007e4f5390e4faa56499d Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Wed, 30 May 2018 16:24:40 +0200 Subject: [PATCH 02/24] adding transaction controller tests --- .../lib/recipient-blacklist-checker.js | 2 +- .../transactions/tx-controller-test.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js index f6fbee678..c52e58863 100644 --- a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js +++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js @@ -30,7 +30,7 @@ async function checkAccount (networkId, account) { const damnedAccounts = await keyring.getAccounts() for (let i = 0; i < damnedAccounts.length; i++) { if (damnedAccounts[i].toLowerCase() === accountToCheck) { - throw new Error('this is a public account') + throw new Error('Recipient is a public account') } } } \ No newline at end of file diff --git a/test/unit/app/controllers/transactions/tx-controller-test.js b/test/unit/app/controllers/transactions/tx-controller-test.js index 1f32a0f37..9bdfe7c1a 100644 --- a/test/unit/app/controllers/transactions/tx-controller-test.js +++ b/test/unit/app/controllers/transactions/tx-controller-test.js @@ -185,6 +185,23 @@ describe('Transaction Controller', function () { .catch(done) }) + it('should fail if recipient is public', function (done) { + txController.networkStore = new ObservableStore(1) + txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' }) + .catch((err) => { + if (err.message === 'Recipient is a public account') done() + else done(err) + }) + }) + + it('should not fail if recipient is public but not on mainnet', function (done) { + txController.once('newUnapprovedTx', (txMetaFromEmit) => { + assert(txMetaFromEmit, 'txMeta is falsey') + done() + }) + txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' }) + .catch(done) + }) }) describe('#addTxGasDefaults', function () { From cf73581c0e1a90371fb23eb05318ce39027325b5 Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Wed, 30 May 2018 17:38:27 +0200 Subject: [PATCH 03/24] adding tests for recipient blacklist checker --- app/scripts/controllers/transactions/index.js | 4 +- .../lib/recipient-blacklist-checker.js | 6 +- .../recipient-blacklist-checker-test.js | 78 +++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 3d6f5beb5..16f7291d6 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -10,7 +10,7 @@ const NonceTracker = require('./nonce-tracker') const txUtils = require('./lib/util') const cleanErrorStack = require('../../lib/cleanErrorStack') const log = require('loglevel') -const recipientBlackListChecker = require('./lib/recipient-blacklist-checker') +const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker') /** Transaction Controller is an aggregate of sub-controllers and trackers @@ -161,7 +161,7 @@ class TransactionController extends EventEmitter { try { // check whether recipient account is public - await recipientBlackListChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to) + await recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to) // add default tx params txMeta = await this.addTxGasDefaults(txMeta) } catch (error) { diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js index c52e58863..414302d12 100644 --- a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js +++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js @@ -12,8 +12,8 @@ module.exports = { */ async function checkAccount (networkId, account) { - // mainnet's network id === 1 - if (networkId !== 1) { + const mainnetId = 1 + if (networkId !== mainnetId) { return } @@ -33,4 +33,4 @@ async function checkAccount (networkId, account) { throw new Error('Recipient is a public account') } } -} \ No newline at end of file +} diff --git a/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js b/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js new file mode 100644 index 000000000..b55894684 --- /dev/null +++ b/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js @@ -0,0 +1,78 @@ +const assert = require('assert') +const recipientBlackListChecker = require('../../../../../app/scripts/controllers/transactions/lib/recipient-blacklist-checker') +const { + ROPSTEN_CODE, + RINKEYBY_CODE, + KOVAN_CODE, +} = require('../../../../../app/scripts/controllers/network/enums') + +const KeyringController = require('eth-keyring-controller') + +describe('Recipient Blacklist Checker', function () { + + let publicAccounts + + before(async function () { + const damnedMnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' + const keyringController = new KeyringController({}) + const Keyring = keyringController.getKeyringClassForType('HD Key Tree') + const opts = { + mnemonic: damnedMnemonic, + numberOfAccounts: 10, + } + const keyring = new Keyring(opts) + publicAccounts = await keyring.getAccounts() + }) + + describe('#checkAccount', function () { + it('does not fail on test networks', async function () { + let callCount = 0 + const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE] + for (let networkId in networks) { + await Promise.all(publicAccounts.map(async (account) => { + await recipientBlackListChecker.checkAccount(networkId, account) + callCount++ + }) + ) + } + assert.equal(callCount, 30) + }) + + it('fails on mainnet', async function () { + const mainnetId = 1 + let callCount = 0 + await Promise.all(publicAccounts.map(async (account) => { + try { + await recipientBlackListChecker.checkAccount(mainnetId, account) + assert.fail('function should have thrown an error') + } catch (err) { + assert.equal(err.message, 'Recipient is a public account') + } + callCount++ + })) + assert.equal(callCount, 10) + }) + + it('fails for public account - uppercase', async function () { + const mainnetId = 1 + const publicAccount = '0X0D1D4E623D10F9FBA5DB95830F7D3839406C6AF2' + try { + await recipientBlackListChecker.checkAccount(mainnetId, publicAccount) + assert.fail('function should have thrown an error') + } catch (err) { + assert.equal(err.message, 'Recipient is a public account') + } + }) + + it('fails for public account - lowercase', async function () { + const mainnetId = 1 + const publicAccount = '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2' + try { + await recipientBlackListChecker.checkAccount(mainnetId, publicAccount) + assert.fail('function should have thrown an error') + } catch (err) { + assert.equal(err.message, 'Recipient is a public account') + } + }) + }) +}) From 3e489ea16506569950c10fc3636071075b2495e8 Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Wed, 30 May 2018 17:42:41 +0200 Subject: [PATCH 04/24] fix documentation --- .../controllers/transactions/lib/recipient-blacklist-checker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js index 414302d12..d44c1ddc1 100644 --- a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js +++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js @@ -6,9 +6,9 @@ module.exports = { } /** + * Checks if a specified account on a specified network is blacklisted. @param networkId {number} @param account {string} - @returns {array} */ async function checkAccount (networkId, account) { From 1dda0c646940179bec6e886117a8ecf3f0f7ab48 Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Wed, 30 May 2018 21:15:59 +0200 Subject: [PATCH 05/24] remove generating blocked accounts and use a config file instead --- app/scripts/controllers/transactions/index.js | 4 ++-- .../lib/recipient-blacklist-checker.js | 20 ++++-------------- .../lib/recipient-blacklist-config.json | 14 +++++++++++++ .../recipient-blacklist-checker-test.js | 21 +++++++++---------- 4 files changed, 30 insertions(+), 29 deletions(-) create mode 100644 app/scripts/controllers/transactions/lib/recipient-blacklist-config.json diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 16f7291d6..b53947e27 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -160,8 +160,8 @@ class TransactionController extends EventEmitter { this.emit('newUnapprovedTx', txMeta) try { - // check whether recipient account is public - await recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to) + // check whether recipient account is blacklisted + recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to) // add default tx params txMeta = await this.addTxGasDefaults(txMeta) } catch (error) { diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js index d44c1ddc1..84c6df1f0 100644 --- a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js +++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js @@ -1,4 +1,4 @@ -const KeyringController = require('eth-keyring-controller') +const Config = require('./recipient-blacklist-config.json') /** @module*/ module.exports = { @@ -10,27 +10,15 @@ module.exports = { @param networkId {number} @param account {string} */ -async function checkAccount (networkId, account) { +function checkAccount (networkId, account) { const mainnetId = 1 if (networkId !== mainnetId) { return } - const damnedMnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' - const keyringController = new KeyringController({}) - const Keyring = keyringController.getKeyringClassForType('HD Key Tree') - const opts = { - mnemonic: damnedMnemonic, - numberOfAccounts: 10, - } - const accountToCheck = account.toLowerCase() - const keyring = new Keyring(opts) - const damnedAccounts = await keyring.getAccounts() - for (let i = 0; i < damnedAccounts.length; i++) { - if (damnedAccounts[i].toLowerCase() === accountToCheck) { - throw new Error('Recipient is a public account') - } + if (Config.blacklist.includes(accountToCheck)) { + throw new Error('Recipient is a public account') } } diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-config.json b/app/scripts/controllers/transactions/lib/recipient-blacklist-config.json new file mode 100644 index 000000000..b348eb72e --- /dev/null +++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-config.json @@ -0,0 +1,14 @@ +{ + "blacklist": [ + "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "0xf17f52151ebef6c7334fad080c5704d77216b732", + "0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef", + "0x821aea9a577a9b44299b9c15c88cf3087f3b5544", + "0x0d1d4e623d10f9fba5db95830f7d3839406c6af2", + "0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e", + "0x2191ef87e392377ec08e7c08eb105ef5448eced5", + "0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5", + "0x6330a553fc93768f612722bb8c2ec78ac90b3bbc", + "0x5aeda56215b167893e80b4fe645ba6d5bab767de" + ] +} diff --git a/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js b/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js index b55894684..56e8d50db 100644 --- a/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js +++ b/test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js @@ -25,39 +25,38 @@ describe('Recipient Blacklist Checker', function () { }) describe('#checkAccount', function () { - it('does not fail on test networks', async function () { + it('does not fail on test networks', function () { let callCount = 0 const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE] for (let networkId in networks) { - await Promise.all(publicAccounts.map(async (account) => { - await recipientBlackListChecker.checkAccount(networkId, account) - callCount++ + publicAccounts.forEach((account) => { + recipientBlackListChecker.checkAccount(networkId, account) + callCount++ }) - ) } assert.equal(callCount, 30) }) - it('fails on mainnet', async function () { + it('fails on mainnet', function () { const mainnetId = 1 let callCount = 0 - await Promise.all(publicAccounts.map(async (account) => { + publicAccounts.forEach((account) => { try { - await recipientBlackListChecker.checkAccount(mainnetId, account) + recipientBlackListChecker.checkAccount(mainnetId, account) assert.fail('function should have thrown an error') } catch (err) { assert.equal(err.message, 'Recipient is a public account') } callCount++ - })) + }) assert.equal(callCount, 10) }) - it('fails for public account - uppercase', async function () { + it('fails for public account - uppercase', function () { const mainnetId = 1 const publicAccount = '0X0D1D4E623D10F9FBA5DB95830F7D3839406C6AF2' try { - await recipientBlackListChecker.checkAccount(mainnetId, publicAccount) + recipientBlackListChecker.checkAccount(mainnetId, publicAccount) assert.fail('function should have thrown an error') } catch (err) { assert.equal(err.message, 'Recipient is a public account') From 1c3d2aa18b85ddb83734d6afdbb1111ba0791229 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 30 May 2018 16:43:45 -0230 Subject: [PATCH 06/24] Importing account by json and private key shows error and does not change account if no selectedAddress comes after import. --- ui/app/actions.js | 10 ++++---- ui/app/app.js | 7 ++++-- .../create-account/import-account/json.js | 23 +++++++++++++++---- .../import-account/private-key.js | 22 ++++++++++++++---- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 649f740e9..ed3c147b8 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -544,10 +544,12 @@ function importNewAccount (strategy, args) { } dispatch(actions.hideLoadingIndication()) dispatch(actions.updateMetamaskState(newState)) - dispatch({ - type: actions.SHOW_ACCOUNT_DETAIL, - value: newState.selectedAddress, - }) + if (newState.selectedAddress) { + dispatch({ + type: actions.SHOW_ACCOUNT_DETAIL, + value: newState.selectedAddress, + }) + } return newState } } diff --git a/ui/app/app.js b/ui/app/app.js index 0e8b907df..7005adb7f 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -99,7 +99,7 @@ class App extends Component { } = this.props const isLoadingNetwork = network === 'loading' && currentView.name !== 'config' const loadMessage = loadingMessage || isLoadingNetwork ? - this.getConnectingLabel() : null + this.getConnectingLabel(loadingMessage) : null log.debug('Main ui render function') return ( @@ -210,7 +210,10 @@ class App extends Component { } } - getConnectingLabel = function () { + getConnectingLabel = function (loadingMessage) { + if (loadingMessage) { + return loadingMessage + } const { provider } = this.props const providerName = provider.type diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js index 0a3314b2a..3dabad68e 100644 --- a/ui/app/components/pages/create-account/import-account/json.js +++ b/ui/app/components/pages/create-account/import-account/json.js @@ -82,18 +82,19 @@ class JsonImportSubview extends Component { } createNewKeychain () { + const { firstAddress, displayWarning, importNewJsonAccount, setSelectedAddress } = this.props const state = this.state if (!state) { const message = this.context.t('validFileImport') - return this.props.displayWarning(message) + return displayWarning(message) } const { fileContents } = state if (!fileContents) { const message = this.context.t('needImportFile') - return this.props.displayWarning(message) + return displayWarning(message) } const passwordInput = document.getElementById('json-password-box') @@ -101,12 +102,20 @@ class JsonImportSubview extends Component { if (!password) { const message = this.context.t('needImportPassword') - return this.props.displayWarning(message) + return displayWarning(message) } - this.props.importNewJsonAccount([ fileContents, password ]) - // JS runtime requires caught rejections but failures are handled by Redux + importNewJsonAccount([ fileContents, password ]) + // JS runtime requires caught rejections but failures are handled by Redux .catch() + .then(({ selectedAddress }) => { + if (selectedAddress) { + history.push(DEFAULT_ROUTE) + } else { + displayWarning('Error importing account.') + setSelectedAddress(firstAddress) + } + }) } } @@ -114,14 +123,17 @@ JsonImportSubview.propTypes = { error: PropTypes.string, goHome: PropTypes.func, displayWarning: PropTypes.func, + firstAddress: PropTypes.string, importNewJsonAccount: PropTypes.func, history: PropTypes.object, + setSelectedAddress: PropTypes.func, t: PropTypes.func, } const mapStateToProps = state => { return { error: state.appState.warning, + firstAddress: Object.keys(state.metamask.accounts)[0], } } @@ -130,6 +142,7 @@ const mapDispatchToProps = dispatch => { goHome: () => dispatch(actions.goHome()), displayWarning: warning => dispatch(actions.displayWarning(warning)), importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)), + setSelectedAddress: (address) => dispatch(actions.setSelectedAddress(address)), } } diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js index df7ac910a..e71e47647 100644 --- a/ui/app/components/pages/create-account/import-account/private-key.js +++ b/ui/app/components/pages/create-account/import-account/private-key.js @@ -21,6 +21,7 @@ module.exports = compose( function mapStateToProps (state) { return { error: state.appState.warning, + firstAddress: Object.keys(state.metamask.accounts)[0], } } @@ -29,7 +30,8 @@ function mapDispatchToProps (dispatch) { importNewAccount: (strategy, [ privateKey ]) => { return dispatch(actions.importNewAccount(strategy, [ privateKey ])) }, - displayWarning: () => dispatch(actions.displayWarning(null)), + displayWarning: (message) => dispatch(actions.displayWarning(message || null)), + setSelectedAddress: (address) => dispatch(actions.setSelectedAddress(address)), } } @@ -40,7 +42,7 @@ function PrivateKeyImportView () { } PrivateKeyImportView.prototype.render = function () { - const { error } = this.props + const { error, displayWarning } = this.props return ( h('div.new-account-import-form__private-key', [ @@ -60,7 +62,10 @@ PrivateKeyImportView.prototype.render = function () { h('div.new-account-import-form__buttons', {}, [ h('button.btn-secondary--lg.new-account-create-form__button', { - onClick: () => this.props.history.push(DEFAULT_ROUTE), + onClick: () => { + displayWarning(null) + this.props.history.push(DEFAULT_ROUTE) + }, }, [ this.context.t('cancel'), ]), @@ -88,10 +93,17 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) { PrivateKeyImportView.prototype.createNewKeychain = function () { const input = document.getElementById('private-key-box') const privateKey = input.value - const { importNewAccount, history } = this.props + const { importNewAccount, history, displayWarning, setSelectedAddress, firstAddress } = this.props importNewAccount('Private Key', [ privateKey ]) // JS runtime requires caught rejections but failures are handled by Redux .catch() - .then(() => history.push(DEFAULT_ROUTE)) + .then(({ selectedAddress }) => { + if (selectedAddress) { + history.push(DEFAULT_ROUTE) + } else { + displayWarning('Error importing account.') + setSelectedAddress(firstAddress) + } + }) } From 0f3480a97f2924de899e49a095ef24b9fa5506f1 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 30 May 2018 21:33:40 -0230 Subject: [PATCH 07/24] Fix then-catch + error handling in import-account --- ui/app/components/pages/create-account/import-account/json.js | 3 +-- .../pages/create-account/import-account/private-key.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js index 3dabad68e..23b7f0afd 100644 --- a/ui/app/components/pages/create-account/import-account/json.js +++ b/ui/app/components/pages/create-account/import-account/json.js @@ -106,8 +106,6 @@ class JsonImportSubview extends Component { } importNewJsonAccount([ fileContents, password ]) - // JS runtime requires caught rejections but failures are handled by Redux - .catch() .then(({ selectedAddress }) => { if (selectedAddress) { history.push(DEFAULT_ROUTE) @@ -116,6 +114,7 @@ class JsonImportSubview extends Component { setSelectedAddress(firstAddress) } }) + .catch(err => displayWarning(err)) } } diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js index e71e47647..81cef09f9 100644 --- a/ui/app/components/pages/create-account/import-account/private-key.js +++ b/ui/app/components/pages/create-account/import-account/private-key.js @@ -96,8 +96,6 @@ PrivateKeyImportView.prototype.createNewKeychain = function () { const { importNewAccount, history, displayWarning, setSelectedAddress, firstAddress } = this.props importNewAccount('Private Key', [ privateKey ]) - // JS runtime requires caught rejections but failures are handled by Redux - .catch() .then(({ selectedAddress }) => { if (selectedAddress) { history.push(DEFAULT_ROUTE) @@ -106,4 +104,5 @@ PrivateKeyImportView.prototype.createNewKeychain = function () { setSelectedAddress(firstAddress) } }) + .catch(err => displayWarning(err)) } From 12a7fc40161e3cd6dd8714f67744a905017b99d9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 4 Jun 2018 16:11:19 -0700 Subject: [PATCH 08/24] 4.7.3 --- CHANGELOG.md | 6 +++++- app/manifest.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4c2abd4d..7aaa2abb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ ## Current Master -- Fixes issue where old nicknames were kept around causing errors. +## 4.7.3 Mon Jun 04 2018 + +- Indicate the current selected account on the popup account view +- Reduce height of notice container in onboarding +- Fixes issue where old nicknames were kept around causing errors ## 4.7.2 Sun Jun 03 2018 diff --git a/app/manifest.json b/app/manifest.json index c1f26d2ea..383b71ce3 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "4.7.2", + "version": "4.7.3", "manifest_version": 2, "author": "https://metamask.io", "description": "__MSG_appDescription__", From c12b02b4af75d6d706d0b8cd9aeb07f18aa3037a Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 4 Jun 2018 16:16:18 -0700 Subject: [PATCH 09/24] changelog - add note on new modal --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aaa2abb5..719033da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## 4.7.3 Mon Jun 04 2018 +- Hide token now uses new modal - Indicate the current selected account on the popup account view - Reduce height of notice container in onboarding - Fixes issue where old nicknames were kept around causing errors From 6247e54fcc11d4d8d857a977d00eee8012afb92f Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 11:15:58 -0700 Subject: [PATCH 10/24] add multivault detection to diagnostics reporting --- app/scripts/metamask-controller.js | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c753fc06f..ad1d6d6a7 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -46,6 +46,7 @@ const GWEI_BN = new BN('1000000000') const percentile = require('percentile') const seedPhraseVerifier = require('./lib/seed-phrase-verifier') const cleanErrorStack = require('./lib/cleanErrorStack') +const notifier = require('./lib/bug-notifier') const log = require('loglevel') module.exports = class MetamaskController extends EventEmitter { @@ -64,6 +65,9 @@ module.exports = class MetamaskController extends EventEmitter { const initState = opts.initState || {} this.recordFirstTimeInfo(initState) + // metamask diagnostics reporter + this.notifier = opts.notifier || notifier + // platform-specific api this.platform = opts.platform @@ -487,6 +491,35 @@ module.exports = class MetamaskController extends EventEmitter { await this.keyringController.submitPassword(password) const accounts = await this.keyringController.getAccounts() + // verify keyrings + try { + const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair') + if (nonSimpleKeyrings.length > 1) { + const keyrings = await Promise.all(nonSimpleKeyrings.map(async (keyring, index) => { + return { + index, + type: keyring.type, + accounts: await keyring.getAccounts() + } + })) + // unexpected number of keyrings, report to diagnostics + const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts' + const firstTimeInfo = this.getFirstTimeInfo ? this.getFirstTimeInfo() : {} + await this.notifier.notify(uri, { + accounts: [], + metadata: { + type: 'keyrings', + keyrings, + version, + firstTimeInfo, + }, + }) + } + } catch (err) { + console.error('Keyring validation error:') + console.error(err) + } + await this.preferencesController.syncAddresses(accounts) return this.keyringController.fullUpdate() } From 20bdba3d1710a070d06c2a395f92d948b9396d47 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 11:51:27 -0700 Subject: [PATCH 11/24] diagnostics - rewrite bug-notifier as diagnostics-reporter --- app/scripts/controllers/preferences.js | 19 ++--- app/scripts/lib/bug-notifier.js | 22 ------ app/scripts/lib/diagnostics-reporter.js | 71 +++++++++++++++++++ app/scripts/metamask-controller.js | 38 +++------- .../controllers/metamask-controller-test.js | 5 -- 5 files changed, 84 insertions(+), 71 deletions(-) delete mode 100644 app/scripts/lib/bug-notifier.js create mode 100644 app/scripts/lib/diagnostics-reporter.js diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 2fe009f9a..a5d8cc27b 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -1,9 +1,7 @@ const ObservableStore = require('obs-store') const normalizeAddress = require('eth-sig-util').normalize const extend = require('xtend') -const notifier = require('../lib/bug-notifier') -const log = require('loglevel') -const { version } = require('../../manifest.json') + class PreferencesController { @@ -34,8 +32,7 @@ class PreferencesController { lostIdentities: {}, }, opts.initState) - this.getFirstTimeInfo = opts.getFirstTimeInfo || null - this.notifier = opts.notifier || notifier + this.diagnostics = opts.diagnostics this.store = new ObservableStore(initState) } @@ -128,17 +125,9 @@ class PreferencesController { if (Object.keys(newlyLost).length > 0) { // Notify our servers: - const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts' - const firstTimeInfo = this.getFirstTimeInfo ? this.getFirstTimeInfo() : {} - this.notifier.notify(uri, { - accounts: Object.keys(newlyLost), - metadata: { - version, - firstTimeInfo, - }, - }) - .catch(log.error) + if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost) + // store lost accounts for (let key in newlyLost) { lostIdentities[key] = newlyLost[key] } diff --git a/app/scripts/lib/bug-notifier.js b/app/scripts/lib/bug-notifier.js deleted file mode 100644 index 4d305b894..000000000 --- a/app/scripts/lib/bug-notifier.js +++ /dev/null @@ -1,22 +0,0 @@ -class BugNotifier { - notify (uri, message) { - return postData(uri, message) - } -} - -function postData(uri, data) { - return fetch(uri, { - body: JSON.stringify(data), // must match 'Content-Type' header - credentials: 'same-origin', // include, same-origin, *omit - headers: { - 'content-type': 'application/json', - }, - method: 'POST', // *GET, POST, PUT, DELETE, etc. - mode: 'cors', // no-cors, cors, *same-origin - }) -} - -const notifier = new BugNotifier() - -module.exports = notifier - diff --git a/app/scripts/lib/diagnostics-reporter.js b/app/scripts/lib/diagnostics-reporter.js new file mode 100644 index 000000000..6c77923bd --- /dev/null +++ b/app/scripts/lib/diagnostics-reporter.js @@ -0,0 +1,71 @@ +class DiagnosticsReporter { + + constructor ({ firstTimeInfo, version }) { + this.firstTimeInfo = firstTimeInfo + this.version = version + } + + async reportOrphans(orphans) { + try { + await this.submit({ + accounts: Object.keys(orphans), + metadata: { + type: 'orphans' + }, + }) + } catch (err) { + console.error('DiagnosticsReporter - "reportOrphans" encountered an error:') + console.error(err) + } + } + + async reportMultipleKeyrings(rawKeyrings) { + try { + const keyrings = await Promise.all(rawKeyrings.map(async (keyring, index) => { + return { + index, + type: keyring.type, + accounts: await keyring.getAccounts(), + } + })) + await this.submit({ + accounts: [], + metadata: { + type: 'keyrings', + keyrings, + }, + }) + } catch (err) { + console.error('DiagnosticsReporter - "reportMultipleKeyrings" encountered an error:') + console.error(err) + } + } + + async submit (message) { + try { + // add metadata + message.metadata.version = this.version + message.metadata.firstTimeInfo = this.firstTimeInfo + return await postData(message) + } catch (err) { + console.error('DiagnosticsReporter - "submit" encountered an error:') + console.error(err) + } + } + +} + +function postData(data) { + const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts' + return fetch(uri, { + body: JSON.stringify(data), // must match 'Content-Type' header + credentials: 'same-origin', // include, same-origin, *omit + headers: { + 'content-type': 'application/json', + }, + method: 'POST', // *GET, POST, PUT, DELETE, etc. + mode: 'cors', // no-cors, cors, *same-origin + }) +} + +module.exports = DiagnosticsReporter diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index ad1d6d6a7..4b0b00306 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -46,7 +46,7 @@ const GWEI_BN = new BN('1000000000') const percentile = require('percentile') const seedPhraseVerifier = require('./lib/seed-phrase-verifier') const cleanErrorStack = require('./lib/cleanErrorStack') -const notifier = require('./lib/bug-notifier') +const DiagnosticsReporter = require('./lib/diagnostics-reporter') const log = require('loglevel') module.exports = class MetamaskController extends EventEmitter { @@ -66,7 +66,10 @@ module.exports = class MetamaskController extends EventEmitter { this.recordFirstTimeInfo(initState) // metamask diagnostics reporter - this.notifier = opts.notifier || notifier + this.diagnostics = opts.diagnostics || new DiagnosticsReporter({ + firstTimeInfo: initState.firstTimeInfo, + version, + }) // platform-specific api this.platform = opts.platform @@ -89,7 +92,7 @@ module.exports = class MetamaskController extends EventEmitter { this.preferencesController = new PreferencesController({ initState: initState.PreferencesController, initLangCode: opts.initLangCode, - getFirstTimeInfo: () => initState.firstTimeInfo, + diagnostics: this.diagnostics, }) // currency controller @@ -492,32 +495,9 @@ module.exports = class MetamaskController extends EventEmitter { const accounts = await this.keyringController.getAccounts() // verify keyrings - try { - const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair') - if (nonSimpleKeyrings.length > 1) { - const keyrings = await Promise.all(nonSimpleKeyrings.map(async (keyring, index) => { - return { - index, - type: keyring.type, - accounts: await keyring.getAccounts() - } - })) - // unexpected number of keyrings, report to diagnostics - const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts' - const firstTimeInfo = this.getFirstTimeInfo ? this.getFirstTimeInfo() : {} - await this.notifier.notify(uri, { - accounts: [], - metadata: { - type: 'keyrings', - keyrings, - version, - firstTimeInfo, - }, - }) - } - } catch (err) { - console.error('Keyring validation error:') - console.error(err) + const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair') + if (nonSimpleKeyrings.length > 1) { + if (this.diagnostics) await this.reportMultipleKeyrings(nonSimpleKeyrings) } await this.preferencesController.syncAddresses(accounts) diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js index 7ec98766a..266c3f258 100644 --- a/test/unit/app/controllers/metamask-controller-test.js +++ b/test/unit/app/controllers/metamask-controller-test.js @@ -72,11 +72,6 @@ describe('MetaMaskController', function () { it('removes any identities that do not correspond to known accounts.', async function () { const fakeAddress = '0xbad0' metamaskController.preferencesController.addAddresses([fakeAddress]) - metamaskController.preferencesController.notifier = { - notify: async () => { - return true - }, - } await metamaskController.submitPassword(password) const identities = Object.keys(metamaskController.preferencesController.store.getState().identities) From ece5cfc7858cb3e8077fad2c11f165a437e60413 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 11:53:21 -0700 Subject: [PATCH 12/24] lint - fix diagnostics reporter --- app/scripts/lib/diagnostics-reporter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/diagnostics-reporter.js b/app/scripts/lib/diagnostics-reporter.js index 6c77923bd..534a0fa8a 100644 --- a/app/scripts/lib/diagnostics-reporter.js +++ b/app/scripts/lib/diagnostics-reporter.js @@ -10,7 +10,7 @@ class DiagnosticsReporter { await this.submit({ accounts: Object.keys(orphans), metadata: { - type: 'orphans' + type: 'orphans', }, }) } catch (err) { From 762695bfd943a8910e4f297a66d9b8cd44eb8579 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 5 Jun 2018 12:04:03 -0700 Subject: [PATCH 13/24] Ensure selectedAddress exists when render wallet --- ui/app/components/wallet-view.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index 3b29dacac..538175cdf 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -111,6 +111,10 @@ WalletView.prototype.render = function () { const checksummedAddress = checksumAddress(selectedAddress) + if (!selectedAddress) { + throw new Error('selectedAddress should not be ' + String(selectedAddress)) + } + const keyring = keyrings.find((kr) => { return kr.accounts.includes(selectedAddress) || kr.accounts.includes(selectedIdentity.address) From 36a0574f566ba2b9aac53396721c0075dd1107e5 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 12:20:24 -0700 Subject: [PATCH 14/24] diagnostics - minor fixes --- app/scripts/lib/diagnostics-reporter.js | 6 +++--- app/scripts/metamask-controller.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/diagnostics-reporter.js b/app/scripts/lib/diagnostics-reporter.js index 534a0fa8a..aa4ca6e26 100644 --- a/app/scripts/lib/diagnostics-reporter.js +++ b/app/scripts/lib/diagnostics-reporter.js @@ -7,7 +7,7 @@ class DiagnosticsReporter { async reportOrphans(orphans) { try { - await this.submit({ + return await this.submit({ accounts: Object.keys(orphans), metadata: { type: 'orphans', @@ -28,7 +28,7 @@ class DiagnosticsReporter { accounts: await keyring.getAccounts(), } })) - await this.submit({ + return await this.submit({ accounts: [], metadata: { type: 'keyrings', @@ -49,7 +49,7 @@ class DiagnosticsReporter { return await postData(message) } catch (err) { console.error('DiagnosticsReporter - "submit" encountered an error:') - console.error(err) + throw err } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 4b0b00306..18a2e7c48 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -496,8 +496,8 @@ module.exports = class MetamaskController extends EventEmitter { // verify keyrings const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair') - if (nonSimpleKeyrings.length > 1) { - if (this.diagnostics) await this.reportMultipleKeyrings(nonSimpleKeyrings) + if (nonSimpleKeyrings.length > 1 && this.diagnostics) { + await this.reportMultipleKeyrings(nonSimpleKeyrings) } await this.preferencesController.syncAddresses(accounts) From 665ac860e5d29c573e07161632b0043ba18ef1d4 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 5 Jun 2018 12:23:30 -0700 Subject: [PATCH 15/24] Remove selectedIdentity prop from wallet view The selectedIdentity property is computed based on the selectedAddress which means that using both the selectedAddress and the selectedIdentity is redundant. In the case of the Array#find call on the set of keyrings, we wouldn't have a situation where one is included and the other isn't. This changeset removes the selectedIdentity from the wallet view because it isn't needed. --- ui/app/components/wallet-view.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index 538175cdf..da142fad8 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -36,7 +36,6 @@ function mapStateToProps (state) { tokens: state.metamask.tokens, keyrings: state.metamask.keyrings, selectedAddress: selectors.getSelectedAddress(state), - selectedIdentity: selectors.getSelectedIdentity(state), selectedAccount: selectors.getSelectedAccount(state), selectedTokenAddress: state.metamask.selectedTokenAddress, } @@ -99,12 +98,12 @@ WalletView.prototype.render = function () { const { responsiveDisplayClassname, selectedAddress, - selectedIdentity, keyrings, showAccountDetailModal, sidebarOpen, hideSidebar, history, + identities, } = this.props // temporary logs + fake extra wallets // console.log('walletview, selectedAccount:', selectedAccount) @@ -116,8 +115,7 @@ WalletView.prototype.render = function () { } const keyring = keyrings.find((kr) => { - return kr.accounts.includes(selectedAddress) || - kr.accounts.includes(selectedIdentity.address) + return kr.accounts.includes(selectedAddress) }) const type = keyring.type @@ -149,7 +147,7 @@ WalletView.prototype.render = function () { h('span.account-name', { style: {}, }, [ - selectedIdentity.name, + identities[selectedAddress].name, ]), h('button.btn-clear.wallet-view__details-button.allcaps', this.context.t('details')), From 60e61e6834a4502fcd9d19e1417725c301c79a13 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 12:36:15 -0700 Subject: [PATCH 16/24] diagnostics - fix reportMultipleKeyrings call --- app/scripts/metamask-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 18a2e7c48..1bb0af5ee 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -497,7 +497,7 @@ module.exports = class MetamaskController extends EventEmitter { // verify keyrings const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair') if (nonSimpleKeyrings.length > 1 && this.diagnostics) { - await this.reportMultipleKeyrings(nonSimpleKeyrings) + await this.diagnostics.reportMultipleKeyrings(nonSimpleKeyrings) } await this.preferencesController.syncAddresses(accounts) From f73feccf5af76312f9b0a65c42b2bd0877dfba7d Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 13:22:48 -0700 Subject: [PATCH 17/24] 4.7.4 --- CHANGELOG.md | 5 +++++ app/manifest.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 719033da0..19287f046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Current Master +## 4.7.4 Tue Jun 05 2018 + +- Add diagnostic reporting for users with multiple HD keyrings +- Throw explicit error when selected account is unset + ## 4.7.3 Mon Jun 04 2018 - Hide token now uses new modal diff --git a/app/manifest.json b/app/manifest.json index 383b71ce3..e3a7fd963 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "4.7.3", + "version": "4.7.4", "manifest_version": 2, "author": "https://metamask.io", "description": "__MSG_appDescription__", From da938ded5c055845885d1e95ba1c74df3f92e71d Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 5 Jun 2018 13:31:34 -0700 Subject: [PATCH 18/24] test - metamask-controller - disable diagnostics --- test/unit/app/controllers/metamask-controller-test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js index 266c3f258..0dda4609b 100644 --- a/test/unit/app/controllers/metamask-controller-test.js +++ b/test/unit/app/controllers/metamask-controller-test.js @@ -53,6 +53,9 @@ describe('MetaMaskController', function () { }, initState: clone(firstTimeState), }) + // disable diagnostics + metamaskController.diagnostics = null + // add sinon method spies sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore') }) From 3cc85c219ef565267093de80e709e53d57b38d4e Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 5 Jun 2018 14:06:56 -0700 Subject: [PATCH 19/24] Add account type assertion to PreferencesController#setAccountLabel --- app/scripts/controllers/preferences.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index a5d8cc27b..8411e3a28 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -247,6 +247,7 @@ class PreferencesController { * @return {Promise} */ setAccountLabel (account, label) { + if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account)) const address = normalizeAddress(account) const {identities} = this.store.getState() identities[address] = identities[address] || {} From 8b449b325d96ad28c346d87729c1ebd230b8d38e Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Mon, 4 Jun 2018 14:11:50 -0700 Subject: [PATCH 20/24] Remove unused identities reducer from UI --- ui/app/reducers.js | 7 ------- ui/app/reducers/identities.js | 15 --------------- 2 files changed, 22 deletions(-) delete mode 100644 ui/app/reducers/identities.js diff --git a/ui/app/reducers.js b/ui/app/reducers.js index f155b2bf3..e3a3077d9 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -4,7 +4,6 @@ const copyToClipboard = require('copy-to-clipboard') // // Sub-Reducers take in the complete state and return their sub-state // -const reduceIdentities = require('./reducers/identities') const reduceMetamask = require('./reducers/metamask') const reduceApp = require('./reducers/app') const reduceLocale = require('./reducers/locale') @@ -21,12 +20,6 @@ function rootReducer (state, action) { return action.value } - // - // Identities - // - - state.identities = reduceIdentities(state, action) - // // MetaMask // diff --git a/ui/app/reducers/identities.js b/ui/app/reducers/identities.js deleted file mode 100644 index 341a404e7..000000000 --- a/ui/app/reducers/identities.js +++ /dev/null @@ -1,15 +0,0 @@ -const extend = require('xtend') - -module.exports = reduceIdentities - -function reduceIdentities (state, action) { - // clone + defaults - var idState = extend({ - - }, state.identities) - - switch (action.type) { - default: - return idState - } -} From d9d09f953b9500b783a164b4aba85efcbd7ddbe2 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Mon, 4 Jun 2018 15:03:03 -0700 Subject: [PATCH 21/24] Render the accounts in keyring order --- ui/app/components/account-menu/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index 7638995ea..f34631ca8 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -135,11 +135,12 @@ AccountMenu.prototype.renderAccounts = function () { showAccountDetail, } = this.props - return Object.keys(identities).map((key, index) => { - const identity = identities[key] + const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), []) + return accountOrder.map((address) => { + const identity = identities[address] const isSelected = identity.address === selectedAddress - const balanceValue = accounts[key] ? accounts[key].balance : '' + const balanceValue = accounts[address] ? accounts[address].balance : '' const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' const simpleAddress = identity.address.substring(2).toLowerCase() From df6d03cefd1917c377f658fa8423654957ca6440 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 5 Jun 2018 11:39:51 -0700 Subject: [PATCH 22/24] Fix account list order for the old UI --- old-ui/app/components/account-dropdowns.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/old-ui/app/components/account-dropdowns.js b/old-ui/app/components/account-dropdowns.js index 53468a1a1..262de6601 100644 --- a/old-ui/app/components/account-dropdowns.js +++ b/old-ui/app/components/account-dropdowns.js @@ -23,9 +23,10 @@ class AccountDropdowns extends Component { renderAccounts () { const { identities, selected, keyrings } = this.props + const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), []) - return Object.keys(identities).map((key, index) => { - const identity = identities[key] + return accountOrder.map((address, index) => { + const identity = identities[address] const isSelected = identity.address === selected const simpleAddress = identity.address.substring(2).toLowerCase() From 6ee96854904d149c00b629f5025fda1bf5c60070 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 5 Jun 2018 19:48:42 -0700 Subject: [PATCH 23/24] Fix mock addresses used for integration tests --- development/states/add-token.json | 6 +++--- development/states/confirm-sig-requests.json | 6 +++--- development/states/currency-localization.json | 6 +++--- development/states/send-edit.json | 6 +++--- development/states/send-new-ui.json | 6 +++--- development/states/tx-list-items.json | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/development/states/add-token.json b/development/states/add-token.json index 9c0f16372..84ad5dd4c 100644 --- a/development/states/add-token.json +++ b/development/states/add-token.json @@ -75,9 +75,9 @@ { "type": "HD Key Tree", "accounts": [ - "fdea65c8e26263f6d9a1b5de9555d2931a33b825", - "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "2f8d4a878cfa04a6e60d46362f5644deab66572d" + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" ] }, { diff --git a/development/states/confirm-sig-requests.json b/development/states/confirm-sig-requests.json index b51003d11..3c9caafb0 100644 --- a/development/states/confirm-sig-requests.json +++ b/development/states/confirm-sig-requests.json @@ -115,9 +115,9 @@ { "type": "HD Key Tree", "accounts": [ - "fdea65c8e26263f6d9a1b5de9555d2931a33b825", - "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "2f8d4a878cfa04a6e60d46362f5644deab66572d" + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" ] }, { diff --git a/development/states/currency-localization.json b/development/states/currency-localization.json index 302e24c11..8c8b18a91 100644 --- a/development/states/currency-localization.json +++ b/development/states/currency-localization.json @@ -76,9 +76,9 @@ { "type": "HD Key Tree", "accounts": [ - "fdea65c8e26263f6d9a1b5de9555d2931a33b825", - "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "2f8d4a878cfa04a6e60d46362f5644deab66572d" + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" ] }, { diff --git a/development/states/send-edit.json b/development/states/send-edit.json index ae3098ecb..0d4c27116 100644 --- a/development/states/send-edit.json +++ b/development/states/send-edit.json @@ -94,9 +94,9 @@ { "type": "HD Key Tree", "accounts": [ - "fdea65c8e26263f6d9a1b5de9555d2931a33b825", - "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "2f8d4a878cfa04a6e60d46362f5644deab66572d" + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" ] }, { diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json index 1297a9139..787f8023e 100644 --- a/development/states/send-new-ui.json +++ b/development/states/send-new-ui.json @@ -76,9 +76,9 @@ { "type": "HD Key Tree", "accounts": [ - "fdea65c8e26263f6d9a1b5de9555d2931a33b825", - "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "2f8d4a878cfa04a6e60d46362f5644deab66572d" + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" ] }, { diff --git a/development/states/tx-list-items.json b/development/states/tx-list-items.json index d567e3fed..ee787aaee 100644 --- a/development/states/tx-list-items.json +++ b/development/states/tx-list-items.json @@ -83,9 +83,9 @@ { "type": "HD Key Tree", "accounts": [ - "fdea65c8e26263f6d9a1b5de9555d2931a33b825", - "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", - "2f8d4a878cfa04a6e60d46362f5644deab66572d" + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" ] }, { From ccd4884db112a5440e7f482f644e6729e638dc49 Mon Sep 17 00:00:00 2001 From: 03-26 <37808790+03-26@users.noreply.github.com> Date: Thu, 7 Jun 2018 03:38:57 +0900 Subject: [PATCH 24/24] i18n - ja improvements --- app/_locales/ja/messages.json | 43 ++++++++++++++++++- .../pages/add-token/add-token.component.js | 6 +-- .../components/pages/create-account/index.js | 15 +++++-- ui/app/components/pages/settings/index.js | 9 +++- .../unlock-page/unlock-page.component.js | 2 +- ui/app/send-v2.js | 4 +- ui/app/welcome-screen.js | 14 +++--- 7 files changed, 75 insertions(+), 18 deletions(-) diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 3a664ec00..75deeaddf 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -62,6 +62,9 @@ "message": " $1以上 $2以下にして下さい。", "description": "helper for inputting hex as decimal input" }, + "blockiesIdenticon": { + "message": "Blockies Identicon を使用" + }, "borrowDharma": { "message": "Dharmaで借りる(ベータ版)" }, @@ -95,6 +98,9 @@ "confirmTransaction": { "message": "トランザクションの確認" }, + "continue": { + "message": "続行" + }, "continueToCoinbase": { "message": "Coinbaseを開く" }, @@ -359,6 +365,9 @@ "likeToAddTokens": { "message": "トークンを追加しますか?" }, + "links": { + "message": "リンク" + }, "limit": { "message": "リミット" }, @@ -371,12 +380,18 @@ "localhost": { "message": "Localhost 8545" }, + "login": { + "message": "ログイン" + }, "logout": { "message": "ログアウト" }, "loose": { "message": "外部秘密鍵" }, + "max": { + "message": "最大" + }, "mainnet": { "message": "Ethereumメインネットワーク" }, @@ -417,7 +432,7 @@ "message": "新規コントラクト" }, "newPassword": { - "message": "新規パスワード(最低8文字)" + "message": "新規パスワード(最低8文字)" }, "newRecipient": { "message": "新規受取人" @@ -453,6 +468,9 @@ "message": "または", "description": "choice between creating or importing a new account" }, + "password": { + "message": "パスワード" + }, "passwordMismatch": { "message": "パスワードが一致しません。", "description": "in password creation process, the two new password fields did not match" @@ -474,6 +492,9 @@ "popularTokens": { "message": "人気のトークン" }, + "privacyMsg": { + "message": "プライバシーポリシー" + }, "privateKey": { "message": "秘密鍵", "description": "select this type of file to use to import an account" @@ -546,6 +567,12 @@ "message": "ファイルとして保存", "description": "Account export process" }, + "search": { + "message": "検索" + }, + "searchResults": { + "message": "検索結果" + }, "selectService": { "message": "サービスを選択" }, @@ -575,7 +602,7 @@ }, "info": { "message": "情報" - }, + }, "shapeshiftBuy": { "message": "Shapeshiftで交換" }, @@ -609,6 +636,9 @@ "takesTooLong": { "message": "送信に時間がかかりますか?" }, + "terms": { + "message": "利用規約" + }, "testFaucet": { "message": "Faucetをテスト" }, @@ -619,6 +649,9 @@ "message": "ShapeShiftで $1をETHにする", "description": "system will fill in deposit type in start of message" }, + "token": { + "message": "トークン" + }, "tokenAddress": { "message": "トークンアドレス" }, @@ -690,6 +723,12 @@ "warning": { "message": "警告" }, + "welcomeBack": { + "message": "おかえりなさい!" + }, + "welcomeBeta": { + "message": "MetaMask ベータ版へようこそ!" + }, "whatsThis": { "message": "この機能について" }, diff --git a/ui/app/components/pages/add-token/add-token.component.js b/ui/app/components/pages/add-token/add-token.component.js index 1f4b37b53..bcb93d401 100644 --- a/ui/app/components/pages/add-token/add-token.component.js +++ b/ui/app/components/pages/add-token/add-token.component.js @@ -231,7 +231,7 @@ class AddToken extends Component {
this.handleCustomAddressChange(e.target.value)} @@ -241,7 +241,7 @@ class AddToken extends Component { /> this.handleCustomSymbolChange(e.target.value)} @@ -252,7 +252,7 @@ class AddToken extends Component { /> this.handleCustomDecimalsChange(e.target.value)} diff --git a/ui/app/components/pages/create-account/index.js b/ui/app/components/pages/create-account/index.js index 475261253..6e3b93742 100644 --- a/ui/app/components/pages/create-account/index.js +++ b/ui/app/components/pages/create-account/index.js @@ -22,7 +22,9 @@ class CreateAccountPage extends Component { }), }), onClick: () => history.push(NEW_ACCOUNT_ROUTE), - }, 'Create'), + }, [ + this.context.t('create'), + ]), h('div.new-account__tabs__tab', { className: classnames('new-account__tabs__tab', { @@ -31,14 +33,16 @@ class CreateAccountPage extends Component { }), }), onClick: () => history.push(IMPORT_ACCOUNT_ROUTE), - }, 'Import'), + }, [ + this.context.t('import'), + ]), ]) } render () { return h('div.new-account', {}, [ h('div.new-account__header', [ - h('div.new-account__title', 'New Account'), + h('div.new-account__title', this.context.t('newAccount') ), this.renderTabs(), ]), h('div.new-account__form', [ @@ -62,6 +66,11 @@ class CreateAccountPage extends Component { CreateAccountPage.propTypes = { location: PropTypes.object, history: PropTypes.object, + t: PropTypes.func, +} + +CreateAccountPage.contextTypes = { + t: PropTypes.func, } const mapStateToProps = state => ({ diff --git a/ui/app/components/pages/settings/index.js b/ui/app/components/pages/settings/index.js index 384ae4b41..aee17e0e8 100644 --- a/ui/app/components/pages/settings/index.js +++ b/ui/app/components/pages/settings/index.js @@ -14,8 +14,8 @@ class Config extends Component { return h('div.settings__tabs', [ h(TabBar, { tabs: [ - { content: 'Settings', key: SETTINGS_ROUTE }, - { content: 'Info', key: INFO_ROUTE }, + { content: this.context.t('settings'), key: SETTINGS_ROUTE }, + { content: this.context.t('info'), key: INFO_ROUTE }, ], isActive: key => matchPath(location.pathname, { path: key, exact: true }), onSelect: key => history.push(key), @@ -54,6 +54,11 @@ class Config extends Component { Config.propTypes = { location: PropTypes.object, history: PropTypes.object, + t: PropTypes.func, +} + +Config.contextTypes = { + t: PropTypes.func, } module.exports = Config diff --git a/ui/app/components/pages/unlock-page/unlock-page.component.js b/ui/app/components/pages/unlock-page/unlock-page.component.js index 8bc3897da..a1d3f9181 100644 --- a/ui/app/components/pages/unlock-page/unlock-page.component.js +++ b/ui/app/components/pages/unlock-page/unlock-page.component.js @@ -120,7 +120,7 @@ class UnlockPage extends Component { > this.handleInputChange(event)} diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 4fbe8ff11..612f256df 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -224,7 +224,7 @@ SendTransactionScreen.prototype.renderFromRow = function () { return h('div.send-v2__form-row', [ - h('div.send-v2__form-label', 'From:'), + h('div.send-v2__form-label', this.context.t('from')), h('div.send-v2__form-field', [ h(FromDropdown, { @@ -396,7 +396,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () { return h('div.send-v2__form-row', [ h('div.send-v2__form-label', [ - 'Amount:', + this.context.t('amount'), this.renderErrorMessage('amount'), !errors.amount && gasTotal && h('div.send-v2__amount-max', { onClick: (event) => { diff --git a/ui/app/welcome-screen.js b/ui/app/welcome-screen.js index 2fa244d9f..63512cd50 100644 --- a/ui/app/welcome-screen.js +++ b/ui/app/welcome-screen.js @@ -14,6 +14,11 @@ class WelcomeScreen extends Component { closeWelcomeScreen: PropTypes.func.isRequired, welcomeScreenSeen: PropTypes.bool, history: PropTypes.object, + t: PropTypes.func, + } + + static contextTypes = { + t: PropTypes.func, } constructor (props) { @@ -45,16 +50,15 @@ class WelcomeScreen extends Component { height: '225', }), - h('div.welcome-screen__info__header', 'Welcome to MetaMask Beta'), + h('div.welcome-screen__info__header', this.context.t('welcomeBeta')), - h('div.welcome-screen__info__copy', 'MetaMask is a secure identity vault for Ethereum.'), + h('div.welcome-screen__info__copy', this.context.t('metamaskDescription')), - h('div.welcome-screen__info__copy', `It allows you to hold ether & tokens, - and serves as your bridge to decentralized applications.`), + h('div.welcome-screen__info__copy', this.context.t('holdEther')), h('button.welcome-screen__button', { onClick: this.initiateAccountCreation, - }, 'Continue'), + }, this.context.t('continue')), ]),