aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/keyring-controller.js55
-rw-r--r--app/scripts/keyrings/simple.js9
-rw-r--r--app/scripts/metamask-controller.js39
-rw-r--r--development/states/accounts-loose.json126
-rw-r--r--gulpfile.js30
-rw-r--r--test/integration/lib/idStore-migrator-test.js21
-rw-r--r--test/integration/mocks/badVault2.json1
-rw-r--r--test/unit/idStore-migration-test.js11
-rw-r--r--ui/app/accounts/account-list-item.js20
-rw-r--r--ui/app/accounts/index.js14
-rw-r--r--ui/app/actions.js74
-rw-r--r--ui/app/components/eth-balance.js4
-rw-r--r--ui/app/css/lib.css17
15 files changed, 324 insertions, 100 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 239203553..b10ff010f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## Current Master
+- Remove certain non-essential permissions from certain builds.
- Add a check for when a tx is included in a block.
## 2.14.1 2016-12-20
diff --git a/app/manifest.json b/app/manifest.json
index 61775ed93..95dcfc31a 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -56,9 +56,7 @@
],
"permissions": [
"storage",
- "tabs",
"clipboardWrite",
- "clipboardRead",
"http://localhost:8545/"
],
"web_accessible_resources": [
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index acec8feb1..1a26a887f 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -86,22 +86,28 @@ module.exports = class KeyringController extends EventEmitter {
const address = configManager.getSelectedAccount()
const wallet = configManager.getWallet() // old style vault
const vault = configManager.getVault() // new style vault
-
- return {
- seedWords: this.configManager.getSeedWords(),
- isInitialized: (!!wallet || !!vault),
- isUnlocked: Boolean(this.password),
- isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), // AUDIT this.configManager.getConfirmedDisclaimer(),
- unconfMsgs: messageManager.unconfirmedMsgs(),
- messages: messageManager.getMsgList(),
- selectedAccount: address,
- shapeShiftTxList: this.configManager.getShapeShiftTxList(),
- currentFiat: this.configManager.getCurrentFiat(),
- conversionRate: this.configManager.getConversionRate(),
- conversionDate: this.configManager.getConversionDate(),
- keyringTypes: this.keyringTypes.map(krt => krt.type),
- identities: this.identities,
- }
+ const keyrings = this.keyrings
+
+ return Promise.all(keyrings.map(this.displayForKeyring))
+ .then((displayKeyrings) => {
+ return {
+ seedWords: this.configManager.getSeedWords(),
+ isInitialized: (!!wallet || !!vault),
+ isUnlocked: Boolean(this.password),
+ isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
+ transactions: this.configManager.getTxList(),
+ unconfMsgs: messageManager.unconfirmedMsgs(),
+ messages: messageManager.getMsgList(),
+ selectedAccount: address,
+ shapeShiftTxList: this.configManager.getShapeShiftTxList(),
+ currentFiat: this.configManager.getCurrentFiat(),
+ conversionRate: this.configManager.getConversionRate(),
+ conversionDate: this.configManager.getConversionDate(),
+ keyringTypes: this.keyringTypes.map(krt => krt.type),
+ identities: this.identities,
+ keyrings: displayKeyrings,
+ }
+ })
}
// Create New Vault And Keychain
@@ -555,6 +561,7 @@ module.exports = class KeyringController extends EventEmitter {
// On success, returns the resulting @Keyring instance.
restoreKeyring (serialized) {
const { type, data } = serialized
+
const Keyring = this.getKeyringClassForType(type)
const keyring = new Keyring()
return keyring.deserialize(data)
@@ -627,6 +634,22 @@ module.exports = class KeyringController extends EventEmitter {
})
}
+ // Display For Keyring
+ // @Keyring keyring
+ //
+ // returns Promise( @Object { type:String, accounts:Array } )
+ //
+ // Is used for adding the current keyrings to the state object.
+ displayForKeyring (keyring) {
+ return keyring.getAccounts()
+ .then((accounts) => {
+ return {
+ type: keyring.type,
+ accounts: accounts,
+ }
+ })
+ }
+
// Add Gas Buffer
// @string gas (as hexadecimal value)
//
diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js
index 8f339cf80..9717f1c45 100644
--- a/app/scripts/keyrings/simple.js
+++ b/app/scripts/keyrings/simple.js
@@ -19,10 +19,11 @@ class SimpleKeyring extends EventEmitter {
return Promise.resolve(this.wallets.map(w => w.getPrivateKey().toString('hex')))
}
- deserialize (wallets = []) {
- this.wallets = wallets.map((w) => {
- var b = new Buffer(w, 'hex')
- const wallet = Wallet.fromPrivateKey(b)
+ deserialize (privateKeys = []) {
+ this.wallets = privateKeys.map((privateKey) => {
+ const stripped = ethUtil.stripHexPrefix(privateKey)
+ const buffer = new Buffer(stripped, 'hex')
+ const wallet = Wallet.fromPrivateKey(buffer)
return wallet
})
return Promise.resolve()
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 32e9faab2..4caad46e1 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -60,19 +60,24 @@ module.exports = class MetamaskController {
this.idStoreMigrator = new IdStoreMigrator({
configManager: this.configManager,
})
+
+ this.ethStore.on('update', this.sendUpdate.bind(this))
}
getState () {
- return extend(
- this.state,
- this.ethStore.getState(),
- this.configManager.getConfig(),
- this.keyringController.getState(),
- this.txManager.getState(),
- this.noticeController.getState(), {
- lostAccounts: this.configManager.getLostAccounts(),
- }
- )
+ return this.keyringController.getState()
+ .then((keyringControllerState) => {
+ return extend(
+ this.state,
+ this.ethStore.getState(),
+ this.configManager.getConfig(),
+ this.txManager.getState(),
+ keyringControllerState,
+ this.noticeController.getState(), {
+ lostAccounts: this.configManager.getLostAccounts(),
+ }
+ )
+ })
}
getApi () {
@@ -81,7 +86,7 @@ module.exports = class MetamaskController {
const noticeController = this.noticeController
return {
- getState: (cb) => { cb(null, this.getState()) },
+ getState: nodeify(this.getState.bind(this)),
setRpcTarget: this.setRpcTarget.bind(this),
setProviderType: this.setProviderType.bind(this),
useEtherscanProvider: this.useEtherscanProvider.bind(this),
@@ -101,7 +106,7 @@ module.exports = class MetamaskController {
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
submitPassword: (password, cb) => {
this.migrateOldVaultIfAny(password)
- .then(keyringController.submitPassword.bind(keyringController))
+ .then(keyringController.submitPassword.bind(keyringController, password))
.then((newState) => { cb(null, newState) })
.catch((reason) => { cb(reason) })
},
@@ -158,8 +163,12 @@ module.exports = class MetamaskController {
}
sendUpdate () {
- this.listeners.forEach((remote) => {
- remote.sendUpdate(this.getState())
+ this.getState()
+ .then((state) => {
+
+ this.listeners.forEach((remote) => {
+ remote.sendUpdate(state)
+ })
})
}
@@ -479,7 +488,7 @@ module.exports = class MetamaskController {
return this.idStoreMigrator.migratedVaultForPassword(password)
.then(this.restoreOldVaultAccounts.bind(this))
.then(this.restoreOldLostAccounts.bind(this))
- .then(keyringController.persistAllKeyrings.bind(keyringController))
+ .then(keyringController.persistAllKeyrings.bind(keyringController, password))
.then(() => password)
}
diff --git a/development/states/accounts-loose.json b/development/states/accounts-loose.json
new file mode 100644
index 000000000..fd0c93c9c
--- /dev/null
+++ b/development/states/accounts-loose.json
@@ -0,0 +1,126 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
+ "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "name": "Account 1"
+ },
+ "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
+ "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
+ "name": "Account 2"
+ },
+ "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
+ "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241",
+ "name": "Account 3"
+ },
+ "0xe15d894becb0354c501ae69429b05143679f39e0": {
+ "address": "0xe15d894becb0354c501ae69429b05143679f39e0",
+ "name": "Account 4"
+ },
+ "0x87658c15aefe7448008a28513a11b6b130ef4cd0": {
+ "address": "0x87658c15aefe7448008a28513a11b6b130ef4cd0",
+ "name": "Account 5"
+ },
+ "0xaa25854c0379e53c957ac9382e720c577fa31fd5": {
+ "address": "0xaa25854c0379e53c957ac9382e720c577fa31fd5",
+ "name": "Account 6"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 0,
+ "conversionDate": "N/A",
+ "noActiveNotices": true,
+ "network": "3",
+ "accounts": {
+ "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
+ "code": "0x",
+ "balance": "0x11f646fe14c9c000",
+ "nonce": "0x3",
+ "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9"
+ },
+ "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4"
+ },
+ "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241"
+ },
+ "0xe15d894becb0354c501ae69429b05143679f39e0": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xe15d894becb0354c501ae69429b05143679f39e0"
+ },
+ "0x87658c15aefe7448008a28513a11b6b130ef4cd0": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x87658c15aefe7448008a28513a11b6b130ef4cd0"
+ },
+ "0xaa25854c0379e53c957ac9382e720c577fa31fd5": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xaa25854c0379e53c957ac9382e720c577fa31fd5"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAccount": "0x87658c15aefe7448008a28513a11b6b130ef4cd0",
+ "isDisclaimerConfirmed": true,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "ac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "d7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
+ "1acfb961c5a8268eac8e09d6241a26cbeff42241"
+ ]
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": [
+ "e15d894becb0354c501ae69429b05143679f39e0",
+ "87658c15aefe7448008a28513a11b6b130ef4cd0",
+ "aa25854c0379e53c957ac9382e720c577fa31fd5"
+ ]
+ }
+ ],
+ "lostAccounts": []
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accounts"
+ },
+ "accountDetail": {
+ "subview": "transactions",
+ "accountExport": "none",
+ "privateKey": ""
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null,
+ "scrollToBottom": false,
+ "forgottenPassword": false
+ },
+ "identities": {}
+} \ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index 26ad0c1f8..ca69b36a8 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -46,6 +46,7 @@ gulp.task('copy:locales', copyTask({
'./dist/firefox/_locales',
'./dist/chrome/_locales',
'./dist/edge/_locales',
+ './dist/opera/_locales',
]
}))
gulp.task('copy:images', copyTask({
@@ -54,6 +55,7 @@ gulp.task('copy:images', copyTask({
'./dist/firefox/images',
'./dist/chrome/images',
'./dist/edge/images',
+ './dist/opera/images',
],
}))
gulp.task('copy:fonts', copyTask({
@@ -62,6 +64,7 @@ gulp.task('copy:fonts', copyTask({
'./dist/firefox/fonts',
'./dist/chrome/fonts',
'./dist/edge/fonts',
+ './dist/opera/fonts',
],
}))
gulp.task('copy:reload', copyTask({
@@ -70,6 +73,7 @@ gulp.task('copy:reload', copyTask({
'./dist/firefox/scripts',
'./dist/chrome/scripts',
'./dist/edge/scripts',
+ './dist/opera/scripts',
],
pattern: '/chromereload.js',
}))
@@ -79,6 +83,7 @@ gulp.task('copy:root', copyTask({
'./dist/firefox',
'./dist/chrome',
'./dist/edge',
+ './dist/opera',
],
pattern: '/*',
}))
@@ -92,6 +97,21 @@ gulp.task('manifest:chrome', function() {
.pipe(gulp.dest('./dist/chrome', { overwrite: true }))
})
+gulp.task('manifest:opera', function() {
+ return gulp.src('./dist/opera/manifest.json')
+ .pipe(jsoneditor(function(json) {
+ json.permissions = [
+ "storage",
+ "tabs",
+ "clipboardWrite",
+ "clipboardRead",
+ "http://localhost:8545/"
+ ]
+ return json
+ }))
+ .pipe(gulp.dest('./dist/opera', { overwrite: true }))
+})
+
gulp.task('manifest:production', function() {
return gulp.src([
'./dist/firefox/manifest.json',
@@ -118,7 +138,7 @@ if (!disableLiveReload) {
copyStrings.push('copy:reload')
}
-gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome'))
+gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome', 'manifest:opera'))
gulp.task('copy:watch', function(){
gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy'))
})
@@ -188,7 +208,12 @@ gulp.task('zip:edge', () => {
.pipe(zip(`metamask-edge-${manifest.version}.zip`))
.pipe(gulp.dest('builds'));
})
-gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge'))
+gulp.task('zip:opera', () => {
+ return gulp.src('dist/opera/**')
+ .pipe(zip(`metamask-opera-${manifest.version}.zip`))
+ .pipe(gulp.dest('builds'));
+})
+gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:opera'))
// high level tasks
@@ -255,6 +280,7 @@ function bundleTask(opts) {
.pipe(gulp.dest('./dist/firefox/scripts'))
.pipe(gulp.dest('./dist/chrome/scripts'))
.pipe(gulp.dest('./dist/edge/scripts'))
+ .pipe(gulp.dest('./dist/opera/scripts'))
.pipe(gulpif(!disableLiveReload,livereload()))
)
diff --git a/test/integration/lib/idStore-migrator-test.js b/test/integration/lib/idStore-migrator-test.js
index 338896171..4ae30411d 100644
--- a/test/integration/lib/idStore-migrator-test.js
+++ b/test/integration/lib/idStore-migrator-test.js
@@ -1,6 +1,7 @@
-var KeyringController = require('../../../app/scripts/keyring-controller')
var ConfigManager = require('../../../app/scripts/lib/config-manager')
var IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator')
+var SimpleKeyring = require('../../../app/scripts/keyrings/simple')
+var normalize = require('../../../app/scripts/lib/sig-util').normalize
var oldStyleVault = require('../mocks/oldVault.json')
var badStyleVault = require('../mocks/badVault.json')
@@ -68,7 +69,23 @@ QUnit.test('migrator:migratedVaultForPassword', function (assert) {
assert.equal(lostAccounts.length, 1, 'one lost account')
assert.equal(lostAccounts[0].address, '0xe15D894BeCB0354c501AE69429B05143679F39e0'.toLowerCase())
assert.ok(lostAccounts[0].privateKey, 'private key exported')
- done()
+
+ var lostAccount = lostAccounts[0]
+ var privateKey = lostAccount.privateKey
+
+ var simple = new SimpleKeyring()
+ simple.deserialize([privateKey])
+ .then(() => {
+ return simple.getAccounts()
+ })
+ .then((accounts) => {
+ assert.equal(normalize(accounts[0]), lostAccount.address, 'recovered address.')
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done(reason)
+ })
})
})
diff --git a/test/integration/mocks/badVault2.json b/test/integration/mocks/badVault2.json
new file mode 100644
index 000000000..4b7aec386
--- /dev/null
+++ b/test/integration/mocks/badVault2.json
@@ -0,0 +1 @@
+{"meta":{"version":4},"data":{"fiatCurrency":"USD","isConfirmed":true,"TOSHash":"a4f4e23f823a7ac51783e7ffba7914a911b09acdb97263296b7e14b527f80c5b","noticesList":[{"read":true,"date":"Fri Dec 16 2016","title":"Ending Morden Support","body":"Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.\n\nUsers will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).\n\nPlease use the new Ropsten Network as your new default test network.\n\nYou can fund your Ropsten account using the buy button on your account page.\n\nBest wishes!\nThe MetaMask Team\n\n","id":0}],"conversionRate":7.07341909,"conversionDate":1482539284,"wallet":"{\"encSeed\":{\"encStr\":\"LZsdN8lJzYkUe1UpmAalnERdgkBFt25gWDdK8kfQUwMAk/27XR+dc+8n5swgoF5qgwhc9LBgliEGNDs1Q/lnuld3aQLabkOeAW4BHS1vS7FxqKrzDS3iyzSuQO6wDQmGno/buuknVgDsKiyjW22hpt7vtVVWA+ZL1P3x6M0+AxGJjeGVrG+E8Q==\",\"nonce\":\"T6O9BmwmTj214XUK3KF0s3iCKo3OlrUD\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"GNNfZevCMlgMVh9y21y1UwrC9qcmH6XYq7v+9UoqbHnzPQJFlxidN5+x/Sldo72a6+5zJpQkkdZ+Q0lePrzvXfuSd3D/RO7WKFIKo9nAQI5+JWwz4INuCmVcmqCv2J4BTLGjrG8fp5pDJ62Bn0XHqkJo3gx3fpvs3cS66+ZKwg==\",\"nonce\":\"HRTlGj44khQs2veYHEF/GqTI1t0yYvyd\"},\"hdIndex\":3,\"encPrivKeys\":{\"e15d894becb0354c501ae69429b05143679f39e0\":{\"key\":\"ZAeZL9VcRUtiiO4VXOQKBFg787PR5R3iymjUeU5vpDRIqOXbjWN6N4ZNR8YpSXl+\",\"nonce\":\"xLsADagS8uqDYae6cImyhxF7o1kBDbPe\"},\"87658c15aefe7448008a28513a11b6b130ef4cd0\":{\"key\":\"ku0mm5s1agRJNAMYIJO0qeoDe+FqcbqdQI6azXF3GL1OLo6uMlt6I4qS+eeravFi\",\"nonce\":\"xdGfSUPKtkW8ge0SWIbbpahs/NyEMzn5\"},\"aa25854c0379e53c957ac9382e720c577fa31fd5\":{\"key\":\"NjpYC9FbiC95CTx/1kwgOHk5LSN9vl4RULEBbvwfVOjqSH8WixNoP3R6I/QyNIs2\",\"nonce\":\"M/HWpXXA9QvuZxEykkGQPJKKdz33ovQr\"}},\"addresses\":[\"e15d894becb0354c501ae69429b05143679f39e0\",\"87658c15aefe7448008a28513a11b6b130ef4cd0\",\"aa25854c0379e53c957ac9382e720c577fa31fd5\"]}},\"encHdRootPriv\":{\"encStr\":\"f+3prUOzl+95aNAV+ad6lZdsYZz120ZsL67ucjj3tiMXf/CC4X8XB9N2QguhoMy6fW+fATUsTdJe8+CbAAyb79V9HY0Pitzq9Yw/g1g0/Ii2JzsdGBriuMsPdwZSVqz+rvQFw/6Qms1xjW6cqa8S7kM2WA5l8RB1Ck6r5zaqbA==\",\"nonce\":\"oGahxNFekVxH9sg6PUCCHIByvo4WFSqm\"},\"salt\":\"N7xYoEA53yhSweOsEphku1UKkIEuZtX2MwLBhVM6RR8=\",\"version\":2}","config":{"provider":{"type":"testnet"},"selectedAccount":"0xe15d894becb0354c501ae69429b05143679f39e0"},"isDisclaimerConfirmed":true,"walletNicknames":{"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9":"Account 1","0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4":"Account 2","0x1acfb961c5a8268eac8e09d6241a26cbeff42241":"Account 3"},"lostAccounts":["0xe15d894becb0354c501ae69429b05143679f39e0","0x87658c15aefe7448008a28513a11b6b130ef4cd0","0xaa25854c0379e53c957ac9382e720c577fa31fd5"]}} \ No newline at end of file
diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js
index 7e91616bf..54f38fb2f 100644
--- a/test/unit/idStore-migration-test.js
+++ b/test/unit/idStore-migration-test.js
@@ -83,11 +83,14 @@ describe('IdentityStore to KeyringController migration', function() {
})
describe('entering a password', function() {
- it('should identify an old wallet as an initialized keyring', function() {
+ it('should identify an old wallet as an initialized keyring', function(done) {
keyringController.configManager.setWallet('something')
- const state = keyringController.getState()
- assert(state.isInitialized, 'old vault counted as initialized.')
- assert(!state.lostAccounts, 'no lost accounts')
+ keyringController.getState()
+ .then((state) => {
+ assert(state.isInitialized, 'old vault counted as initialized.')
+ assert(!state.lostAccounts, 'no lost accounts')
+ done()
+ })
})
})
})
diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js
index ef7b749e4..16019c88a 100644
--- a/ui/app/accounts/account-list-item.js
+++ b/ui/app/accounts/account-list-item.js
@@ -15,19 +15,21 @@ function AccountListItem () {
}
AccountListItem.prototype.render = function () {
- const identity = this.props.identity
- var isSelected = this.props.selectedAccount === identity.address
- var account = this.props.accounts[identity.address]
+ const { identity, selectedAccount, accounts, onShowDetail } = this.props
+
+ const isSelected = selectedAccount === identity.address
+ const account = accounts[identity.address]
const selectedClass = isSelected ? '.selected' : ''
return (
h(`.accounts-list-option.flex-row.flex-space-between.pointer.hover-white${selectedClass}`, {
key: `account-panel-${identity.address}`,
- onClick: (event) => this.props.onShowDetail(identity.address, event),
+ onClick: (event) => onShowDetail(identity.address, event),
}, [
h('.identicon-wrapper.flex-column.flex-center.select-none', [
this.pendingOrNot(),
+ this.indicateIfLoose(),
h(Identicon, {
address: identity.address,
imageify: true,
@@ -48,7 +50,7 @@ AccountListItem.prototype.render = function () {
},
}, ethUtil.toChecksumAddress(identity.address)),
h(EthBalance, {
- value: account.balance,
+ value: account && account.balance,
style: {
lineHeight: '7px',
marginTop: '10px',
@@ -70,6 +72,14 @@ AccountListItem.prototype.render = function () {
)
}
+AccountListItem.prototype.indicateIfLoose = function () {
+ try { // Sometimes keyrings aren't loaded yet:
+ const type = this.props.keyring.type
+ const isLoose = type !== 'HD Key Tree'
+ return isLoose ? h('.keyring-label', 'LOOSE') : null
+ } catch (e) { return }
+}
+
AccountListItem.prototype.pendingOrNot = function () {
const pending = this.props.pending
if (pending.length === 0) return null
diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js
index fcb3a7b0f..edb15eafe 100644
--- a/ui/app/accounts/index.js
+++ b/ui/app/accounts/index.js
@@ -22,6 +22,7 @@ function mapStateToProps (state) {
selectedAccount: state.metamask.selectedAccount,
scrollToBottom: state.appState.scrollToBottom,
pending,
+ keyrings: state.metamask.keyrings,
}
}
@@ -31,9 +32,10 @@ function AccountsScreen () {
}
AccountsScreen.prototype.render = function () {
- var state = this.props
- var identityList = valuesFor(state.identities)
- var unconfTxList = valuesFor(state.unconfTxs)
+ const props = this.props
+ const { keyrings } = props
+ const identityList = valuesFor(props.identities)
+ const unconfTxList = valuesFor(props.unconfTxs)
return (
@@ -69,6 +71,11 @@ AccountsScreen.prototype.render = function () {
}
})
+ const simpleAddress = identity.address.substring(2).toLowerCase()
+ const keyring = keyrings.find((kr) => {
+ return kr.accounts.includes(simpleAddress)
+ })
+
return h(AccountListItem, {
key: `acct-panel-${identity.address}`,
identity,
@@ -76,6 +83,7 @@ AccountsScreen.prototype.render = function () {
accounts: this.props.accounts,
onShowDetail: this.onShowDetail.bind(this),
pending,
+ keyring,
})
}),
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 606460314..d63d36f19 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -153,7 +153,7 @@ var actions = {
SHOW_NEW_KEYCHAIN: 'SHOW_NEW_KEYCHAIN',
showNewKeychain: showNewKeychain,
-
+ callBackgroundThenUpdate,
}
module.exports = actions
@@ -226,14 +226,7 @@ function createNewVaultAndRestore (password, seed) {
}
function createNewVaultAndKeychain (password) {
- return (dispatch) => {
- background.createNewVaultAndKeychain(password, (err, newState) => {
- if (err) {
- return dispatch(actions.showWarning(err.message))
- }
- dispatch(actions.updateMetamaskState(newState))
- })
- }
+ return callBackgroundThenUpdate(background.createNewVaultAndKeychain, password)
}
function revealSeedConfirmation () {
@@ -255,29 +248,12 @@ function requestRevealSeed (password) {
}
}
-
function addNewKeyring (type, opts) {
- return (dispatch) => {
- dispatch(actions.showLoadingIndication())
- background.addNewKeyring(type, opts, (err) => {
- dispatch(this.hideLoadingIndication())
- if (err) {
- return dispatch(actions.showWarning(err))
- }
- })
- }
+ return callBackgroundThenUpdate(background.addNewKeyring, type, opts)
}
function addNewAccount (ringNumber = 0) {
- return (dispatch) => {
- dispatch(actions.showLoadingIndication())
- background.addNewAccount(ringNumber, (err) => {
- dispatch(this.hideLoadingIndication())
- if (err) {
- return dispatch(actions.showWarning(err))
- }
- })
- }
+ return callBackgroundThenUpdate(background.addNewAccount, ringNumber)
}
function showInfoPage () {
@@ -475,15 +451,7 @@ function updateMetamaskState (newState) {
}
function lockMetamask () {
- return (dispatch) => {
- background.setLocked((err, newState) => {
- dispatch(actions.hideLoadingIndication())
- if (err) {
- return dispatch(actions.displayWarning(err.message))
- }
- dispatch(actions.updateMetamaskState(newState))
- })
- }
+ return callBackgroundThenUpdate(background.setLocked)
}
function showAccountDetail (address) {
@@ -565,7 +533,7 @@ function markNoticeRead (notice) {
background.markNoticeRead(notice, (err, notice) => {
dispatch(this.hideLoadingIndication())
if (err) {
- return dispatch(actions.showWarning(err))
+ return dispatch(actions.displayWarning(err))
}
if (notice) {
return dispatch(actions.showNotice(notice))
@@ -593,14 +561,7 @@ function clearNotices () {
}
function markAccountsFound() {
- return (dispatch) => {
- dispatch(this.showLoadingIndication())
- background.markAccountsFound((err, newState) => {
- dispatch(this.hideLoadingIndication())
- if (err) return dispatch(this.showWarning(err.message))
- dispatch(actions.updateMetamaskState(newState))
- })
- }
+ return callBackgroundThenUpdate(background.markAccountsFound)
}
//
@@ -857,3 +818,24 @@ function shapeShiftRequest (query, options, cb) {
return shapShiftReq.send()
}
}
+
+// Call Background Then Update
+//
+// A function generator for a common pattern wherein:
+// We show loading indication.
+// We call a background method.
+// We hide loading indication.
+// If it errored, we show a warning.
+// If it didn't, we update the state.
+function callBackgroundThenUpdate (method, ...args) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ method.call(background, ...args, (err, newState) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ dispatch(actions.updateMetamaskState(newState))
+ })
+ }
+}
diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js
index 46127bed5..57ca84564 100644
--- a/ui/app/components/eth-balance.js
+++ b/ui/app/components/eth-balance.js
@@ -15,9 +15,10 @@ function EthBalanceComponent () {
EthBalanceComponent.prototype.render = function () {
var props = this.props
+ let { value } = props
var style = props.style
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
- const value = formatBalance(props.value, 6, needsParse)
+ value = value ? formatBalance(value, 6, needsParse) : '...'
var width = props.width
return (
@@ -38,6 +39,7 @@ EthBalanceComponent.prototype.render = function () {
EthBalanceComponent.prototype.renderBalance = function (value) {
var props = this.props
if (value === 'None') return value
+ if (value === '...') return value
var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
var balance
var splitBalance = value.split(' ')
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index f5f602729..abbf8667e 100644
--- a/ui/app/css/lib.css
+++ b/ui/app/css/lib.css
@@ -196,6 +196,23 @@ hr.horizontal-line {
align-items: center;
justify-content: center;
padding: 4px;
+ z-index: 1;
+}
+
+.keyring-label {
+ z-index: 1;
+ font-size: 11px;
+ background: rgba(255,0,0,0.8);
+ bottom: -47px;
+ color: white;
+ border-radius: 10px;
+ height: 20px;
+ min-width: 20px;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 4px;
}
.ether-balance {