aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfrankiebee <frankie.diamond@gmail.com>2017-03-24 06:44:29 +0800
committerfrankiebee <frankie.diamond@gmail.com>2017-03-24 06:44:29 +0800
commit8e30acb41a2cd97efd5428c60c2a5c910758b412 (patch)
treeae797e27d776827424fb9f15be07d3b02e5c9f92
parentc8540261c1c9b60c98845bdd19095efc064fdde8 (diff)
parentf29a116d4027e387180aa1232d35afcc15b920b0 (diff)
downloadtangerine-wallet-browser-8e30acb41a2cd97efd5428c60c2a5c910758b412.tar.gz
tangerine-wallet-browser-8e30acb41a2cd97efd5428c60c2a5c910758b412.tar.zst
tangerine-wallet-browser-8e30acb41a2cd97efd5428c60c2a5c910758b412.zip
Merge branch 'master' into mascara
-rw-r--r--CHANGELOG.md7
-rw-r--r--app/scripts/config.js2
-rw-r--r--app/scripts/contentscript.js14
-rw-r--r--app/scripts/controllers/address-book.js79
-rw-r--r--app/scripts/controllers/preferences.js4
-rw-r--r--app/scripts/lib/config-manager.js4
-rw-r--r--app/scripts/lib/eth-store.js6
-rw-r--r--app/scripts/lib/id-management.js90
-rw-r--r--app/scripts/lib/idStore-migrator.js80
-rw-r--r--app/scripts/lib/idStore.js343
-rw-r--r--app/scripts/metamask-controller.js55
-rw-r--r--app/scripts/migrations/010.js2
-rw-r--r--app/scripts/migrations/011.js2
-rw-r--r--app/scripts/transaction-manager.js15
-rw-r--r--development/states/account-detail-with-shapeshift-tx.json2
-rw-r--r--development/states/account-detail-with-transaction-history.json2
-rw-r--r--development/states/account-detail.json2
-rw-r--r--development/states/account-list-with-imported.json2
-rw-r--r--development/states/accounts-loose.json2
-rw-r--r--development/states/accounts.json2
-rw-r--r--development/states/compilation-bug.json2
-rw-r--r--development/states/conf-tx.json2
-rw-r--r--development/states/config.json2
-rw-r--r--development/states/first-time.json2
-rw-r--r--development/states/import-private-key-warning.json2
-rw-r--r--development/states/import-private-key.json2
-rw-r--r--development/states/locked.json2
-rw-r--r--development/states/lost-accounts.json2
-rw-r--r--development/states/new-account.json2
-rw-r--r--development/states/notice.json2
-rw-r--r--development/states/pending-signature.json2
-rw-r--r--development/states/pending-tx-insufficient.json2
-rw-r--r--development/states/personal-sign.json2
-rw-r--r--development/states/private-key-export-success.json70
-rw-r--r--development/states/private-key-export.json69
-rw-r--r--development/states/private-network.json2
-rw-r--r--development/states/restore-vault.json2
-rw-r--r--development/states/send.json2
-rw-r--r--development/states/shapeshift.json2
-rw-r--r--development/states/terms-and-conditions.json2
-rw-r--r--package.json3
-rw-r--r--test/integration/lib/idStore-migrator-test.js92
-rw-r--r--test/unit/address-book-controller.js56
-rw-r--r--test/unit/currency-controller-test.js2
-rw-r--r--test/unit/id-management-test.js35
-rw-r--r--test/unit/idStore-migration-test.js83
-rw-r--r--test/unit/idStore-test.js142
-rw-r--r--ui/app/accounts/import/json.js3
-rw-r--r--ui/app/accounts/import/private-key.js3
-rw-r--r--ui/app/actions.js54
-rw-r--r--ui/app/app.js15
-rw-r--r--ui/app/components/account-export.js93
-rw-r--r--ui/app/components/binary-renderer.js21
-rw-r--r--ui/app/components/drop-menu-item.js5
-rw-r--r--ui/app/components/ens-input.js29
-rw-r--r--ui/app/components/fiat-value.js4
-rw-r--r--ui/app/components/network.js12
-rw-r--r--ui/app/components/pending-personal-msg-details.js13
-rw-r--r--ui/app/components/transaction-list-item.js2
-rw-r--r--ui/app/config.js17
-rw-r--r--ui/app/css/index.css3
-rw-r--r--ui/app/css/lib.css2
-rw-r--r--ui/app/reducers/app.js1
-rw-r--r--ui/app/reducers/metamask.js6
-rw-r--r--ui/app/send.js15
-rw-r--r--ui/lib/account-link.js4
-rw-r--r--ui/lib/explorer-link.js5
67 files changed, 573 insertions, 1037 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f070d9a9..b77ceb864 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,14 @@
## Current Master
+- Add better error messages for when a transaction fails on approval
- Allow sending to ENS names in send form on Ropsten.
+- Added an address book functionality that remembers the last 15 unique addresses sent to.
+- Can now change network to custom RPC URL from lock screen.
+- Removed support for old, lightwallet based vault. Users who have not opened app in over a month will need to recover with their seed phrase. This will allow Firefox support sooner.
+- Fixed bug where spinner wouldn't disappear on incorrect password submission on seed word reveal.
+- Polish the private key UI.
+- Add Kovan as an option on our network list.
## 3.4.0 2017-3-8
diff --git a/app/scripts/config.js b/app/scripts/config.js
index b4541a04a..ec421744d 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -1,5 +1,6 @@
const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
const TESTNET_RPC_URL = 'https://ropsten.infura.io/metamask'
+const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
const DEFAULT_RPC_URL = TESTNET_RPC_URL
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
@@ -10,5 +11,6 @@ module.exports = {
mainnet: MAINET_RPC_URL,
testnet: TESTNET_RPC_URL,
morden: TESTNET_RPC_URL,
+ kovan: KOVAN_RPC_URL,
},
}
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index ab64dc9fa..020208ceb 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -69,14 +69,10 @@ function shouldInjectWeb3 () {
}
function isAllowedSuffix (testCase) {
- var prohibitedTypes = ['xml', 'pdf']
- var currentUrl = window.location.href
- var currentRegex
- for (let i = 0; i < prohibitedTypes.length; i++) {
- currentRegex = new RegExp(`\.${prohibitedTypes[i]}$`)
- if (currentRegex.test(currentUrl)) {
- return false
- }
+ const doctype = window.document.doctype
+ if (doctype) {
+ return doctype.name === 'html'
+ } else {
+ return false
}
- return true
}
diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js
new file mode 100644
index 000000000..c66eb2bd4
--- /dev/null
+++ b/app/scripts/controllers/address-book.js
@@ -0,0 +1,79 @@
+const ObservableStore = require('obs-store')
+const extend = require('xtend')
+
+class AddressBookController {
+
+
+ // Controller in charge of managing the address book functionality from the
+ // recipients field on the send screen. Manages a history of all saved
+ // addresses and all currently owned addresses.
+ constructor (opts = {}, keyringController) {
+ const initState = extend({
+ addressBook: [],
+ }, opts.initState)
+ this.store = new ObservableStore(initState)
+ this.keyringController = keyringController
+ }
+
+ //
+ // PUBLIC METHODS
+ //
+
+ // Sets a new address book in store by accepting a new address and nickname.
+ setAddressBook (address, name) {
+ return this._addToAddressBook(address, name)
+ .then((addressBook) => {
+ this.store.updateState({
+ addressBook,
+ })
+ return Promise.resolve()
+ })
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+
+ // Performs the logic to add the address and name into the address book. The
+ // pushed object is an object of two fields. Current behavior does not set an
+ // upper limit to the number of addresses.
+ _addToAddressBook (address, name) {
+ let addressBook = this._getAddressBook()
+ let identities = this._getIdentities()
+
+ let addressBookIndex = addressBook.findIndex((element) => { return element.address.toLowerCase() === address.toLowerCase() || element.name === name })
+ let identitiesIndex = Object.keys(identities).findIndex((element) => { return element.toLowerCase() === address.toLowerCase() })
+ // trigger this condition if we own this address--no need to overwrite.
+ if (identitiesIndex !== -1) {
+ return Promise.resolve(addressBook)
+ // trigger this condition if we've seen this address before--may need to update nickname.
+ } else if (addressBookIndex !== -1) {
+ addressBook.splice(addressBookIndex, 1)
+ } else if (addressBook.length > 15) {
+ addressBook.shift()
+ }
+
+
+ addressBook.push({
+ address: address,
+ name,
+ })
+ return Promise.resolve(addressBook)
+ }
+
+ // Internal method to get the address book. Current persistence behavior
+ // should not require that this method be called from the UI directly.
+ _getAddressBook () {
+ return this.store.getState().addressBook
+ }
+
+ // Retrieves identities from the keyring controller in order to avoid
+ // duplication
+ _getIdentities () {
+ return this.keyringController.memStore.getState().identities
+ }
+
+}
+
+module.exports = AddressBookController
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 18fccf11b..c7f675a41 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -5,7 +5,9 @@ const extend = require('xtend')
class PreferencesController {
constructor (opts = {}) {
- const initState = extend({ frequentRpcList: [] }, opts.initState)
+ const initState = extend({
+ frequentRpcList: [],
+ }, opts.initState)
this.store = new ObservableStore(initState)
}
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 6868637e5..e31cb45ed 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -5,6 +5,7 @@ const normalize = require('eth-sig-util').normalize
const TESTNET_RPC = MetamaskConfig.network.testnet
const MAINNET_RPC = MetamaskConfig.network.mainnet
const MORDEN_RPC = MetamaskConfig.network.morden
+const KOVAN_RPC = MetamaskConfig.network.kovan
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
@@ -150,6 +151,9 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
case 'morden':
return MORDEN_RPC
+ case 'kovan':
+ return KOVAN_RPC
+
default:
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
}
diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js
index 8812a507b..243253df2 100644
--- a/app/scripts/lib/eth-store.js
+++ b/app/scripts/lib/eth-store.js
@@ -19,6 +19,8 @@ class EthereumStore extends ObservableStore {
super({
accounts: {},
transactions: {},
+ currentBlockNumber: '0',
+ currentBlockHash: '',
})
this._provider = opts.provider
this._query = new EthQuery(this._provider)
@@ -69,6 +71,8 @@ class EthereumStore extends ObservableStore {
_updateForBlock (block) {
const blockNumber = '0x' + block.number.toString('hex')
this._currentBlockNumber = blockNumber
+ this.updateState({ currentBlockNumber: parseInt(blockNumber) })
+ this.updateState({ currentBlockHash: `0x${block.hash.toString('hex')}`})
async.parallel([
this._updateAccounts.bind(this),
this._updateTransactions.bind(this, blockNumber),
@@ -129,4 +133,4 @@ class EthereumStore extends ObservableStore {
}
-module.exports = EthereumStore \ No newline at end of file
+module.exports = EthereumStore
diff --git a/app/scripts/lib/id-management.js b/app/scripts/lib/id-management.js
deleted file mode 100644
index 90b3fdb13..000000000
--- a/app/scripts/lib/id-management.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* ID Management
- *
- * This module exists to hold the decrypted credentials for the current session.
- * It therefore exposes sign methods, because it is able to perform these
- * with noa dditional authentication, because its very instantiation
- * means the vault is unlocked.
- */
-
-const ethUtil = require('ethereumjs-util')
-const Transaction = require('ethereumjs-tx')
-
-module.exports = IdManagement
-
-function IdManagement (opts) {
- if (!opts) opts = {}
-
- this.keyStore = opts.keyStore
- this.derivedKey = opts.derivedKey
- this.configManager = opts.configManager
- this.hdPathString = "m/44'/60'/0'/0"
-
- this.getAddresses = function () {
- return this.keyStore.getAddresses(this.hdPathString).map(function (address) { return '0x' + address })
- }
-
- this.signTx = function (txParams) {
-
- // normalize values
- txParams.gasPrice = ethUtil.intToHex(txParams.gasPrice)
- txParams.to = ethUtil.addHexPrefix(txParams.to)
- txParams.from = ethUtil.addHexPrefix(txParams.from.toLowerCase())
- txParams.value = ethUtil.addHexPrefix(txParams.value)
- txParams.data = ethUtil.addHexPrefix(txParams.data)
- txParams.gasLimit = ethUtil.addHexPrefix(txParams.gasLimit || txParams.gas)
- txParams.nonce = ethUtil.addHexPrefix(txParams.nonce)
- var tx = new Transaction(txParams)
-
- // sign tx
- var privKeyHex = this.exportPrivateKey(txParams.from)
- var privKey = ethUtil.toBuffer(privKeyHex)
- tx.sign(privKey)
-
- // Add the tx hash to the persisted meta-tx object
- var txHash = ethUtil.bufferToHex(tx.hash())
- var metaTx = this.configManager.getTx(txParams.metamaskId)
- metaTx.hash = txHash
- this.configManager.updateTx(metaTx)
-
- // return raw serialized tx
- var rawTx = ethUtil.bufferToHex(tx.serialize())
- return rawTx
- }
-
- this.signMsg = function (address, message) {
- // sign message
- var privKeyHex = this.exportPrivateKey(address.toLowerCase())
- var privKey = ethUtil.toBuffer(privKeyHex)
- var msgSig = ethUtil.ecsign(new Buffer(message.replace('0x', ''), 'hex'), privKey)
- var rawMsgSig = ethUtil.bufferToHex(concatSig(msgSig.v, msgSig.r, msgSig.s))
- return rawMsgSig
- }
-
- this.getSeed = function () {
- return this.keyStore.getSeed(this.derivedKey)
- }
-
- this.exportPrivateKey = function (address) {
- var privKeyHex = ethUtil.addHexPrefix(this.keyStore.exportPrivateKey(address, this.derivedKey, this.hdPathString))
- return privKeyHex
- }
-}
-
-function padWithZeroes (number, length) {
- var myString = '' + number
- while (myString.length < length) {
- myString = '0' + myString
- }
- return myString
-}
-
-function concatSig (v, r, s) {
- const rSig = ethUtil.fromSigned(r)
- const sSig = ethUtil.fromSigned(s)
- const vSig = ethUtil.bufferToInt(v)
- const rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64)
- const sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64)
- const vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig))
- return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex')
-}
-
diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js
deleted file mode 100644
index 62d21eee7..000000000
--- a/app/scripts/lib/idStore-migrator.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const IdentityStore = require('./idStore')
-const HdKeyring = require('eth-hd-keyring')
-const sigUtil = require('eth-sig-util')
-const normalize = sigUtil.normalize
-const denodeify = require('denodeify')
-
-module.exports = class IdentityStoreMigrator {
-
- constructor ({ configManager }) {
- this.configManager = configManager
- const hasOldVault = this.hasOldVault()
- if (!hasOldVault) {
- this.idStore = new IdentityStore({ configManager })
- }
- }
-
- migratedVaultForPassword (password) {
- const hasOldVault = this.hasOldVault()
- const configManager = this.configManager
-
- if (!this.idStore) {
- this.idStore = new IdentityStore({ configManager })
- }
-
- if (!hasOldVault) {
- return Promise.resolve(null)
- }
-
- const idStore = this.idStore
- const submitPassword = denodeify(idStore.submitPassword.bind(idStore))
-
- return submitPassword(password)
- .then(() => {
- const serialized = this.serializeVault()
- return this.checkForLostAccounts(serialized)
- })
- }
-
- serializeVault () {
- const mnemonic = this.idStore._idmgmt.getSeed()
- const numberOfAccounts = this.idStore._getAddresses().length
-
- return {
- type: 'HD Key Tree',
- data: { mnemonic, numberOfAccounts },
- }
- }
-
- checkForLostAccounts (serialized) {
- const hd = new HdKeyring()
- return hd.deserialize(serialized.data)
- .then((hexAccounts) => {
- const newAccounts = hexAccounts.map(normalize)
- const oldAccounts = this.idStore._getAddresses().map(normalize)
- const lostAccounts = oldAccounts.reduce((result, account) => {
- if (newAccounts.includes(account)) {
- return result
- } else {
- result.push(account)
- return result
- }
- }, [])
-
- return {
- serialized,
- lostAccounts: lostAccounts.map((address) => {
- return {
- address,
- privateKey: this.idStore.exportAccount(address),
- }
- }),
- }
- })
- }
-
- hasOldVault () {
- const wallet = this.configManager.getWallet()
- return wallet
- }
-}
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
deleted file mode 100644
index 01474035e..000000000
--- a/app/scripts/lib/idStore.js
+++ /dev/null
@@ -1,343 +0,0 @@
-const EventEmitter = require('events').EventEmitter
-const inherits = require('util').inherits
-const ethUtil = require('ethereumjs-util')
-const KeyStore = require('eth-lightwallet').keystore
-const clone = require('clone')
-const extend = require('xtend')
-const autoFaucet = require('./auto-faucet')
-const DEFAULT_RPC = 'https://testrpc.metamask.io/'
-const IdManagement = require('./id-management')
-
-
-module.exports = IdentityStore
-
-inherits(IdentityStore, EventEmitter)
-function IdentityStore (opts = {}) {
- EventEmitter.call(this)
-
- // we just use the ethStore to auto-add accounts
- this._ethStore = opts.ethStore
- this.configManager = opts.configManager
- // lightwallet key store
- this._keyStore = null
- // lightwallet wrapper
- this._idmgmt = null
-
- this.hdPathString = "m/44'/60'/0'/0"
-
- this._currentState = {
- selectedAddress: null,
- identities: {},
- }
- // not part of serilized metamask state - only kept in memory
-}
-
-//
-// public
-//
-
-IdentityStore.prototype.createNewVault = function (password, cb) {
- delete this._keyStore
- var serializedKeystore = this.configManager.getWallet()
-
- if (serializedKeystore) {
- this.configManager.setData({})
- }
-
- this.purgeCache()
- this._createVault(password, null, (err) => {
- if (err) return cb(err)
-
- this._autoFaucet()
-
- this.configManager.setShowSeedWords(true)
- var seedWords = this._idmgmt.getSeed()
-
- this._loadIdentities()
-
- cb(null, seedWords)
- })
-}
-
-IdentityStore.prototype.recoverSeed = function (cb) {
- this.configManager.setShowSeedWords(true)
- if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.'))
- var seedWords = this._idmgmt.getSeed()
- cb(null, seedWords)
-}
-
-IdentityStore.prototype.recoverFromSeed = function (password, seed, cb) {
- this.purgeCache()
-
- this._createVault(password, seed, (err) => {
- if (err) return cb(err)
-
- this._loadIdentities()
- cb(null, this.getState())
- })
-}
-
-IdentityStore.prototype.setStore = function (store) {
- this._ethStore = store
-}
-
-IdentityStore.prototype.clearSeedWordCache = function (cb) {
- const configManager = this.configManager
- configManager.setShowSeedWords(false)
- cb(null, configManager.getSelectedAccount())
-}
-
-IdentityStore.prototype.getState = function () {
- const configManager = this.configManager
- var seedWords = this.getSeedIfUnlocked()
- return clone(extend(this._currentState, {
- isInitialized: !!configManager.getWallet() && !seedWords,
- isUnlocked: this._isUnlocked(),
- seedWords: seedWords,
- selectedAddress: configManager.getSelectedAccount(),
- }))
-}
-
-IdentityStore.prototype.getSeedIfUnlocked = function () {
- const configManager = this.configManager
- var showSeed = configManager.getShouldShowSeedWords()
- var idmgmt = this._idmgmt
- var shouldShow = showSeed && !!idmgmt
- var seedWords = shouldShow ? idmgmt.getSeed() : null
- return seedWords
-}
-
-IdentityStore.prototype.getSelectedAddress = function () {
- const configManager = this.configManager
- return configManager.getSelectedAccount()
-}
-
-IdentityStore.prototype.setSelectedAddressSync = function (address) {
- const configManager = this.configManager
- if (!address) {
- var addresses = this._getAddresses()
- address = addresses[0]
- }
-
- configManager.setSelectedAccount(address)
- return address
-}
-
-IdentityStore.prototype.setSelectedAddress = function (address, cb) {
- const resultAddress = this.setSelectedAddressSync(address)
- if (cb) return cb(null, resultAddress)
-}
-
-IdentityStore.prototype.revealAccount = function (cb) {
- const derivedKey = this._idmgmt.derivedKey
- const keyStore = this._keyStore
- const configManager = this.configManager
-
- keyStore.setDefaultHdDerivationPath(this.hdPathString)
- keyStore.generateNewAddress(derivedKey, 1)
- const addresses = keyStore.getAddresses()
- const address = addresses[ addresses.length - 1 ]
-
- this._ethStore.addAccount(ethUtil.addHexPrefix(address))
-
- configManager.setWallet(keyStore.serialize())
-
- this._loadIdentities()
- this._didUpdate()
- cb(null)
-}
-
-IdentityStore.prototype.getNetwork = function (err) {
- if (err) {
- this._currentState.network = 'loading'
- this._didUpdate()
- }
-
- this.web3.version.getNetwork((err, network) => {
- if (err) {
- this._currentState.network = 'loading'
- return this._didUpdate()
- }
- if (global.METAMASK_DEBUG) {
- console.log('web3.getNetwork returned ' + network)
- }
- this._currentState.network = network
- this._didUpdate()
- })
-}
-
-IdentityStore.prototype.setLocked = function (cb) {
- delete this._keyStore
- delete this._idmgmt
- cb()
-}
-
-IdentityStore.prototype.submitPassword = function (password, cb) {
- const configManager = this.configManager
- this.tryPassword(password, (err) => {
- if (err) return cb(err)
- // load identities before returning...
- this._loadIdentities()
- cb(null, configManager.getSelectedAccount())
- })
-}
-
-IdentityStore.prototype.exportAccount = function (address, cb) {
- var privateKey = this._idmgmt.exportPrivateKey(address)
- if (cb) cb(null, privateKey)
- return privateKey
-}
-
-// private
-//
-
-IdentityStore.prototype._didUpdate = function () {
- this.emit('update', this.getState())
-}
-
-IdentityStore.prototype._isUnlocked = function () {
- var result = Boolean(this._keyStore) && Boolean(this._idmgmt)
- return result
-}
-
-// load identities from keyStoreet
-IdentityStore.prototype._loadIdentities = function () {
- const configManager = this.configManager
- if (!this._isUnlocked()) throw new Error('not unlocked')
-
- var addresses = this._getAddresses()
- addresses.forEach((address, i) => {
- // // add to ethStore
- if (this._ethStore) {
- this._ethStore.addAccount(ethUtil.addHexPrefix(address))
- }
- // add to identities
- const defaultLabel = 'Account ' + (i + 1)
- const nickname = configManager.nicknameForWallet(address)
- var identity = {
- name: nickname || defaultLabel,
- address: address,
- mayBeFauceting: this._mayBeFauceting(i),
- }
- this._currentState.identities[address] = identity
- })
- this._didUpdate()
-}
-
-IdentityStore.prototype.saveAccountLabel = function (account, label, cb) {
- const configManager = this.configManager
- configManager.setNicknameForWallet(account, label)
- this._loadIdentities()
- cb(null, label)
-}
-
-// mayBeFauceting
-// If on testnet, index 0 may be fauceting.
-// The UI will have to check the balance to know.
-// If there is no balance and it mayBeFauceting,
-// then it is in fact fauceting.
-IdentityStore.prototype._mayBeFauceting = function (i) {
- const configManager = this.configManager
- var config = configManager.getProvider()
- if (i === 0 &&
- config.type === 'rpc' &&
- config.rpcTarget === DEFAULT_RPC) {
- return true
- }
- return false
-}
-
-//
-// keyStore managment - unlocking + deserialization
-//
-
-IdentityStore.prototype.tryPassword = function (password, cb) {
- var serializedKeystore = this.configManager.getWallet()
- var keyStore = KeyStore.deserialize(serializedKeystore)
-
- keyStore.keyFromPassword(password, (err, pwDerivedKey) => {
- if (err) return cb(err)
-
- const isCorrect = keyStore.isDerivedKeyCorrect(pwDerivedKey)
- if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
-
- this._keyStore = keyStore
- this._createIdMgmt(pwDerivedKey)
- cb()
- })
-}
-
-IdentityStore.prototype._createVault = function (password, seedPhrase, cb) {
- const opts = {
- password,
- hdPathString: this.hdPathString,
- }
-
- if (seedPhrase) {
- opts.seedPhrase = seedPhrase
- }
-
- KeyStore.createVault(opts, (err, keyStore) => {
- if (err) return cb(err)
-
- this._keyStore = keyStore
-
- keyStore.keyFromPassword(password, (err, derivedKey) => {
- if (err) return cb(err)
-
- this.purgeCache()
-
- keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
-
- this._createFirstWallet(derivedKey)
- this._createIdMgmt(derivedKey)
- this.setSelectedAddressSync()
-
- cb()
- })
- })
-}
-
-IdentityStore.prototype._createIdMgmt = function (derivedKey) {
- this._idmgmt = new IdManagement({
- keyStore: this._keyStore,
- derivedKey: derivedKey,
- configManager: this.configManager,
- })
-}
-
-IdentityStore.prototype.purgeCache = function () {
- this._currentState.identities = {}
- let accounts
- try {
- accounts = Object.keys(this._ethStore._currentState.accounts)
- } catch (e) {
- accounts = []
- }
- accounts.forEach((address) => {
- this._ethStore.removeAccount(address)
- })
-}
-
-IdentityStore.prototype._createFirstWallet = function (derivedKey) {
- const keyStore = this._keyStore
- keyStore.setDefaultHdDerivationPath(this.hdPathString)
- keyStore.generateNewAddress(derivedKey, 1)
- this.configManager.setWallet(keyStore.serialize())
- var addresses = keyStore.getAddresses()
- this._ethStore.addAccount(ethUtil.addHexPrefix(addresses[0]))
-}
-
-// get addresses and normalize address hexString
-IdentityStore.prototype._getAddresses = function () {
- return this._keyStore.getAddresses(this.hdPathString).map((address) => {
- return ethUtil.addHexPrefix(address)
- })
-}
-
-IdentityStore.prototype._autoFaucet = function () {
- var addresses = this._getAddresses()
- autoFaucet(addresses[0])
-}
-
-// util
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index a26c0e45d..15bf9f436 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -15,6 +15,7 @@ const PreferencesController = require('./controllers/preferences')
const CurrencyController = require('./controllers/currency')
const NoticeController = require('./notice-controller')
const ShapeShiftController = require('./controllers/shapeshift')
+const AddressBookController = require('./controllers/address-book')
const MessageManager = require('./lib/message-manager')
const PersonalMessageManager = require('./lib/personal-message-manager')
const TxManager = require('./transaction-manager')
@@ -22,7 +23,6 @@ const ConfigManager = require('./lib/config-manager')
const extension = require('./lib/extension')
const autoFaucet = require('./lib/auto-faucet')
const nodeify = require('./lib/nodeify')
-const IdStoreMigrator = require('./lib/idStore-migrator')
const accountImporter = require('./account-import-strategies')
const version = require('../manifest.json').version
@@ -80,6 +80,11 @@ module.exports = class MetamaskController extends EventEmitter {
autoFaucet(address)
})
+ // address book controller
+ this.addressBookController = new AddressBookController({
+ initState: initState.AddressBookController,
+ }, this.keyringController)
+
// tx mgmt
this.txManager = new TxManager({
initState: initState.TransactionManager,
@@ -109,11 +114,6 @@ module.exports = class MetamaskController extends EventEmitter {
this.personalMessageManager = new PersonalMessageManager()
this.publicConfigStore = this.initPublicConfigStore()
- // TEMPORARY UNTIL FULL DEPRECATION:
- this.idStoreMigrator = new IdStoreMigrator({
- configManager: this.configManager,
- })
-
// manual disk state subscriptions
this.txManager.store.subscribe((state) => {
this.store.updateState({ TransactionManager: state })
@@ -124,6 +124,9 @@ module.exports = class MetamaskController extends EventEmitter {
this.preferencesController.store.subscribe((state) => {
this.store.updateState({ PreferencesController: state })
})
+ this.addressBookController.store.subscribe((state) => {
+ this.store.updateState({ AddressBookController: state })
+ })
this.currencyController.store.subscribe((state) => {
this.store.updateState({ CurrencyController: state })
})
@@ -142,6 +145,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
+ this.addressBookController.store.subscribe(this.sendUpdate.bind(this))
this.currencyController.store.subscribe(this.sendUpdate.bind(this))
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
@@ -218,6 +222,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.personalMessageManager.memStore.getState(),
this.keyringController.memStore.getState(),
this.preferencesController.store.getState(),
+ this.addressBookController.store.getState(),
this.currencyController.store.getState(),
this.noticeController.memStore.getState(),
// config manager
@@ -239,6 +244,7 @@ module.exports = class MetamaskController extends EventEmitter {
const preferencesController = this.preferencesController
const txManager = this.txManager
const noticeController = this.noticeController
+ const addressBookController = this.addressBookController
return {
// etc
@@ -266,6 +272,9 @@ module.exports = class MetamaskController extends EventEmitter {
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
+ // AddressController
+ setAddressBook: nodeify(addressBookController.setAddressBook).bind(addressBookController),
+
// KeyringController
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
@@ -350,8 +359,7 @@ module.exports = class MetamaskController extends EventEmitter {
//
submitPassword (password, cb) {
- this.migrateOldVaultIfAny(password)
- .then(this.keyringController.submitPassword.bind(this.keyringController, password))
+ return this.keyringController.submitPassword(password)
.then((newState) => { cb(null, newState) })
.catch((reason) => { cb(reason) })
}
@@ -546,35 +554,6 @@ module.exports = class MetamaskController extends EventEmitter {
cb(null, this.getState())
}
- // Migrate Old Vault If Any
- // @string password
- //
- // returns Promise()
- //
- // Temporary step used when logging in.
- // Checks if old style (pre-3.0.0) Metamask Vault exists.
- // If so, persists that vault in the new vault format
- // with the provided password, so the other unlock steps
- // may be completed without interruption.
- migrateOldVaultIfAny (password) {
-
- if (!this.checkIfShouldMigrate()) {
- return Promise.resolve(password)
- }
-
- const keyringController = this.keyringController
-
- return this.idStoreMigrator.migratedVaultForPassword(password)
- .then(this.restoreOldVaultAccounts.bind(this))
- .then(this.restoreOldLostAccounts.bind(this))
- .then(keyringController.persistAllKeyrings.bind(keyringController, password))
- .then(() => password)
- }
-
- checkIfShouldMigrate() {
- return !!this.configManager.getWallet() && !this.configManager.getVault()
- }
-
restoreOldVaultAccounts(migratorOutput) {
const { serialized } = migratorOutput
return this.keyringController.restoreKeyring(serialized)
@@ -620,7 +599,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.currencyController.updateConversionRate()
const data = {
conversionRate: this.currencyController.getConversionRate(),
- currentFiat: this.currencyController.getCurrentCurrency(),
+ currentCurrency: this.currencyController.getCurrentCurrency(),
conversionDate: this.currencyController.getConversionDate(),
}
cb(null, data)
diff --git a/app/scripts/migrations/010.js b/app/scripts/migrations/010.js
index 48a841bc1..c0cc56ae4 100644
--- a/app/scripts/migrations/010.js
+++ b/app/scripts/migrations/010.js
@@ -2,7 +2,7 @@ const version = 10
/*
-This migration breaks out the CurrencyController substate
+This migration breaks out the ShapeShiftController substate
*/
diff --git a/app/scripts/migrations/011.js b/app/scripts/migrations/011.js
index bf283ef98..0d5d6d307 100644
--- a/app/scripts/migrations/011.js
+++ b/app/scripts/migrations/011.js
@@ -2,7 +2,7 @@ const version = 11
/*
-This migration breaks out the CurrencyController substate
+This migration removes the discaimer state from our app, which was integrated into our notices.
*/
diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js
index c6cfdf11d..31c1c8431 100644
--- a/app/scripts/transaction-manager.js
+++ b/app/scripts/transaction-manager.js
@@ -172,7 +172,10 @@ module.exports = class TransactionManager extends EventEmitter {
], (err) => {
self.nonceLock.leave()
if (err) {
- this.setTxStatusFailed(txId)
+ this.setTxStatusFailed(txId, {
+ errCode: err.errCode || err,
+ message: err.message || 'Transaction failed during approval',
+ })
return cb(err)
}
cb()
@@ -291,7 +294,10 @@ module.exports = class TransactionManager extends EventEmitter {
this._setTxStatus(txId, 'confirmed')
}
- setTxStatusFailed (txId) {
+ setTxStatusFailed (txId, reason) {
+ let txMeta = this.getTx(txId)
+ txMeta.err = reason
+ this.updateTx(txMeta)
this._setTxStatus(txId, 'failed')
}
@@ -312,12 +318,11 @@ module.exports = class TransactionManager extends EventEmitter {
var txHash = txMeta.hash
var txId = txMeta.id
if (!txHash) {
- txMeta.err = {
+ let errReason = {
errCode: 'No hash was provided',
message: 'We had an error while submitting this transaction, please try again.',
}
- this.updateTx(txMeta)
- return this.setTxStatusFailed(txId)
+ return this.setTxStatusFailed(txId, errReason)
}
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
if (err || !txParams) {
diff --git a/development/states/account-detail-with-shapeshift-tx.json b/development/states/account-detail-with-shapeshift-tx.json
index c14062350..97d5e9f06 100644
--- a/development/states/account-detail-with-shapeshift-tx.json
+++ b/development/states/account-detail-with-shapeshift-tx.json
@@ -1,6 +1,6 @@
{
"metamask": {
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
diff --git a/development/states/account-detail-with-transaction-history.json b/development/states/account-detail-with-transaction-history.json
index 8cb495656..a6bcc2658 100644
--- a/development/states/account-detail-with-transaction-history.json
+++ b/development/states/account-detail-with-transaction-history.json
@@ -1,6 +1,6 @@
{
"metamask": {
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
diff --git a/development/states/account-detail.json b/development/states/account-detail.json
index 644e3674d..6d11c1deb 100644
--- a/development/states/account-detail.json
+++ b/development/states/account-detail.json
@@ -1,6 +1,6 @@
{
"metamask": {
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
diff --git a/development/states/account-list-with-imported.json b/development/states/account-list-with-imported.json
index b450b4fb8..41d586db6 100644
--- a/development/states/account-list-with-imported.json
+++ b/development/states/account-list-with-imported.json
@@ -14,7 +14,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 10.19458075,
"conversionDate": 1484696373,
"noActiveNotices": true,
diff --git a/development/states/accounts-loose.json b/development/states/accounts-loose.json
index 542e207d4..df51f0d7e 100644
--- a/development/states/accounts-loose.json
+++ b/development/states/accounts-loose.json
@@ -30,7 +30,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 0,
"conversionDate": "N/A",
"noActiveNotices": true,
diff --git a/development/states/accounts.json b/development/states/accounts.json
index 4eef1145f..c8ff40ed9 100644
--- a/development/states/accounts.json
+++ b/development/states/accounts.json
@@ -41,7 +41,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.84461814,
"conversionDate": 1476226414,
"accounts": {
diff --git a/development/states/compilation-bug.json b/development/states/compilation-bug.json
index b33e59c5c..588d069d4 100644
--- a/development/states/compilation-bug.json
+++ b/development/states/compilation-bug.json
@@ -41,7 +41,7 @@
"simulationFails": true
}
},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 7.69158136,
"conversionDate": 1482279663,
"noActiveNotices": true,
diff --git a/development/states/conf-tx.json b/development/states/conf-tx.json
index b44d23ad8..0f1a13751 100644
--- a/development/states/conf-tx.json
+++ b/development/states/conf-tx.json
@@ -48,7 +48,7 @@
"gasPrice": "4a817c800"
}
},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 12.7200827,
"conversionDate": 1487363041,
"noActiveNotices": true,
diff --git a/development/states/config.json b/development/states/config.json
index de1df1831..551c0e3fa 100644
--- a/development/states/config.json
+++ b/development/states/config.json
@@ -1,6 +1,6 @@
{
"metamask": {
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
diff --git a/development/states/first-time.json b/development/states/first-time.json
index 3554ee911..683a61fdf 100644
--- a/development/states/first-time.json
+++ b/development/states/first-time.json
@@ -6,7 +6,7 @@
"identities": {},
"frequentRpcList": [],
"unapprovedTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 12.7527416,
"conversionDate": 1487624341,
"noActiveNotices": false,
diff --git a/development/states/import-private-key-warning.json b/development/states/import-private-key-warning.json
index a2d33ed7f..80ebc650d 100644
--- a/development/states/import-private-key-warning.json
+++ b/development/states/import-private-key-warning.json
@@ -10,7 +10,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 10.1219126,
"conversionDate": 1484695442,
"noActiveNotices": true,
diff --git a/development/states/import-private-key.json b/development/states/import-private-key.json
index 73fbd68db..bd455c6d5 100644
--- a/development/states/import-private-key.json
+++ b/development/states/import-private-key.json
@@ -10,7 +10,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 10.10788584,
"conversionDate": 1484694362,
"noActiveNotices": true,
diff --git a/development/states/locked.json b/development/states/locked.json
index 8e6ed7860..866394e86 100644
--- a/development/states/locked.json
+++ b/development/states/locked.json
@@ -6,7 +6,7 @@
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.4379398,
"conversionDate": 1473358355,
"accounts": {},
diff --git a/development/states/lost-accounts.json b/development/states/lost-accounts.json
index dcba7a764..4f50092b0 100644
--- a/development/states/lost-accounts.json
+++ b/development/states/lost-accounts.json
@@ -1,6 +1,6 @@
{
"metamask": {
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"lostAccounts": [
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
diff --git a/development/states/new-account.json b/development/states/new-account.json
index a056e14e7..dc2341b0e 100644
--- a/development/states/new-account.json
+++ b/development/states/new-account.json
@@ -14,7 +14,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 10.92067835,
"conversionDate": 1478282884,
"network": null,
diff --git a/development/states/notice.json b/development/states/notice.json
index 1cdcefcfb..efeffab08 100644
--- a/development/states/notice.json
+++ b/development/states/notice.json
@@ -9,7 +9,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 8.3533002,
"conversionDate": 1481671082,
"noActiveNotices": false,
diff --git a/development/states/pending-signature.json b/development/states/pending-signature.json
index 0d49e0229..7deaee8f7 100644
--- a/development/states/pending-signature.json
+++ b/development/states/pending-signature.json
@@ -22,7 +22,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.02269525,
"conversionDate": 1472076963,
"accounts": {
diff --git a/development/states/pending-tx-insufficient.json b/development/states/pending-tx-insufficient.json
index 10ecc826d..18be84089 100644
--- a/development/states/pending-tx-insufficient.json
+++ b/development/states/pending-tx-insufficient.json
@@ -31,7 +31,7 @@
"maxCost": "de234b52e4a0800"
}
},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 12.59854817,
"conversionDate": 1487662141,
"noActiveNotices": true,
diff --git a/development/states/personal-sign.json b/development/states/personal-sign.json
index 2fc71f448..8ded6205c 100644
--- a/development/states/personal-sign.json
+++ b/development/states/personal-sign.json
@@ -18,7 +18,7 @@
}
},
"unapprovedTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 13.2126613,
"conversionDate": 1487888522,
"noActiveNotices": true,
diff --git a/development/states/private-key-export-success.json b/development/states/private-key-export-success.json
new file mode 100644
index 000000000..2ff3c4d17
--- /dev/null
+++ b/development/states/private-key-export-success.json
@@ -0,0 +1,70 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x07284e146926a4facd0ea60598dc4f001ad620f1": {
+ "address": "0x07284e146926a4facd0ea60598dc4f001ad620f1",
+ "name": "Account 1"
+ }
+ },
+ "unapprovedTxs": {},
+ "noActiveNotices": true,
+ "frequentRpcList": [],
+ "addressBook": [],
+ "network": "3",
+ "accounts": {
+ "0x07284e146926a4facd0ea60598dc4f001ad620f1": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x0",
+ "address": "0x07284e146926a4facd0ea60598dc4f001ad620f1"
+ }
+ },
+ "transactions": {},
+ "selectedAddressTxList": [],
+ "unapprovedMsgs": {},
+ "unapprovedMsgCount": 0,
+ "unapprovedPersonalMsgs": {},
+ "unapprovedPersonalMsgCount": 0,
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "07284e146926a4facd0ea60598dc4f001ad620f1"
+ ]
+ }
+ ],
+ "selectedAddress": "0x07284e146926a4facd0ea60598dc4f001ad620f1",
+ "currentCurrency": "USD",
+ "conversionRate": 43.35903476,
+ "conversionDate": 1490105102,
+ "provider": {
+ "type": "testnet"
+ },
+ "shapeShiftTxList": [],
+ "lostAccounts": [],
+ "seedWords": null
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accountDetail",
+ "context": "0x07284e146926a4facd0ea60598dc4f001ad620f1"
+ },
+ "accountDetail": {
+ "subview": "export",
+ "accountExport": "completed",
+ "privateKey": "549c9638ad06432568969accacad4a02f8548cc358085938071745138ec134b7"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/private-key-export.json b/development/states/private-key-export.json
new file mode 100644
index 000000000..db7a53e22
--- /dev/null
+++ b/development/states/private-key-export.json
@@ -0,0 +1,69 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x07284e146926a4facd0ea60598dc4f001ad620f1": {
+ "address": "0x07284e146926a4facd0ea60598dc4f001ad620f1",
+ "name": "Account 1"
+ }
+ },
+ "unapprovedTxs": {},
+ "noActiveNotices": true,
+ "frequentRpcList": [],
+ "addressBook": [],
+ "network": "3",
+ "accounts": {
+ "0x07284e146926a4facd0ea60598dc4f001ad620f1": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x0",
+ "address": "0x07284e146926a4facd0ea60598dc4f001ad620f1"
+ }
+ },
+ "transactions": {},
+ "selectedAddressTxList": [],
+ "unapprovedMsgs": {},
+ "unapprovedMsgCount": 0,
+ "unapprovedPersonalMsgs": {},
+ "unapprovedPersonalMsgCount": 0,
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "07284e146926a4facd0ea60598dc4f001ad620f1"
+ ]
+ }
+ ],
+ "selectedAddress": "0x07284e146926a4facd0ea60598dc4f001ad620f1",
+ "currentCurrency": "USD",
+ "conversionRate": 43.35903476,
+ "conversionDate": 1490105102,
+ "provider": {
+ "type": "testnet"
+ },
+ "shapeShiftTxList": [],
+ "lostAccounts": [],
+ "seedWords": null
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accountDetail",
+ "context": "0x07284e146926a4facd0ea60598dc4f001ad620f1"
+ },
+ "accountDetail": {
+ "subview": "export",
+ "accountExport": "requested"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/private-network.json b/development/states/private-network.json
index 155aa6584..8cdcb4eeb 100644
--- a/development/states/private-network.json
+++ b/development/states/private-network.json
@@ -16,7 +16,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 9.52855776,
"conversionDate": 1479756513,
"accounts": {
diff --git a/development/states/restore-vault.json b/development/states/restore-vault.json
index ad136c78e..0d6c2610d 100644
--- a/development/states/restore-vault.json
+++ b/development/states/restore-vault.json
@@ -6,7 +6,7 @@
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 0,
"conversionDate": "N/A",
"accounts": {},
diff --git a/development/states/send.json b/development/states/send.json
index 3f52185af..73ac62f65 100644
--- a/development/states/send.json
+++ b/development/states/send.json
@@ -22,7 +22,7 @@
}
},
"unapprovedTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 16.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
diff --git a/development/states/shapeshift.json b/development/states/shapeshift.json
index bc10143d0..bfd4b7c16 100644
--- a/development/states/shapeshift.json
+++ b/development/states/shapeshift.json
@@ -22,7 +22,7 @@
}
},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 11.21274318,
"conversionDate": 1472159644,
"accounts": {
diff --git a/development/states/terms-and-conditions.json b/development/states/terms-and-conditions.json
index b995e446f..f5ebe8254 100644
--- a/development/states/terms-and-conditions.json
+++ b/development/states/terms-and-conditions.json
@@ -5,7 +5,7 @@
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {},
"unconfTxs": {},
- "currentFiat": "USD",
+ "currentCurrency": "USD",
"conversionRate": 8.18703468,
"conversionDate": 1481755832,
"network": "3",
diff --git a/package.json b/package.json
index 1d19d5e75..c08d92339 100644
--- a/package.json
+++ b/package.json
@@ -53,7 +53,6 @@
"ensnare": "^1.0.0",
"eth-bin-to-ops": "^1.0.1",
"eth-hd-keyring": "^1.1.1",
- "eth-lightwallet": "^2.3.3",
"eth-query": "^1.0.3",
"eth-sig-util": "^1.1.1",
"eth-simple-keyring": "^1.1.1",
@@ -99,7 +98,7 @@
"react-tooltip-component": "^0.3.0",
"readable-stream": "^2.1.2",
"redux": "^3.0.5",
- "redux-logger": "^2.3.1",
+ "redux-logger": "^2.10.2",
"redux-thunk": "^1.0.2",
"request-promise": "^4.1.1",
"sandwich-expando": "^1.0.5",
diff --git a/test/integration/lib/idStore-migrator-test.js b/test/integration/lib/idStore-migrator-test.js
deleted file mode 100644
index 290216ae8..000000000
--- a/test/integration/lib/idStore-migrator-test.js
+++ /dev/null
@@ -1,92 +0,0 @@
-const ObservableStore = require('obs-store')
-const ConfigManager = require('../../../app/scripts/lib/config-manager')
-const IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator')
-const SimpleKeyring = require('eth-simple-keyring')
-const normalize = require('eth-sig-util').normalize
-
-const oldStyleVault = require('../mocks/oldVault.json').data
-const badStyleVault = require('../mocks/badVault.json').data
-
-const PASSWORD = '12345678'
-const FIRST_ADDRESS = '0x4dd5d356c5A016A220bCD69e82e5AF680a430d00'.toLowerCase()
-const BAD_STYLE_FIRST_ADDRESS = '0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9'
-const SEED = 'fringe damage bounce extend tunnel afraid alert sound all soldier all dinner'
-
-QUnit.module('Old Style Vaults', {
- beforeEach: function () {
- let managers = managersFromInitState(oldStyleVault)
-
- this.configManager = managers.configManager
- this.migrator = managers.migrator
- }
-})
-
-QUnit.test('migrator:isInitialized', function (assert) {
- assert.ok(this.migrator)
-})
-
-QUnit.test('migrator:migratedVaultForPassword', function (assert) {
- var done = assert.async()
-
- this.migrator.migratedVaultForPassword(PASSWORD)
- .then((result) => {
- assert.ok(result, 'migratedVaultForPassword returned result')
- const { serialized, lostAccounts } = result
- assert.equal(serialized.data.mnemonic, SEED, 'seed phrase recovered')
- assert.equal(lostAccounts.length, 0, 'no lost accounts')
- done()
- })
-})
-
-QUnit.module('Old Style Vaults with bad HD seed', {
- beforeEach: function () {
- let managers = managersFromInitState(badStyleVault)
-
- this.configManager = managers.configManager
- this.migrator = managers.migrator
- }
-})
-
-QUnit.test('migrator:migratedVaultForPassword', function (assert) {
- var done = assert.async()
-
- this.migrator.migratedVaultForPassword(PASSWORD)
- .then((result) => {
- assert.ok(result, 'migratedVaultForPassword returned result')
- const { serialized, lostAccounts } = result
-
- assert.equal(lostAccounts.length, 1, 'one lost account')
- assert.equal(lostAccounts[0].address, '0xe15D894BeCB0354c501AE69429B05143679F39e0'.toLowerCase())
- assert.ok(lostAccounts[0].privateKey, 'private key exported')
-
- 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)
- })
- })
-})
-
-function managersFromInitState(initState){
-
- let configManager = new ConfigManager({
- store: new ObservableStore(initState),
- })
-
- let migrator = new IdStoreMigrator({
- configManager: configManager,
- })
-
- return { configManager, migrator }
-}
diff --git a/test/unit/address-book-controller.js b/test/unit/address-book-controller.js
new file mode 100644
index 000000000..f345b0328
--- /dev/null
+++ b/test/unit/address-book-controller.js
@@ -0,0 +1,56 @@
+const assert = require('assert')
+const extend = require('xtend')
+const AddressBookController = require('../../app/scripts/controllers/address-book')
+
+const mockKeyringController = {
+ memStore: {
+ getState: function () {
+ return {
+ identities: {
+ '0x0aaa' : {
+ address: '0x0aaa',
+ name: 'owned',
+ }
+ }
+ }
+ }
+ }
+}
+
+
+describe('address-book-controller', function() {
+ var addressBookController
+
+ beforeEach(function() {
+ addressBookController = new AddressBookController({}, mockKeyringController)
+ })
+
+ describe('addres book management', function () {
+ describe('#_getAddressBook', function () {
+ it('should be empty by default.', function () {
+ assert.equal(addressBookController._getAddressBook().length, 0)
+ })
+ })
+ describe('#setAddressBook', function () {
+ it('should properly set a new address.', function () {
+ addressBookController.setAddressBook('0x01234', 'test')
+ var addressBook = addressBookController._getAddressBook()
+ assert.equal(addressBook.length, 1, 'incorrect address book length.')
+ assert.equal(addressBook[0].address, '0x01234', 'incorrect addresss')
+ assert.equal(addressBook[0].name, 'test', 'incorrect nickname')
+ })
+
+ it('should reject duplicates.', function () {
+ addressBookController.setAddressBook('0x01234', 'test')
+ addressBookController.setAddressBook('0x01234', 'test')
+ var addressBook = addressBookController._getAddressBook()
+ assert.equal(addressBook.length, 1, 'incorrect address book length.')
+ })
+ it('should not add any identities that are under user control', function () {
+ addressBookController.setAddressBook('0x0aaa', ' ')
+ var addressBook = addressBookController._getAddressBook()
+ assert.equal(addressBook.length, 0, 'incorrect address book length.')
+ })
+ })
+ })
+})
diff --git a/test/unit/currency-controller-test.js b/test/unit/currency-controller-test.js
index dd7fa91e0..079f8b488 100644
--- a/test/unit/currency-controller-test.js
+++ b/test/unit/currency-controller-test.js
@@ -7,7 +7,7 @@ const rp = require('request-promise')
const nock = require('nock')
const CurrencyController = require('../../app/scripts/controllers/currency')
-describe('config-manager', function() {
+describe('currency-controller', function() {
var currencyController
beforeEach(function() {
diff --git a/test/unit/id-management-test.js b/test/unit/id-management-test.js
deleted file mode 100644
index cbc6403bc..000000000
--- a/test/unit/id-management-test.js
+++ /dev/null
@@ -1,35 +0,0 @@
-var assert = require('assert')
-var IdManagement = require('../../app/scripts/lib/id-management')
-var sinon = require('sinon')
-
-describe('IdManagement', function() {
-
- beforeEach(function() {
- // sinon allows stubbing methods that are easily verified
- this.sinon = sinon.sandbox.create()
- window.localStorage = {} // Hacking localStorage support into JSDom
- })
-
- afterEach(function() {
- // sinon requires cleanup otherwise it will overwrite context
- this.sinon.restore()
- })
-
- describe('#signMsg', function () {
- it('passes the dennis test', function() {
- const address = '0x9858e7d8b79fc3e6d989636721584498926da38a'
- const message = '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'
- const privateKey = '0x7dd98753d7b4394095de7d176c58128e2ed6ee600abe97c9f6d9fd65015d9b18'
- const expectedResult = '0x28fcb6768e5110144a55b2e6ce9d1ea5a58103033632d272d2b5cf506906f7941a00b539383fd872109633d8c71c404e13dba87bc84166ee31b0e36061a69e161c'
-
- const idManagement = new IdManagement()
- const exportKeyStub = sinon.stub(idManagement, 'exportPrivateKey', (addr) => {
- assert.equal(addr, address)
- return privateKey
- })
-
- const result = idManagement.signMsg(address, message)
- assert.equal(result, expectedResult)
- })
- })
-})
diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js
deleted file mode 100644
index 81a99ef63..000000000
--- a/test/unit/idStore-migration-test.js
+++ /dev/null
@@ -1,83 +0,0 @@
-const async = require('async')
-const assert = require('assert')
-const ObservableStore = require('obs-store')
-const ethUtil = require('ethereumjs-util')
-const BN = ethUtil.BN
-const ConfigManager = require('../../app/scripts/lib/config-manager')
-const firstTimeState = require('../../app/scripts/first-time-state')
-const delegateCallCode = require('../lib/example-code.json').delegateCallCode
-const clone = require('clone')
-
-// The old way:
-const IdentityStore = require('../../app/scripts/lib/idStore')
-const STORAGE_KEY = 'metamask-config'
-
-// The new ways:
-var KeyringController = require('../../app/scripts/keyring-controller')
-const mockEncryptor = require('../lib/mock-encryptor')
-const MockSimpleKeychain = require('../lib/mock-simple-keychain')
-const sinon = require('sinon')
-
-const mockVault = {
- seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague',
- account: '0x5d8de92c205279c10e5669f797b853ccef4f739a',
-}
-
-const badVault = {
- seed: 'radar blur cabbage chef fix engine embark joy scheme fiction master release',
-}
-
-describe('IdentityStore to KeyringController migration', function() {
-
- // The stars of the show:
- let idStore, keyringController, seedWords, configManager
-
- let password = 'password123'
- let entropy = 'entripppppyy duuude'
- let accounts = []
- let newAccounts = []
- let originalKeystore
-
- // This is a lot of setup, I know!
- // We have to create an old style vault, populate it,
- // and THEN create a new one, before we can run tests on it.
- beforeEach(function(done) {
- this.sinon = sinon.sandbox.create()
- let store = new ObservableStore(clone(firstTimeState))
- configManager = new ConfigManager({ store })
-
- idStore = new IdentityStore({
- configManager: configManager,
- ethStore: {
- addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
- del(acct) { delete accounts[acct] },
- },
- })
-
- idStore._createVault(password, mockVault.seed, (err) => {
- assert.ifError(err, 'createNewVault threw error')
- originalKeystore = idStore._idmgmt.keyStore
-
- idStore.setLocked((err) => {
- assert.ifError(err, 'createNewVault threw error')
- keyringController = new KeyringController({
- configManager,
- ethStore: {
- addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) },
- del(acct) { delete newAccounts[acct] },
- },
- txManager: {
- getTxList: () => [],
- getUnapprovedTxList: () => []
- },
- })
-
- // Stub out the browser crypto for a mock encryptor.
- // Browser crypto is tested in the integration test suite.
- keyringController.encryptor = mockEncryptor
- done()
- })
- })
- })
-
-})
diff --git a/test/unit/idStore-test.js b/test/unit/idStore-test.js
deleted file mode 100644
index 000c58a82..000000000
--- a/test/unit/idStore-test.js
+++ /dev/null
@@ -1,142 +0,0 @@
-const async = require('async')
-const assert = require('assert')
-const ethUtil = require('ethereumjs-util')
-const BN = ethUtil.BN
-const configManagerGen = require('../lib/mock-config-manager')
-const delegateCallCode = require('../lib/example-code.json').delegateCallCode
-const IdentityStore = require('../../app/scripts/lib/idStore')
-
-describe('IdentityStore', function() {
-
- describe('#createNewVault', function () {
- let idStore
- let password = 'password123'
- let seedWords
- let accounts = []
- let originalKeystore
-
- before(function(done) {
- window.localStorage = {} // Hacking localStorage support into JSDom
-
- idStore = new IdentityStore({
- configManager: configManagerGen(),
- ethStore: {
- addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
- },
- })
-
- idStore.createNewVault(password, (err, seeds) => {
- assert.ifError(err, 'createNewVault threw error')
- seedWords = seeds
- originalKeystore = idStore._idmgmt.keyStore
- done()
- })
- })
-
- describe('#recoverFromSeed', function() {
- let newAccounts = []
-
- before(function() {
- window.localStorage = {} // Hacking localStorage support into JSDom
-
- idStore = new IdentityStore({
- configManager: configManagerGen(),
- ethStore: {
- addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) },
- },
- })
- })
-
- it('should return the expected keystore', function (done) {
-
- idStore.recoverFromSeed(password, seedWords, (err) => {
- assert.ifError(err)
-
- let newKeystore = idStore._idmgmt.keyStore
- assert.equal(newAccounts[0], accounts[0])
- done()
- })
- })
- })
- })
-
- describe('#recoverFromSeed BIP44 compliance', function() {
- const salt = 'lightwalletSalt'
-
- let password = 'secret!'
- let accounts = {}
- let idStore
-
- var assertions = [
- {
- seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague',
- account: '0x5d8de92c205279c10e5669f797b853ccef4f739a',
- },
- {
- seed: 'radar blur cabbage chef fix engine embark joy scheme fiction master release',
- account: '0xe15d894becb0354c501ae69429b05143679f39e0',
- },
- {
- seed: 'phone coyote caught pattern found table wedding list tumble broccoli chief swing',
- account: '0xb0e868f24bc7fec2bce2efc2b1c344d7569cd9d2',
- },
- {
- seed: 'recycle tag bird palace blue village anxiety census cook soldier example music',
- account: '0xab34a45920afe4af212b96ec51232aaa6a33f663',
- },
- {
- seed: 'half glimpse tape cute harvest sweet bike voyage actual floor poet lazy',
- account: '0x28e9044597b625ac4beda7250011670223de43b2',
- },
- {
- seed: 'flavor tiger carpet motor angry hungry document inquiry large critic usage liar',
- account: '0xb571be96558940c4e9292e1999461aa7499fb6cd',
- },
- ]
-
- before(function() {
- window.localStorage = {} // Hacking localStorage support into JSDom
-
- idStore = new IdentityStore({
- configManager: configManagerGen(),
- ethStore: {
- addAccount(acct) { accounts[acct] = acct},
- del(acct) { delete accounts[acct] },
- },
- })
- })
-
- it('should enforce seed compliance with TestRPC', function (done) {
- this.timeout(10000)
- const tests = assertions.map((assertion) => {
- return function (cb) {
-
- idStore.recoverFromSeed(password, assertion.seed, (err) => {
- assert.ifError(err)
-
- var expected = assertion.account.toLowerCase()
- var received = accounts[expected].toLowerCase()
- assert.equal(received, expected)
-
- idStore.tryPassword(password, function (err) {
-
- assert.ok(idStore._isUnlocked(), 'should unlock the id store')
-
- idStore.submitPassword(password, function(err, account) {
- assert.ifError(err)
- assert.equal(account, expected)
- assert.equal(Object.keys(idStore._getAddresses()).length, 1, 'only one account on restore')
- cb()
- })
- })
- })
- }
- })
-
- async.series(tests, function(err, results) {
- assert.ifError(err)
- done()
- })
- })
- })
-})
diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js
index 1c2b331d4..5ed31ab0a 100644
--- a/ui/app/accounts/import/json.js
+++ b/ui/app/accounts/import/json.js
@@ -60,7 +60,7 @@ JsonImportSubview.prototype.render = function () {
},
}, 'Import'),
- error ? h('span.warning', error) : null,
+ error ? h('span.error', error) : null,
])
)
}
@@ -95,4 +95,3 @@ JsonImportSubview.prototype.createNewKeychain = function () {
this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ]))
}
-
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
index b139a0374..68ccee58e 100644
--- a/ui/app/accounts/import/private-key.js
+++ b/ui/app/accounts/import/private-key.js
@@ -48,7 +48,7 @@ PrivateKeyImportView.prototype.render = function () {
},
}, 'Import'),
- error ? h('span.warning', error) : null,
+ error ? h('span.error', error) : null,
])
)
}
@@ -65,4 +65,3 @@ PrivateKeyImportView.prototype.createNewKeychain = function () {
const privateKey = input.value
this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
}
-
diff --git a/ui/app/actions.js b/ui/app/actions.js
index d4fd7553b..d02b7dcaa 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -71,10 +71,12 @@ var actions = {
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
- setCurrentFiat: setCurrentFiat,
+ setCurrentCurrency: setCurrentCurrency,
// account detail screen
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
showSendPage: showSendPage,
+ ADD_TO_ADDRESS_BOOK: 'ADD_TO_ADDRESS_BOOK',
+ addToAddressBook: addToAddressBook,
REQUEST_ACCOUNT_EXPORT: 'REQUEST_ACCOUNT_EXPORT',
requestExportAccount: requestExportAccount,
EXPORT_ACCOUNT: 'EXPORT_ACCOUNT',
@@ -267,11 +269,12 @@ function requestRevealSeed (password) {
dispatch(actions.showLoadingIndication())
log.debug(`background.submitPassword`)
background.submitPassword(password, (err) => {
- if (err) return dispatch(actions.displayWarning(err.message))
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
log.debug(`background.placeSeedWords`)
background.placeSeedWords((err) => {
if (err) return dispatch(actions.displayWarning(err.message))
- dispatch(actions.hideLoadingIndication())
})
})
}
@@ -294,10 +297,10 @@ function importNewAccount (strategy, args) {
dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
log.debug(`background.importAccountWithStrategy`)
background.importAccountWithStrategy(strategy, args, (err) => {
- dispatch(actions.hideLoadingIndication())
if (err) return dispatch(actions.displayWarning(err.message))
log.debug(`background.getState`)
background.getState((err, newState) => {
+ dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.displayWarning(err.message))
}
@@ -328,10 +331,10 @@ function showInfoPage () {
}
}
-function setCurrentFiat (currencyCode) {
+function setCurrentCurrency (currencyCode) {
return (dispatch) => {
dispatch(this.showLoadingIndication())
- log.debug(`background.setCurrentFiat`)
+ log.debug(`background.setCurrentCurrency`)
background.setCurrentCurrency(currencyCode, (err, data) => {
dispatch(this.hideLoadingIndication())
if (err) {
@@ -341,7 +344,7 @@ function setCurrentFiat (currencyCode) {
dispatch({
type: this.SET_CURRENT_FIAT,
value: {
- currentFiat: data.currentFiat,
+ currentCurrency: data.currentCurrency,
conversionRate: data.conversionRate,
conversionDate: data.conversionDate,
},
@@ -696,6 +699,19 @@ function setRpcTarget (newRpc) {
}
}
+// Calls the addressBookController to add a new address.
+function addToAddressBook (recipient, nickname) {
+ log.debug(`background.addToAddressBook`)
+ return (dispatch) => {
+ background.setAddressBook(recipient, nickname, (err, result) => {
+ if (err) {
+ log.error(err)
+ return dispatch(self.displayWarning('Address book failed to update'))
+ }
+ })
+ }
+}
+
function setProviderType (type) {
log.debug(`background.setProviderType`)
background.setProviderType(type)
@@ -757,22 +773,30 @@ function requestExportAccount () {
}
}
-function exportAccount (address) {
+function exportAccount (password, address) {
var self = this
return function (dispatch) {
dispatch(self.showLoadingIndication())
- log.debug(`background.exportAccount`)
- background.exportAccount(address, function (err, result) {
- dispatch(self.hideLoadingIndication())
-
+ log.debug(`background.submitPassword`)
+ background.submitPassword(password, function (err) {
if (err) {
- log.error(err)
- return dispatch(self.displayWarning('Had a problem exporting the account.'))
+ log.error('Error in submiting password.')
+ dispatch(self.hideLoadingIndication())
+ return dispatch(self.displayWarning('Incorrect Password.'))
}
+ log.debug(`background.exportAccount`)
+ background.exportAccount(address, function (err, result) {
+ dispatch(self.hideLoadingIndication())
- dispatch(self.showPrivateKey(result))
+ if (err) {
+ log.error(err)
+ return dispatch(self.displayWarning('Had a problem exporting the account.'))
+ }
+
+ dispatch(self.showPrivateKey(result))
+ })
})
}
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 2bc92b54c..5a7596aca 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -256,6 +256,15 @@ App.prototype.renderNetworkDropdown = function () {
}),
h(DropMenuItem, {
+ label: 'Kovan Test Network',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false}),
+ action: () => props.dispatch(actions.setProviderType('kovan')),
+ icon: h('.menu-icon.hollow-diamond'),
+ activeNetworkRender: props.network,
+ provider: props.provider,
+ }),
+
+ h(DropMenuItem, {
label: 'Localhost 8545',
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setDefaultRpcTarget(rpcList)),
@@ -266,7 +275,7 @@ App.prototype.renderNetworkDropdown = function () {
this.renderCustomOption(props.provider),
this.renderCommonRpc(rpcList, props.provider),
- props.isUnlocked && h(DropMenuItem, {
+ h(DropMenuItem, {
label: 'Custom RPC',
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => this.props.dispatch(actions.showConfigPage()),
@@ -400,6 +409,10 @@ App.prototype.renderPrimary = function () {
log.debug('rendering restore vault screen')
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
+ case 'config':
+ log.debug('rendering config screen from unlock screen.')
+ return h(ConfigScreen, {key: 'config'})
+
default:
log.debug('rendering locked screen')
return h(UnlockScreen, {key: 'locked'})
diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js
index 6d8b099a5..888196c5d 100644
--- a/ui/app/components/account-export.js
+++ b/ui/app/components/account-export.js
@@ -4,14 +4,21 @@ const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
const ethUtil = require('ethereumjs-util')
+const connect = require('react-redux').connect
-module.exports = ExportAccountView
+module.exports = connect(mapStateToProps)(ExportAccountView)
inherits(ExportAccountView, Component)
function ExportAccountView () {
Component.call(this)
}
+function mapStateToProps (state) {
+ return {
+ warning: state.appState.warning,
+ }
+}
+
ExportAccountView.prototype.render = function () {
console.log('EXPORT VIEW')
console.dir(this.props)
@@ -28,35 +35,58 @@ ExportAccountView.prototype.render = function () {
if (notExporting) return h('div')
if (exportRequested) {
- var warning = `Exporting your private key is very dangerous,
- and you should only do it if you know what you're doing.`
- var confirmation = `If you're absolutely sure, type "I understand" below and
- submit.`
+ var warning = `Export private keys at your own risk.`
return (
-
h('div', {
- key: 'exporting',
style: {
- margin: '0 20px',
+ display: 'inline-block',
+ textAlign: 'center',
},
- }, [
- h('p.error', warning),
- h('p', confirmation),
- h('input#exportAccount.sizing-input', {
- onKeyPress: this.onExportKeyPress.bind(this),
- style: {
- position: 'relative',
- top: '1.5px',
+ },
+ [
+ h('div', {
+ key: 'exporting',
+ style: {
+ margin: '0 20px',
+ },
+ }, [
+ h('p.error', warning),
+ h('input#exportAccount.sizing-input', {
+ type: 'password',
+ placeholder: 'confirm password',
+ onKeyPress: this.onExportKeyPress.bind(this),
+ style: {
+ position: 'relative',
+ top: '1.5px',
+ marginBottom: '7px',
+ },
+ }),
+ ]),
+ h('div', {
+ key: 'buttons',
+ style: {
+ margin: '0 20px',
+ },
},
- }),
- h('button', {
- onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
- }, 'Submit'),
- h('button', {
- onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, 'Cancel'),
- ])
-
+ [
+ h('button', {
+ onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
+ style: {
+ marginRight: '10px',
+ },
+ }, 'Submit'),
+ h('button', {
+ onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
+ }, 'Cancel'),
+ ]),
+ (this.props.warning) && (
+ h('span.error', {
+ style: {
+ margin: '20px',
+ },
+ }, this.props.warning.split('-'))
+ ),
+ ])
)
}
@@ -89,15 +119,6 @@ ExportAccountView.prototype.onExportKeyPress = function (event) {
if (event.key !== 'Enter') return
event.preventDefault()
- var input = document.getElementById('exportAccount')
- if (input.value === 'I understand') {
- this.props.dispatch(actions.exportAccount(this.props.address))
- } else {
- input.value = ''
- input.placeholder = 'Please retype "I understand" exactly.'
- }
-}
-
-ExportAccountView.prototype.exportAccount = function (address) {
- this.props.dispatch(actions.exportAccount(address))
+ var input = document.getElementById('exportAccount').value
+ this.props.dispatch(actions.exportAccount(input, this.props.address))
}
diff --git a/ui/app/components/binary-renderer.js b/ui/app/components/binary-renderer.js
index a9d49b128..0b6a1f5c2 100644
--- a/ui/app/components/binary-renderer.js
+++ b/ui/app/components/binary-renderer.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
+const extend = require('xtend')
module.exports = BinaryRenderer
@@ -12,20 +13,22 @@ function BinaryRenderer () {
BinaryRenderer.prototype.render = function () {
const props = this.props
- const { value } = props
+ const { value, style } = props
const text = this.hexToText(value)
+ const defaultStyle = extend({
+ width: '315px',
+ maxHeight: '210px',
+ resize: 'none',
+ border: 'none',
+ background: 'white',
+ padding: '3px',
+ }, style)
+
return (
h('textarea.font-small', {
readOnly: true,
- style: {
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- },
+ style: defaultStyle,
defaultValue: text,
})
)
diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js
index 9f002234e..3eb6ec876 100644
--- a/ui/app/components/drop-menu-item.js
+++ b/ui/app/components/drop-menu-item.js
@@ -42,7 +42,10 @@ DropMenuItem.prototype.activeNetworkRender = function () {
if (providerType === 'mainnet') return h('.check', '✓')
break
case 'Ropsten Test Network':
- if (provider.type === 'testnet') return h('.check', '✓')
+ if (providerType === 'testnet') return h('.check', '✓')
+ break
+ case 'Kovan Test Network':
+ if (providerType === 'kovan') return h('.check', '✓')
break
case 'Localhost 8545':
if (activeNetwork === 'http://localhost:8545') return h('.check', '✓')
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index ffc4eab4a..facf29d97 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -21,6 +21,7 @@ function EnsInput () {
EnsInput.prototype.render = function () {
const props = this.props
const opts = extend(props, {
+ list: 'addresses',
onChange: () => {
const network = this.props.network
let resolverAddress = networkResolvers[network]
@@ -46,6 +47,25 @@ EnsInput.prototype.render = function () {
style: { width: '100%' },
}, [
h('input.large-input', opts),
+ // The address book functionality.
+ h('datalist#addresses',
+ [
+ // Corresponds to the addresses owned.
+ Object.keys(props.identities).map((key) => {
+ let identity = props.identities[key]
+ return h('option', {
+ value: identity.address,
+ label: identity.name,
+ })
+ }),
+ // Corresponds to previously sent-to addresses.
+ props.addressBook.map((identity) => {
+ return h('option', {
+ value: identity.address,
+ label: identity.name,
+ })
+ }),
+ ]),
this.ensIcon(),
])
}
@@ -80,11 +100,13 @@ EnsInput.prototype.lookupEnsName = function () {
this.setState({
loadingEns: false,
ensResolution: address,
+ nickname: recipient.trim(),
hoverText: address + '\nClick to Copy',
})
}
})
.catch((reason) => {
+ log.error(reason)
return this.setState({
loadingEns: false,
ensFailure: true,
@@ -95,10 +117,13 @@ EnsInput.prototype.lookupEnsName = function () {
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
const state = this.state || {}
- const { ensResolution } = state
+ const ensResolution = state.ensResolution
+ // If an address is sent without a nickname, meaning not from ENS or from
+ // the user's own accounts, a default of a one-space string is used.
+ const nickname = state.nickname || ' '
if (ensResolution && this.props.onChange &&
ensResolution !== prevState.ensResolution) {
- this.props.onChange(ensResolution)
+ this.props.onChange(ensResolution, nickname)
}
}
diff --git a/ui/app/components/fiat-value.js b/ui/app/components/fiat-value.js
index 13ee48245..298809b30 100644
--- a/ui/app/components/fiat-value.js
+++ b/ui/app/components/fiat-value.js
@@ -9,7 +9,7 @@ module.exports = connect(mapStateToProps)(FiatValue)
function mapStateToProps (state) {
return {
conversionRate: state.metamask.conversionRate,
- currentFiat: state.metamask.currentFiat,
+ currentCurrency: state.metamask.currentCurrency,
}
}
@@ -34,7 +34,7 @@ FiatValue.prototype.render = function () {
fiatTooltipNumber = 'Unknown'
}
- var fiatSuffix = props.currentFiat
+ var fiatSuffix = props.currentCurrency
return fiatDisplay(fiatDisplayNumber, fiatSuffix)
}
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 77805fd57..d9045167f 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -40,6 +40,9 @@ Network.prototype.render = function () {
} else if (parseInt(networkNumber) === 3) {
hoverText = 'Ropsten Test Network'
iconName = 'ropsten-test-network'
+ } else if (providerName === 'kovan') {
+ hoverText = 'Kovan Test Network'
+ iconName = 'kovan-test-network'
} else {
hoverText = 'Unknown Private Network'
iconName = 'unknown-private-network'
@@ -70,6 +73,15 @@ Network.prototype.render = function () {
}},
'Ropsten Test Net'),
])
+ case 'kovan-test-network':
+ return h('.network-indicator', [
+ h('.menu-icon.hollow-diamond'),
+ h('.network-name', {
+ style: {
+ color: '#690496',
+ }},
+ 'Kovan Test Net'),
+ ])
default:
return h('.network-indicator', [
h('i.fa.fa-question-circle.fa-lg', {
diff --git a/ui/app/components/pending-personal-msg-details.js b/ui/app/components/pending-personal-msg-details.js
index fa2c6416c..1050513f2 100644
--- a/ui/app/components/pending-personal-msg-details.js
+++ b/ui/app/components/pending-personal-msg-details.js
@@ -40,9 +40,18 @@ PendingMsgDetails.prototype.render = function () {
}),
// message data
- h('div', [
+ h('div', {
+ style: {
+ height: '260px',
+ },
+ }, [
h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
- h(BinaryRenderer, { value: data }),
+ h(BinaryRenderer, {
+ value: data,
+ style: {
+ height: '215px',
+ },
+ }),
]),
])
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
index 44d2dc587..ee32be7d3 100644
--- a/ui/app/components/transaction-list-item.js
+++ b/ui/app/components/transaction-list-item.js
@@ -28,7 +28,7 @@ TransactionListItem.prototype.render = function () {
let isLinkable = false
const numericNet = parseInt(network)
- isLinkable = numericNet === 1 || numericNet === 3
+ isLinkable = numericNet === 1 || numericNet === 3 || numericNet === 42
var isMsg = ('msgParams' in transaction)
var isTx = ('txParams' in transaction)
diff --git a/ui/app/config.js b/ui/app/config.js
index 00a4cba88..444365de2 100644
--- a/ui/app/config.js
+++ b/ui/app/config.js
@@ -125,19 +125,19 @@ function rpcValidation (newRpc, state) {
}
function currentConversionInformation (metamaskState, state) {
- var currentFiat = metamaskState.currentFiat
+ var currentCurrency = metamaskState.currentCurrency
var conversionDate = metamaskState.conversionDate
return h('div', [
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
- h('select#currentFiat', {
+ h('select#currentCurrency', {
onChange (event) {
event.preventDefault()
- var element = document.getElementById('currentFiat')
- var newFiat = element.value
- state.dispatch(actions.setCurrentFiat(newFiat))
+ var element = document.getElementById('currentCurrency')
+ var newCurrency = element.value
+ state.dispatch(actions.setCurrentCurrency(newCurrency))
},
- defaultValue: currentFiat,
+ defaultValue: currentCurrency,
}, currencies.map((currency) => {
return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`)
})
@@ -161,6 +161,11 @@ function currentProviderDisplay (metamaskState) {
value = 'Ropsten Test Network'
break
+ case 'kovan':
+ title = 'Current Network'
+ value = 'Kovan Test Network'
+ break
+
default:
title = 'Current RPC'
value = metamaskState.provider.rpcTarget
diff --git a/ui/app/css/index.css b/ui/app/css/index.css
index 8c6ff29d3..3ec0ac5c5 100644
--- a/ui/app/css/index.css
+++ b/ui/app/css/index.css
@@ -266,8 +266,9 @@ app sections
}
.sizing-input{
- font-size: 1em;
+ font-size: 14px;
height: 30px;
+ padding-left: 5px;
}
.editable-label{
display: flex;
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index 99c6f1b9d..670dc9fd0 100644
--- a/ui/app/css/lib.css
+++ b/ui/app/css/lib.css
@@ -188,7 +188,7 @@ hr.horizontal-line {
.hollow-diamond {
transform: rotate(45deg);
- border: 1px solid #038789;
+ border: 3px solid #690496;
}
.pending-dot {
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 7ea1e1d7c..b9e3f7b16 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -426,6 +426,7 @@ function reduceApp (state, action) {
case actions.DISPLAY_WARNING:
return extend(appState, {
warning: action.value,
+ isLoading: false,
})
case actions.HIDE_WARNING:
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index a3c07d977..2b5151466 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -13,12 +13,10 @@ function reduceMetamask (state, action) {
rpcTarget: 'https://rawtestrpc.metamask.io/',
identities: {},
unapprovedTxs: {},
- currentFiat: 'USD',
- conversionRate: 0,
- conversionDate: 'N/A',
noActiveNotices: true,
lastUnreadNotice: undefined,
frequentRpcList: [],
+ addressBook: [],
}, state.metamask)
switch (action.type) {
@@ -126,7 +124,7 @@ function reduceMetamask (state, action) {
case actions.SET_CURRENT_FIAT:
return extend(metamaskState, {
- currentFiat: action.value.currentFiat,
+ currentCurrency: action.value.currentCurrency,
conversionRate: action.value.conversionRate,
conversionDate: action.value.conversionDate,
})
diff --git a/ui/app/send.js b/ui/app/send.js
index a281a5fcf..eb32d5e06 100644
--- a/ui/app/send.js
+++ b/ui/app/send.js
@@ -20,6 +20,7 @@ function mapStateToProps (state) {
identities: state.metamask.identities,
warning: state.appState.warning,
network: state.metamask.network,
+ addressBook: state.metamask.addressBook,
}
result.error = result.warning && result.warning.split('.')[0]
@@ -44,6 +45,8 @@ SendTransactionScreen.prototype.render = function () {
var account = state.account
var identity = state.identity
var network = state.network
+ var identities = state.identities
+ var addressBook = state.addressBook
return (
@@ -153,6 +156,8 @@ SendTransactionScreen.prototype.render = function () {
placeholder: 'Recipient Address',
onChange: this.recipientDidChange.bind(this),
network,
+ identities,
+ addressBook,
}),
]),
@@ -222,13 +227,17 @@ SendTransactionScreen.prototype.back = function () {
this.props.dispatch(actions.backToAccountDetail(address))
}
-SendTransactionScreen.prototype.recipientDidChange = function (recipient) {
- this.setState({ recipient })
+SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) {
+ this.setState({
+ recipient: recipient,
+ nickname: nickname,
+ })
}
SendTransactionScreen.prototype.onSubmit = function () {
const state = this.state || {}
const recipient = state.recipient || document.querySelector('input[name="address"]').value
+ const nickname = state.nickname || ' '
const input = document.querySelector('input[name="amount"]').value
const value = util.normalizeEthStringToWei(input)
const txData = document.querySelector('input[name="txData"]').value
@@ -257,6 +266,8 @@ SendTransactionScreen.prototype.onSubmit = function () {
this.props.dispatch(actions.hideWarning())
+ this.props.dispatch(actions.addToAddressBook(recipient, nickname))
+
var txParams = {
from: this.props.address,
value: '0x' + value.toString(16),
diff --git a/ui/lib/account-link.js b/ui/lib/account-link.js
index 77db0851d..948f32da1 100644
--- a/ui/lib/account-link.js
+++ b/ui/lib/account-link.js
@@ -1,7 +1,6 @@
module.exports = function (address, network) {
const net = parseInt(network)
let link
-
switch (net) {
case 1: // main net
link = `http://etherscan.io/address/${address}`
@@ -12,6 +11,9 @@ module.exports = function (address, network) {
case 3: // ropsten test net
link = `http://testnet.etherscan.io/address/${address}`
break
+ case 42: // kovan test net
+ link = `http://kovan.etherscan.io/address/${address}`
+ break
default:
link = ''
break
diff --git a/ui/lib/explorer-link.js b/ui/lib/explorer-link.js
index dc6be2984..7ae19cca0 100644
--- a/ui/lib/explorer-link.js
+++ b/ui/lib/explorer-link.js
@@ -5,9 +5,12 @@ module.exports = function (hash, network) {
case 1: // main net
prefix = ''
break
- case 3: // morden test net
+ case 3: // ropsten test net
prefix = 'testnet.'
break
+ case 42: // kovan test net
+ prefix = 'kovan.'
+ break
default:
prefix = ''
}