mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'master' into mascara
This commit is contained in:
commit
8e30acb41a
@ -2,7 +2,14 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
- Add better error messages for when a transaction fails on approval
|
||||||
- Allow sending to ENS names in send form on Ropsten.
|
- 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
|
## 3.4.0 2017-3-8
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
|
const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
|
||||||
const TESTNET_RPC_URL = 'https://ropsten.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
|
const DEFAULT_RPC_URL = TESTNET_RPC_URL
|
||||||
|
|
||||||
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
||||||
@ -10,5 +11,6 @@ module.exports = {
|
|||||||
mainnet: MAINET_RPC_URL,
|
mainnet: MAINET_RPC_URL,
|
||||||
testnet: TESTNET_RPC_URL,
|
testnet: TESTNET_RPC_URL,
|
||||||
morden: TESTNET_RPC_URL,
|
morden: TESTNET_RPC_URL,
|
||||||
|
kovan: KOVAN_RPC_URL,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -69,14 +69,10 @@ function shouldInjectWeb3 () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isAllowedSuffix (testCase) {
|
function isAllowedSuffix (testCase) {
|
||||||
var prohibitedTypes = ['xml', 'pdf']
|
const doctype = window.document.doctype
|
||||||
var currentUrl = window.location.href
|
if (doctype) {
|
||||||
var currentRegex
|
return doctype.name === 'html'
|
||||||
for (let i = 0; i < prohibitedTypes.length; i++) {
|
} else {
|
||||||
currentRegex = new RegExp(`\.${prohibitedTypes[i]}$`)
|
return false
|
||||||
if (currentRegex.test(currentUrl)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
79
app/scripts/controllers/address-book.js
Normal file
79
app/scripts/controllers/address-book.js
Normal file
@ -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
|
@ -5,7 +5,9 @@ const extend = require('xtend')
|
|||||||
class PreferencesController {
|
class PreferencesController {
|
||||||
|
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
const initState = extend({ frequentRpcList: [] }, opts.initState)
|
const initState = extend({
|
||||||
|
frequentRpcList: [],
|
||||||
|
}, opts.initState)
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ const normalize = require('eth-sig-util').normalize
|
|||||||
const TESTNET_RPC = MetamaskConfig.network.testnet
|
const TESTNET_RPC = MetamaskConfig.network.testnet
|
||||||
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
||||||
const MORDEN_RPC = MetamaskConfig.network.morden
|
const MORDEN_RPC = MetamaskConfig.network.morden
|
||||||
|
const KOVAN_RPC = MetamaskConfig.network.kovan
|
||||||
|
|
||||||
/* The config-manager is a convenience object
|
/* The config-manager is a convenience object
|
||||||
* wrapping a pojo-migrator.
|
* wrapping a pojo-migrator.
|
||||||
@ -150,6 +151,9 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
|
|||||||
case 'morden':
|
case 'morden':
|
||||||
return MORDEN_RPC
|
return MORDEN_RPC
|
||||||
|
|
||||||
|
case 'kovan':
|
||||||
|
return KOVAN_RPC
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
|
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ class EthereumStore extends ObservableStore {
|
|||||||
super({
|
super({
|
||||||
accounts: {},
|
accounts: {},
|
||||||
transactions: {},
|
transactions: {},
|
||||||
|
currentBlockNumber: '0',
|
||||||
|
currentBlockHash: '',
|
||||||
})
|
})
|
||||||
this._provider = opts.provider
|
this._provider = opts.provider
|
||||||
this._query = new EthQuery(this._provider)
|
this._query = new EthQuery(this._provider)
|
||||||
@ -69,6 +71,8 @@ class EthereumStore extends ObservableStore {
|
|||||||
_updateForBlock (block) {
|
_updateForBlock (block) {
|
||||||
const blockNumber = '0x' + block.number.toString('hex')
|
const blockNumber = '0x' + block.number.toString('hex')
|
||||||
this._currentBlockNumber = blockNumber
|
this._currentBlockNumber = blockNumber
|
||||||
|
this.updateState({ currentBlockNumber: parseInt(blockNumber) })
|
||||||
|
this.updateState({ currentBlockHash: `0x${block.hash.toString('hex')}`})
|
||||||
async.parallel([
|
async.parallel([
|
||||||
this._updateAccounts.bind(this),
|
this._updateAccounts.bind(this),
|
||||||
this._updateTransactions.bind(this, blockNumber),
|
this._updateTransactions.bind(this, blockNumber),
|
||||||
@ -129,4 +133,4 @@ class EthereumStore extends ObservableStore {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = EthereumStore
|
module.exports = EthereumStore
|
||||||
|
@ -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')
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
@ -15,6 +15,7 @@ const PreferencesController = require('./controllers/preferences')
|
|||||||
const CurrencyController = require('./controllers/currency')
|
const CurrencyController = require('./controllers/currency')
|
||||||
const NoticeController = require('./notice-controller')
|
const NoticeController = require('./notice-controller')
|
||||||
const ShapeShiftController = require('./controllers/shapeshift')
|
const ShapeShiftController = require('./controllers/shapeshift')
|
||||||
|
const AddressBookController = require('./controllers/address-book')
|
||||||
const MessageManager = require('./lib/message-manager')
|
const MessageManager = require('./lib/message-manager')
|
||||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||||
const TxManager = require('./transaction-manager')
|
const TxManager = require('./transaction-manager')
|
||||||
@ -22,7 +23,6 @@ const ConfigManager = require('./lib/config-manager')
|
|||||||
const extension = require('./lib/extension')
|
const extension = require('./lib/extension')
|
||||||
const autoFaucet = require('./lib/auto-faucet')
|
const autoFaucet = require('./lib/auto-faucet')
|
||||||
const nodeify = require('./lib/nodeify')
|
const nodeify = require('./lib/nodeify')
|
||||||
const IdStoreMigrator = require('./lib/idStore-migrator')
|
|
||||||
const accountImporter = require('./account-import-strategies')
|
const accountImporter = require('./account-import-strategies')
|
||||||
|
|
||||||
const version = require('../manifest.json').version
|
const version = require('../manifest.json').version
|
||||||
@ -80,6 +80,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
autoFaucet(address)
|
autoFaucet(address)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// address book controller
|
||||||
|
this.addressBookController = new AddressBookController({
|
||||||
|
initState: initState.AddressBookController,
|
||||||
|
}, this.keyringController)
|
||||||
|
|
||||||
// tx mgmt
|
// tx mgmt
|
||||||
this.txManager = new TxManager({
|
this.txManager = new TxManager({
|
||||||
initState: initState.TransactionManager,
|
initState: initState.TransactionManager,
|
||||||
@ -109,11 +114,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.personalMessageManager = new PersonalMessageManager()
|
this.personalMessageManager = new PersonalMessageManager()
|
||||||
this.publicConfigStore = this.initPublicConfigStore()
|
this.publicConfigStore = this.initPublicConfigStore()
|
||||||
|
|
||||||
// TEMPORARY UNTIL FULL DEPRECATION:
|
|
||||||
this.idStoreMigrator = new IdStoreMigrator({
|
|
||||||
configManager: this.configManager,
|
|
||||||
})
|
|
||||||
|
|
||||||
// manual disk state subscriptions
|
// manual disk state subscriptions
|
||||||
this.txManager.store.subscribe((state) => {
|
this.txManager.store.subscribe((state) => {
|
||||||
this.store.updateState({ TransactionManager: state })
|
this.store.updateState({ TransactionManager: state })
|
||||||
@ -124,6 +124,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.preferencesController.store.subscribe((state) => {
|
this.preferencesController.store.subscribe((state) => {
|
||||||
this.store.updateState({ PreferencesController: state })
|
this.store.updateState({ PreferencesController: state })
|
||||||
})
|
})
|
||||||
|
this.addressBookController.store.subscribe((state) => {
|
||||||
|
this.store.updateState({ AddressBookController: state })
|
||||||
|
})
|
||||||
this.currencyController.store.subscribe((state) => {
|
this.currencyController.store.subscribe((state) => {
|
||||||
this.store.updateState({ CurrencyController: 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.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
this.preferencesController.store.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.currencyController.store.subscribe(this.sendUpdate.bind(this))
|
||||||
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
this.shapeshiftController.store.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.personalMessageManager.memStore.getState(),
|
||||||
this.keyringController.memStore.getState(),
|
this.keyringController.memStore.getState(),
|
||||||
this.preferencesController.store.getState(),
|
this.preferencesController.store.getState(),
|
||||||
|
this.addressBookController.store.getState(),
|
||||||
this.currencyController.store.getState(),
|
this.currencyController.store.getState(),
|
||||||
this.noticeController.memStore.getState(),
|
this.noticeController.memStore.getState(),
|
||||||
// config manager
|
// config manager
|
||||||
@ -239,6 +244,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
const preferencesController = this.preferencesController
|
const preferencesController = this.preferencesController
|
||||||
const txManager = this.txManager
|
const txManager = this.txManager
|
||||||
const noticeController = this.noticeController
|
const noticeController = this.noticeController
|
||||||
|
const addressBookController = this.addressBookController
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// etc
|
// etc
|
||||||
@ -266,6 +272,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
|
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
|
||||||
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
|
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
|
||||||
|
|
||||||
|
// AddressController
|
||||||
|
setAddressBook: nodeify(addressBookController.setAddressBook).bind(addressBookController),
|
||||||
|
|
||||||
// KeyringController
|
// KeyringController
|
||||||
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
|
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
|
||||||
createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
|
createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
|
||||||
@ -350,8 +359,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
//
|
//
|
||||||
|
|
||||||
submitPassword (password, cb) {
|
submitPassword (password, cb) {
|
||||||
this.migrateOldVaultIfAny(password)
|
return this.keyringController.submitPassword(password)
|
||||||
.then(this.keyringController.submitPassword.bind(this.keyringController, password))
|
|
||||||
.then((newState) => { cb(null, newState) })
|
.then((newState) => { cb(null, newState) })
|
||||||
.catch((reason) => { cb(reason) })
|
.catch((reason) => { cb(reason) })
|
||||||
}
|
}
|
||||||
@ -546,35 +554,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
cb(null, this.getState())
|
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) {
|
restoreOldVaultAccounts(migratorOutput) {
|
||||||
const { serialized } = migratorOutput
|
const { serialized } = migratorOutput
|
||||||
return this.keyringController.restoreKeyring(serialized)
|
return this.keyringController.restoreKeyring(serialized)
|
||||||
@ -620,7 +599,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.currencyController.updateConversionRate()
|
this.currencyController.updateConversionRate()
|
||||||
const data = {
|
const data = {
|
||||||
conversionRate: this.currencyController.getConversionRate(),
|
conversionRate: this.currencyController.getConversionRate(),
|
||||||
currentFiat: this.currencyController.getCurrentCurrency(),
|
currentCurrency: this.currencyController.getCurrentCurrency(),
|
||||||
conversionDate: this.currencyController.getConversionDate(),
|
conversionDate: this.currencyController.getConversionDate(),
|
||||||
}
|
}
|
||||||
cb(null, data)
|
cb(null, data)
|
||||||
|
@ -2,7 +2,7 @@ const version = 10
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
This migration breaks out the CurrencyController substate
|
This migration breaks out the ShapeShiftController substate
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -172,7 +172,10 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
], (err) => {
|
], (err) => {
|
||||||
self.nonceLock.leave()
|
self.nonceLock.leave()
|
||||||
if (err) {
|
if (err) {
|
||||||
this.setTxStatusFailed(txId)
|
this.setTxStatusFailed(txId, {
|
||||||
|
errCode: err.errCode || err,
|
||||||
|
message: err.message || 'Transaction failed during approval',
|
||||||
|
})
|
||||||
return cb(err)
|
return cb(err)
|
||||||
}
|
}
|
||||||
cb()
|
cb()
|
||||||
@ -291,7 +294,10 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
this._setTxStatus(txId, 'confirmed')
|
this._setTxStatus(txId, 'confirmed')
|
||||||
}
|
}
|
||||||
|
|
||||||
setTxStatusFailed (txId) {
|
setTxStatusFailed (txId, reason) {
|
||||||
|
let txMeta = this.getTx(txId)
|
||||||
|
txMeta.err = reason
|
||||||
|
this.updateTx(txMeta)
|
||||||
this._setTxStatus(txId, 'failed')
|
this._setTxStatus(txId, 'failed')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,12 +318,11 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
var txHash = txMeta.hash
|
var txHash = txMeta.hash
|
||||||
var txId = txMeta.id
|
var txId = txMeta.id
|
||||||
if (!txHash) {
|
if (!txHash) {
|
||||||
txMeta.err = {
|
let errReason = {
|
||||||
errCode: 'No hash was provided',
|
errCode: 'No hash was provided',
|
||||||
message: 'We had an error while submitting this transaction, please try again.',
|
message: 'We had an error while submitting this transaction, please try again.',
|
||||||
}
|
}
|
||||||
this.updateTx(txMeta)
|
return this.setTxStatusFailed(txId, errReason)
|
||||||
return this.setTxStatusFailed(txId)
|
|
||||||
}
|
}
|
||||||
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
|
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
|
||||||
if (err || !txParams) {
|
if (err || !txParams) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.06608791,
|
"conversionRate": 11.06608791,
|
||||||
"conversionDate": 1470421024,
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.06608791,
|
"conversionRate": 11.06608791,
|
||||||
"conversionDate": 1470421024,
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.06608791,
|
"conversionRate": 11.06608791,
|
||||||
"conversionDate": 1470421024,
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 10.19458075,
|
"conversionRate": 10.19458075,
|
||||||
"conversionDate": 1484696373,
|
"conversionDate": 1484696373,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 0,
|
"conversionRate": 0,
|
||||||
"conversionDate": "N/A",
|
"conversionDate": "N/A",
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.84461814,
|
"conversionRate": 11.84461814,
|
||||||
"conversionDate": 1476226414,
|
"conversionDate": 1476226414,
|
||||||
"accounts": {
|
"accounts": {
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
"simulationFails": true
|
"simulationFails": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 7.69158136,
|
"conversionRate": 7.69158136,
|
||||||
"conversionDate": 1482279663,
|
"conversionDate": 1482279663,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
"gasPrice": "4a817c800"
|
"gasPrice": "4a817c800"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 12.7200827,
|
"conversionRate": 12.7200827,
|
||||||
"conversionDate": 1487363041,
|
"conversionDate": 1487363041,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.06608791,
|
"conversionRate": 11.06608791,
|
||||||
"conversionDate": 1470421024,
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"identities": {},
|
"identities": {},
|
||||||
"frequentRpcList": [],
|
"frequentRpcList": [],
|
||||||
"unapprovedTxs": {},
|
"unapprovedTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 12.7527416,
|
"conversionRate": 12.7527416,
|
||||||
"conversionDate": 1487624341,
|
"conversionDate": 1487624341,
|
||||||
"noActiveNotices": false,
|
"noActiveNotices": false,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 10.1219126,
|
"conversionRate": 10.1219126,
|
||||||
"conversionDate": 1484695442,
|
"conversionDate": 1484695442,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 10.10788584,
|
"conversionRate": 10.10788584,
|
||||||
"conversionDate": 1484694362,
|
"conversionDate": 1484694362,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
"identities": {},
|
"identities": {},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.4379398,
|
"conversionRate": 11.4379398,
|
||||||
"conversionDate": 1473358355,
|
"conversionDate": 1473358355,
|
||||||
"accounts": {},
|
"accounts": {},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"lostAccounts": [
|
"lostAccounts": [
|
||||||
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
|
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 10.92067835,
|
"conversionRate": 10.92067835,
|
||||||
"conversionDate": 1478282884,
|
"conversionDate": 1478282884,
|
||||||
"network": null,
|
"network": null,
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 8.3533002,
|
"conversionRate": 8.3533002,
|
||||||
"conversionDate": 1481671082,
|
"conversionDate": 1481671082,
|
||||||
"noActiveNotices": false,
|
"noActiveNotices": false,
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.02269525,
|
"conversionRate": 11.02269525,
|
||||||
"conversionDate": 1472076963,
|
"conversionDate": 1472076963,
|
||||||
"accounts": {
|
"accounts": {
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"maxCost": "de234b52e4a0800"
|
"maxCost": "de234b52e4a0800"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 12.59854817,
|
"conversionRate": 12.59854817,
|
||||||
"conversionDate": 1487662141,
|
"conversionDate": 1487662141,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unapprovedTxs": {},
|
"unapprovedTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 13.2126613,
|
"conversionRate": 13.2126613,
|
||||||
"conversionDate": 1487888522,
|
"conversionDate": 1487888522,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
70
development/states/private-key-export-success.json
Normal file
70
development/states/private-key-export-success.json
Normal file
@ -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": {}
|
||||||
|
}
|
69
development/states/private-key-export.json
Normal file
69
development/states/private-key-export.json
Normal file
@ -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": {}
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 9.52855776,
|
"conversionRate": 9.52855776,
|
||||||
"conversionDate": 1479756513,
|
"conversionDate": 1479756513,
|
||||||
"accounts": {
|
"accounts": {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
"identities": {},
|
"identities": {},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 0,
|
"conversionRate": 0,
|
||||||
"conversionDate": "N/A",
|
"conversionDate": "N/A",
|
||||||
"accounts": {},
|
"accounts": {},
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unapprovedTxs": {},
|
"unapprovedTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 16.88200327,
|
"conversionRate": 16.88200327,
|
||||||
"conversionDate": 1489013762,
|
"conversionDate": 1489013762,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 11.21274318,
|
"conversionRate": 11.21274318,
|
||||||
"conversionDate": 1472159644,
|
"conversionDate": 1472159644,
|
||||||
"accounts": {
|
"accounts": {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
"identities": {},
|
"identities": {},
|
||||||
"unconfTxs": {},
|
"unconfTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentCurrency": "USD",
|
||||||
"conversionRate": 8.18703468,
|
"conversionRate": 8.18703468,
|
||||||
"conversionDate": 1481755832,
|
"conversionDate": 1481755832,
|
||||||
"network": "3",
|
"network": "3",
|
||||||
|
@ -53,7 +53,6 @@
|
|||||||
"ensnare": "^1.0.0",
|
"ensnare": "^1.0.0",
|
||||||
"eth-bin-to-ops": "^1.0.1",
|
"eth-bin-to-ops": "^1.0.1",
|
||||||
"eth-hd-keyring": "^1.1.1",
|
"eth-hd-keyring": "^1.1.1",
|
||||||
"eth-lightwallet": "^2.3.3",
|
|
||||||
"eth-query": "^1.0.3",
|
"eth-query": "^1.0.3",
|
||||||
"eth-sig-util": "^1.1.1",
|
"eth-sig-util": "^1.1.1",
|
||||||
"eth-simple-keyring": "^1.1.1",
|
"eth-simple-keyring": "^1.1.1",
|
||||||
@ -99,7 +98,7 @@
|
|||||||
"react-tooltip-component": "^0.3.0",
|
"react-tooltip-component": "^0.3.0",
|
||||||
"readable-stream": "^2.1.2",
|
"readable-stream": "^2.1.2",
|
||||||
"redux": "^3.0.5",
|
"redux": "^3.0.5",
|
||||||
"redux-logger": "^2.3.1",
|
"redux-logger": "^2.10.2",
|
||||||
"redux-thunk": "^1.0.2",
|
"redux-thunk": "^1.0.2",
|
||||||
"request-promise": "^4.1.1",
|
"request-promise": "^4.1.1",
|
||||||
"sandwich-expando": "^1.0.5",
|
"sandwich-expando": "^1.0.5",
|
||||||
|
@ -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 }
|
|
||||||
}
|
|
56
test/unit/address-book-controller.js
Normal file
56
test/unit/address-book-controller.js
Normal file
@ -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.')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -7,7 +7,7 @@ const rp = require('request-promise')
|
|||||||
const nock = require('nock')
|
const nock = require('nock')
|
||||||
const CurrencyController = require('../../app/scripts/controllers/currency')
|
const CurrencyController = require('../../app/scripts/controllers/currency')
|
||||||
|
|
||||||
describe('config-manager', function() {
|
describe('currency-controller', function() {
|
||||||
var currencyController
|
var currencyController
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
@ -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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -60,7 +60,7 @@ JsonImportSubview.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}, 'Import'),
|
}, '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 ]))
|
this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ PrivateKeyImportView.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}, 'Import'),
|
}, '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
|
const privateKey = input.value
|
||||||
this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
|
this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,10 +71,12 @@ var actions = {
|
|||||||
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
|
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
|
||||||
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
|
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
|
||||||
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
|
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
|
||||||
setCurrentFiat: setCurrentFiat,
|
setCurrentCurrency: setCurrentCurrency,
|
||||||
// account detail screen
|
// account detail screen
|
||||||
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
||||||
showSendPage: showSendPage,
|
showSendPage: showSendPage,
|
||||||
|
ADD_TO_ADDRESS_BOOK: 'ADD_TO_ADDRESS_BOOK',
|
||||||
|
addToAddressBook: addToAddressBook,
|
||||||
REQUEST_ACCOUNT_EXPORT: 'REQUEST_ACCOUNT_EXPORT',
|
REQUEST_ACCOUNT_EXPORT: 'REQUEST_ACCOUNT_EXPORT',
|
||||||
requestExportAccount: requestExportAccount,
|
requestExportAccount: requestExportAccount,
|
||||||
EXPORT_ACCOUNT: 'EXPORT_ACCOUNT',
|
EXPORT_ACCOUNT: 'EXPORT_ACCOUNT',
|
||||||
@ -267,11 +269,12 @@ function requestRevealSeed (password) {
|
|||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
log.debug(`background.submitPassword`)
|
log.debug(`background.submitPassword`)
|
||||||
background.submitPassword(password, (err) => {
|
background.submitPassword(password, (err) => {
|
||||||
if (err) return dispatch(actions.displayWarning(err.message))
|
if (err) {
|
||||||
|
return dispatch(actions.displayWarning(err.message))
|
||||||
|
}
|
||||||
log.debug(`background.placeSeedWords`)
|
log.debug(`background.placeSeedWords`)
|
||||||
background.placeSeedWords((err) => {
|
background.placeSeedWords((err) => {
|
||||||
if (err) return dispatch(actions.displayWarning(err.message))
|
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.'))
|
dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
|
||||||
log.debug(`background.importAccountWithStrategy`)
|
log.debug(`background.importAccountWithStrategy`)
|
||||||
background.importAccountWithStrategy(strategy, args, (err) => {
|
background.importAccountWithStrategy(strategy, args, (err) => {
|
||||||
dispatch(actions.hideLoadingIndication())
|
|
||||||
if (err) return dispatch(actions.displayWarning(err.message))
|
if (err) return dispatch(actions.displayWarning(err.message))
|
||||||
log.debug(`background.getState`)
|
log.debug(`background.getState`)
|
||||||
background.getState((err, newState) => {
|
background.getState((err, newState) => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
if (err) {
|
if (err) {
|
||||||
return dispatch(actions.displayWarning(err.message))
|
return dispatch(actions.displayWarning(err.message))
|
||||||
}
|
}
|
||||||
@ -328,10 +331,10 @@ function showInfoPage () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCurrentFiat (currencyCode) {
|
function setCurrentCurrency (currencyCode) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(this.showLoadingIndication())
|
dispatch(this.showLoadingIndication())
|
||||||
log.debug(`background.setCurrentFiat`)
|
log.debug(`background.setCurrentCurrency`)
|
||||||
background.setCurrentCurrency(currencyCode, (err, data) => {
|
background.setCurrentCurrency(currencyCode, (err, data) => {
|
||||||
dispatch(this.hideLoadingIndication())
|
dispatch(this.hideLoadingIndication())
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -341,7 +344,7 @@ function setCurrentFiat (currencyCode) {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: this.SET_CURRENT_FIAT,
|
type: this.SET_CURRENT_FIAT,
|
||||||
value: {
|
value: {
|
||||||
currentFiat: data.currentFiat,
|
currentCurrency: data.currentCurrency,
|
||||||
conversionRate: data.conversionRate,
|
conversionRate: data.conversionRate,
|
||||||
conversionDate: data.conversionDate,
|
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) {
|
function setProviderType (type) {
|
||||||
log.debug(`background.setProviderType`)
|
log.debug(`background.setProviderType`)
|
||||||
background.setProviderType(type)
|
background.setProviderType(type)
|
||||||
@ -757,22 +773,30 @@ function requestExportAccount () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportAccount (address) {
|
function exportAccount (password, address) {
|
||||||
var self = this
|
var self = this
|
||||||
|
|
||||||
return function (dispatch) {
|
return function (dispatch) {
|
||||||
dispatch(self.showLoadingIndication())
|
dispatch(self.showLoadingIndication())
|
||||||
|
|
||||||
log.debug(`background.exportAccount`)
|
log.debug(`background.submitPassword`)
|
||||||
background.exportAccount(address, function (err, result) {
|
background.submitPassword(password, function (err) {
|
||||||
dispatch(self.hideLoadingIndication())
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
log.error(err)
|
log.error('Error in submiting password.')
|
||||||
return dispatch(self.displayWarning('Had a problem exporting the account.'))
|
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))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,6 +255,15 @@ App.prototype.renderNetworkDropdown = function () {
|
|||||||
provider: props.provider,
|
provider: props.provider,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
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, {
|
h(DropMenuItem, {
|
||||||
label: 'Localhost 8545',
|
label: 'Localhost 8545',
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
@ -266,7 +275,7 @@ App.prototype.renderNetworkDropdown = function () {
|
|||||||
this.renderCustomOption(props.provider),
|
this.renderCustomOption(props.provider),
|
||||||
this.renderCommonRpc(rpcList, props.provider),
|
this.renderCommonRpc(rpcList, props.provider),
|
||||||
|
|
||||||
props.isUnlocked && h(DropMenuItem, {
|
h(DropMenuItem, {
|
||||||
label: 'Custom RPC',
|
label: 'Custom RPC',
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
action: () => this.props.dispatch(actions.showConfigPage()),
|
action: () => this.props.dispatch(actions.showConfigPage()),
|
||||||
@ -400,6 +409,10 @@ App.prototype.renderPrimary = function () {
|
|||||||
log.debug('rendering restore vault screen')
|
log.debug('rendering restore vault screen')
|
||||||
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
|
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
|
||||||
|
|
||||||
|
case 'config':
|
||||||
|
log.debug('rendering config screen from unlock screen.')
|
||||||
|
return h(ConfigScreen, {key: 'config'})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.debug('rendering locked screen')
|
log.debug('rendering locked screen')
|
||||||
return h(UnlockScreen, {key: 'locked'})
|
return h(UnlockScreen, {key: 'locked'})
|
||||||
|
@ -4,14 +4,21 @@ const inherits = require('util').inherits
|
|||||||
const copyToClipboard = require('copy-to-clipboard')
|
const copyToClipboard = require('copy-to-clipboard')
|
||||||
const actions = require('../actions')
|
const actions = require('../actions')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
|
||||||
module.exports = ExportAccountView
|
module.exports = connect(mapStateToProps)(ExportAccountView)
|
||||||
|
|
||||||
inherits(ExportAccountView, Component)
|
inherits(ExportAccountView, Component)
|
||||||
function ExportAccountView () {
|
function ExportAccountView () {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
warning: state.appState.warning,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExportAccountView.prototype.render = function () {
|
ExportAccountView.prototype.render = function () {
|
||||||
console.log('EXPORT VIEW')
|
console.log('EXPORT VIEW')
|
||||||
console.dir(this.props)
|
console.dir(this.props)
|
||||||
@ -28,35 +35,58 @@ ExportAccountView.prototype.render = function () {
|
|||||||
if (notExporting) return h('div')
|
if (notExporting) return h('div')
|
||||||
|
|
||||||
if (exportRequested) {
|
if (exportRequested) {
|
||||||
var warning = `Exporting your private key is very dangerous,
|
var warning = `Export private keys at your own risk.`
|
||||||
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.`
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h('div', {
|
h('div', {
|
||||||
key: 'exporting',
|
|
||||||
style: {
|
style: {
|
||||||
margin: '0 20px',
|
display: 'inline-block',
|
||||||
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
}, [
|
},
|
||||||
h('p.error', warning),
|
[
|
||||||
h('p', confirmation),
|
h('div', {
|
||||||
h('input#exportAccount.sizing-input', {
|
key: 'exporting',
|
||||||
onKeyPress: this.onExportKeyPress.bind(this),
|
style: {
|
||||||
style: {
|
margin: '0 20px',
|
||||||
position: 'relative',
|
},
|
||||||
top: '1.5px',
|
}, [
|
||||||
|
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', {
|
h('button', {
|
||||||
onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
|
onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
|
||||||
}, 'Submit'),
|
style: {
|
||||||
h('button', {
|
marginRight: '10px',
|
||||||
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
|
},
|
||||||
}, 'Cancel'),
|
}, '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
|
if (event.key !== 'Enter') return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
var input = document.getElementById('exportAccount')
|
var input = document.getElementById('exportAccount').value
|
||||||
if (input.value === 'I understand') {
|
this.props.dispatch(actions.exportAccount(input, this.props.address))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ const Component = require('react').Component
|
|||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const extend = require('xtend')
|
||||||
|
|
||||||
module.exports = BinaryRenderer
|
module.exports = BinaryRenderer
|
||||||
|
|
||||||
@ -12,20 +13,22 @@ function BinaryRenderer () {
|
|||||||
|
|
||||||
BinaryRenderer.prototype.render = function () {
|
BinaryRenderer.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const { value } = props
|
const { value, style } = props
|
||||||
const text = this.hexToText(value)
|
const text = this.hexToText(value)
|
||||||
|
|
||||||
|
const defaultStyle = extend({
|
||||||
|
width: '315px',
|
||||||
|
maxHeight: '210px',
|
||||||
|
resize: 'none',
|
||||||
|
border: 'none',
|
||||||
|
background: 'white',
|
||||||
|
padding: '3px',
|
||||||
|
}, style)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('textarea.font-small', {
|
h('textarea.font-small', {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
style: {
|
style: defaultStyle,
|
||||||
width: '315px',
|
|
||||||
maxHeight: '210px',
|
|
||||||
resize: 'none',
|
|
||||||
border: 'none',
|
|
||||||
background: 'white',
|
|
||||||
padding: '3px',
|
|
||||||
},
|
|
||||||
defaultValue: text,
|
defaultValue: text,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -42,7 +42,10 @@ DropMenuItem.prototype.activeNetworkRender = function () {
|
|||||||
if (providerType === 'mainnet') return h('.check', '✓')
|
if (providerType === 'mainnet') return h('.check', '✓')
|
||||||
break
|
break
|
||||||
case 'Ropsten Test Network':
|
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
|
break
|
||||||
case 'Localhost 8545':
|
case 'Localhost 8545':
|
||||||
if (activeNetwork === 'http://localhost:8545') return h('.check', '✓')
|
if (activeNetwork === 'http://localhost:8545') return h('.check', '✓')
|
||||||
|
@ -21,6 +21,7 @@ function EnsInput () {
|
|||||||
EnsInput.prototype.render = function () {
|
EnsInput.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const opts = extend(props, {
|
const opts = extend(props, {
|
||||||
|
list: 'addresses',
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
const network = this.props.network
|
const network = this.props.network
|
||||||
let resolverAddress = networkResolvers[network]
|
let resolverAddress = networkResolvers[network]
|
||||||
@ -46,6 +47,25 @@ EnsInput.prototype.render = function () {
|
|||||||
style: { width: '100%' },
|
style: { width: '100%' },
|
||||||
}, [
|
}, [
|
||||||
h('input.large-input', opts),
|
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(),
|
this.ensIcon(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -80,11 +100,13 @@ EnsInput.prototype.lookupEnsName = function () {
|
|||||||
this.setState({
|
this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
ensResolution: address,
|
ensResolution: address,
|
||||||
|
nickname: recipient.trim(),
|
||||||
hoverText: address + '\nClick to Copy',
|
hoverText: address + '\nClick to Copy',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
|
log.error(reason)
|
||||||
return this.setState({
|
return this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
ensFailure: true,
|
ensFailure: true,
|
||||||
@ -95,10 +117,13 @@ EnsInput.prototype.lookupEnsName = function () {
|
|||||||
|
|
||||||
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
||||||
const state = this.state || {}
|
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 &&
|
if (ensResolution && this.props.onChange &&
|
||||||
ensResolution !== prevState.ensResolution) {
|
ensResolution !== prevState.ensResolution) {
|
||||||
this.props.onChange(ensResolution)
|
this.props.onChange(ensResolution, nickname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ module.exports = connect(mapStateToProps)(FiatValue)
|
|||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return {
|
return {
|
||||||
conversionRate: state.metamask.conversionRate,
|
conversionRate: state.metamask.conversionRate,
|
||||||
currentFiat: state.metamask.currentFiat,
|
currentCurrency: state.metamask.currentCurrency,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ FiatValue.prototype.render = function () {
|
|||||||
fiatTooltipNumber = 'Unknown'
|
fiatTooltipNumber = 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
var fiatSuffix = props.currentFiat
|
var fiatSuffix = props.currentCurrency
|
||||||
|
|
||||||
return fiatDisplay(fiatDisplayNumber, fiatSuffix)
|
return fiatDisplay(fiatDisplayNumber, fiatSuffix)
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,9 @@ Network.prototype.render = function () {
|
|||||||
} else if (parseInt(networkNumber) === 3) {
|
} else if (parseInt(networkNumber) === 3) {
|
||||||
hoverText = 'Ropsten Test Network'
|
hoverText = 'Ropsten Test Network'
|
||||||
iconName = 'ropsten-test-network'
|
iconName = 'ropsten-test-network'
|
||||||
|
} else if (providerName === 'kovan') {
|
||||||
|
hoverText = 'Kovan Test Network'
|
||||||
|
iconName = 'kovan-test-network'
|
||||||
} else {
|
} else {
|
||||||
hoverText = 'Unknown Private Network'
|
hoverText = 'Unknown Private Network'
|
||||||
iconName = 'unknown-private-network'
|
iconName = 'unknown-private-network'
|
||||||
@ -70,6 +73,15 @@ Network.prototype.render = function () {
|
|||||||
}},
|
}},
|
||||||
'Ropsten Test Net'),
|
'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:
|
default:
|
||||||
return h('.network-indicator', [
|
return h('.network-indicator', [
|
||||||
h('i.fa.fa-question-circle.fa-lg', {
|
h('i.fa.fa-question-circle.fa-lg', {
|
||||||
|
@ -40,9 +40,18 @@ PendingMsgDetails.prototype.render = function () {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
// message data
|
// message data
|
||||||
h('div', [
|
h('div', {
|
||||||
|
style: {
|
||||||
|
height: '260px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
|
h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
|
||||||
h(BinaryRenderer, { value: data }),
|
h(BinaryRenderer, {
|
||||||
|
value: data,
|
||||||
|
style: {
|
||||||
|
height: '215px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
@ -28,7 +28,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
let isLinkable = false
|
let isLinkable = false
|
||||||
const numericNet = parseInt(network)
|
const numericNet = parseInt(network)
|
||||||
isLinkable = numericNet === 1 || numericNet === 3
|
isLinkable = numericNet === 1 || numericNet === 3 || numericNet === 42
|
||||||
|
|
||||||
var isMsg = ('msgParams' in transaction)
|
var isMsg = ('msgParams' in transaction)
|
||||||
var isTx = ('txParams' in transaction)
|
var isTx = ('txParams' in transaction)
|
||||||
|
@ -125,19 +125,19 @@ function rpcValidation (newRpc, state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function currentConversionInformation (metamaskState, state) {
|
function currentConversionInformation (metamaskState, state) {
|
||||||
var currentFiat = metamaskState.currentFiat
|
var currentCurrency = metamaskState.currentCurrency
|
||||||
var conversionDate = metamaskState.conversionDate
|
var conversionDate = metamaskState.conversionDate
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
|
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
|
||||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
|
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
|
||||||
h('select#currentFiat', {
|
h('select#currentCurrency', {
|
||||||
onChange (event) {
|
onChange (event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
var element = document.getElementById('currentFiat')
|
var element = document.getElementById('currentCurrency')
|
||||||
var newFiat = element.value
|
var newCurrency = element.value
|
||||||
state.dispatch(actions.setCurrentFiat(newFiat))
|
state.dispatch(actions.setCurrentCurrency(newCurrency))
|
||||||
},
|
},
|
||||||
defaultValue: currentFiat,
|
defaultValue: currentCurrency,
|
||||||
}, currencies.map((currency) => {
|
}, currencies.map((currency) => {
|
||||||
return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`)
|
return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`)
|
||||||
})
|
})
|
||||||
@ -161,6 +161,11 @@ function currentProviderDisplay (metamaskState) {
|
|||||||
value = 'Ropsten Test Network'
|
value = 'Ropsten Test Network'
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'kovan':
|
||||||
|
title = 'Current Network'
|
||||||
|
value = 'Kovan Test Network'
|
||||||
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
title = 'Current RPC'
|
title = 'Current RPC'
|
||||||
value = metamaskState.provider.rpcTarget
|
value = metamaskState.provider.rpcTarget
|
||||||
|
@ -266,8 +266,9 @@ app sections
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sizing-input{
|
.sizing-input{
|
||||||
font-size: 1em;
|
font-size: 14px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
.editable-label{
|
.editable-label{
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -188,7 +188,7 @@ hr.horizontal-line {
|
|||||||
|
|
||||||
.hollow-diamond {
|
.hollow-diamond {
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
border: 1px solid #038789;
|
border: 3px solid #690496;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pending-dot {
|
.pending-dot {
|
||||||
|
@ -426,6 +426,7 @@ function reduceApp (state, action) {
|
|||||||
case actions.DISPLAY_WARNING:
|
case actions.DISPLAY_WARNING:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
warning: action.value,
|
warning: action.value,
|
||||||
|
isLoading: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
case actions.HIDE_WARNING:
|
case actions.HIDE_WARNING:
|
||||||
|
@ -13,12 +13,10 @@ function reduceMetamask (state, action) {
|
|||||||
rpcTarget: 'https://rawtestrpc.metamask.io/',
|
rpcTarget: 'https://rawtestrpc.metamask.io/',
|
||||||
identities: {},
|
identities: {},
|
||||||
unapprovedTxs: {},
|
unapprovedTxs: {},
|
||||||
currentFiat: 'USD',
|
|
||||||
conversionRate: 0,
|
|
||||||
conversionDate: 'N/A',
|
|
||||||
noActiveNotices: true,
|
noActiveNotices: true,
|
||||||
lastUnreadNotice: undefined,
|
lastUnreadNotice: undefined,
|
||||||
frequentRpcList: [],
|
frequentRpcList: [],
|
||||||
|
addressBook: [],
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -126,7 +124,7 @@ function reduceMetamask (state, action) {
|
|||||||
|
|
||||||
case actions.SET_CURRENT_FIAT:
|
case actions.SET_CURRENT_FIAT:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
currentFiat: action.value.currentFiat,
|
currentCurrency: action.value.currentCurrency,
|
||||||
conversionRate: action.value.conversionRate,
|
conversionRate: action.value.conversionRate,
|
||||||
conversionDate: action.value.conversionDate,
|
conversionDate: action.value.conversionDate,
|
||||||
})
|
})
|
||||||
|
@ -20,6 +20,7 @@ function mapStateToProps (state) {
|
|||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
|
addressBook: state.metamask.addressBook,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.error = result.warning && result.warning.split('.')[0]
|
result.error = result.warning && result.warning.split('.')[0]
|
||||||
@ -44,6 +45,8 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
var account = state.account
|
var account = state.account
|
||||||
var identity = state.identity
|
var identity = state.identity
|
||||||
var network = state.network
|
var network = state.network
|
||||||
|
var identities = state.identities
|
||||||
|
var addressBook = state.addressBook
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@ -153,6 +156,8 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
placeholder: 'Recipient Address',
|
placeholder: 'Recipient Address',
|
||||||
onChange: this.recipientDidChange.bind(this),
|
onChange: this.recipientDidChange.bind(this),
|
||||||
network,
|
network,
|
||||||
|
identities,
|
||||||
|
addressBook,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -222,13 +227,17 @@ SendTransactionScreen.prototype.back = function () {
|
|||||||
this.props.dispatch(actions.backToAccountDetail(address))
|
this.props.dispatch(actions.backToAccountDetail(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.recipientDidChange = function (recipient) {
|
SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) {
|
||||||
this.setState({ recipient })
|
this.setState({
|
||||||
|
recipient: recipient,
|
||||||
|
nickname: nickname,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.onSubmit = function () {
|
SendTransactionScreen.prototype.onSubmit = function () {
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const recipient = state.recipient || document.querySelector('input[name="address"]').value
|
const recipient = state.recipient || document.querySelector('input[name="address"]').value
|
||||||
|
const nickname = state.nickname || ' '
|
||||||
const input = document.querySelector('input[name="amount"]').value
|
const input = document.querySelector('input[name="amount"]').value
|
||||||
const value = util.normalizeEthStringToWei(input)
|
const value = util.normalizeEthStringToWei(input)
|
||||||
const txData = document.querySelector('input[name="txData"]').value
|
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.hideWarning())
|
||||||
|
|
||||||
|
this.props.dispatch(actions.addToAddressBook(recipient, nickname))
|
||||||
|
|
||||||
var txParams = {
|
var txParams = {
|
||||||
from: this.props.address,
|
from: this.props.address,
|
||||||
value: '0x' + value.toString(16),
|
value: '0x' + value.toString(16),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
module.exports = function (address, network) {
|
module.exports = function (address, network) {
|
||||||
const net = parseInt(network)
|
const net = parseInt(network)
|
||||||
let link
|
let link
|
||||||
|
|
||||||
switch (net) {
|
switch (net) {
|
||||||
case 1: // main net
|
case 1: // main net
|
||||||
link = `http://etherscan.io/address/${address}`
|
link = `http://etherscan.io/address/${address}`
|
||||||
@ -12,6 +11,9 @@ module.exports = function (address, network) {
|
|||||||
case 3: // ropsten test net
|
case 3: // ropsten test net
|
||||||
link = `http://testnet.etherscan.io/address/${address}`
|
link = `http://testnet.etherscan.io/address/${address}`
|
||||||
break
|
break
|
||||||
|
case 42: // kovan test net
|
||||||
|
link = `http://kovan.etherscan.io/address/${address}`
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
link = ''
|
link = ''
|
||||||
break
|
break
|
||||||
|
@ -5,9 +5,12 @@ module.exports = function (hash, network) {
|
|||||||
case 1: // main net
|
case 1: // main net
|
||||||
prefix = ''
|
prefix = ''
|
||||||
break
|
break
|
||||||
case 3: // morden test net
|
case 3: // ropsten test net
|
||||||
prefix = 'testnet.'
|
prefix = 'testnet.'
|
||||||
break
|
break
|
||||||
|
case 42: // kovan test net
|
||||||
|
prefix = 'kovan.'
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
prefix = ''
|
prefix = ''
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user