aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts')
-rw-r--r--app/scripts/account-import-strategies/index.js2
-rw-r--r--app/scripts/background.js63
-rw-r--r--app/scripts/config.js11
-rw-r--r--app/scripts/contentscript.js16
-rw-r--r--app/scripts/controllers/address-book.js8
-rw-r--r--app/scripts/controllers/currency.js10
-rw-r--r--app/scripts/controllers/network.js129
-rw-r--r--app/scripts/controllers/preferences.js8
-rw-r--r--app/scripts/controllers/transactions.js (renamed from app/scripts/transaction-manager.js)191
-rw-r--r--app/scripts/first-time-state.js6
-rw-r--r--app/scripts/inpage.js21
-rw-r--r--app/scripts/keyring-controller.js5
-rw-r--r--app/scripts/lib/auto-faucet.js20
-rw-r--r--app/scripts/lib/auto-reload.js47
-rw-r--r--app/scripts/lib/buy-eth-url.js23
-rw-r--r--app/scripts/lib/config-manager.js80
-rw-r--r--app/scripts/lib/eth-store.js10
-rw-r--r--app/scripts/lib/extension-instance.js68
-rw-r--r--app/scripts/lib/extension.js14
-rw-r--r--app/scripts/lib/hex-to-bn.js7
-rw-r--r--app/scripts/lib/id-management.js90
-rw-r--r--app/scripts/lib/idStore-migrator.js80
-rw-r--r--app/scripts/lib/idStore.js343
-rw-r--r--app/scripts/lib/inpage-provider.js5
-rw-r--r--app/scripts/lib/message-manager.js4
-rw-r--r--app/scripts/lib/migrator/index.js43
-rw-r--r--app/scripts/lib/notification-manager.js71
-rw-r--r--app/scripts/lib/notifications.js65
-rw-r--r--app/scripts/lib/personal-message-manager.js4
-rw-r--r--app/scripts/lib/tx-utils.js75
-rw-r--r--app/scripts/metamask-controller.js297
-rw-r--r--app/scripts/migrations/002.js2
-rw-r--r--app/scripts/migrations/003.js2
-rw-r--r--app/scripts/migrations/004.js2
-rw-r--r--app/scripts/migrations/005.js2
-rw-r--r--app/scripts/migrations/006.js2
-rw-r--r--app/scripts/migrations/007.js2
-rw-r--r--app/scripts/migrations/008.js2
-rw-r--r--app/scripts/migrations/009.js2
-rw-r--r--app/scripts/migrations/010.js2
-rw-r--r--app/scripts/migrations/011.js2
-rw-r--r--app/scripts/migrations/012.js36
-rw-r--r--app/scripts/migrations/013.js34
-rw-r--r--app/scripts/migrations/014.js34
-rw-r--r--app/scripts/migrations/_multi-keyring.js11
-rw-r--r--app/scripts/migrations/index.js3
-rw-r--r--app/scripts/notice-controller.js1
-rw-r--r--app/scripts/platforms/extension.js23
-rw-r--r--app/scripts/platforms/sw.js24
-rw-r--r--app/scripts/platforms/window.js22
-rw-r--r--app/scripts/popup-core.js31
-rw-r--r--app/scripts/popup.js48
52 files changed, 925 insertions, 1178 deletions
diff --git a/app/scripts/account-import-strategies/index.js b/app/scripts/account-import-strategies/index.js
index d5124eb7f..96e2b5912 100644
--- a/app/scripts/account-import-strategies/index.js
+++ b/app/scripts/account-import-strategies/index.js
@@ -4,7 +4,7 @@ const ethUtil = require('ethereumjs-util')
const accountImporter = {
- importAccount(strategy, args) {
+ importAccount (strategy, args) {
try {
const importer = this.strategies[strategy]
const privateKeyHex = importer.apply(null, args)
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 254737dec..63c8a7252 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -1,15 +1,15 @@
const urlUtil = require('url')
const endOfStream = require('end-of-stream')
-const asyncQ = require('async-q')
const pipe = require('pump')
const LocalStorageStore = require('obs-store/lib/localStorage')
const storeTransform = require('obs-store/lib/transform')
+const ExtensionPlatform = require('./platforms/extension')
const Migrator = require('./lib/migrator/')
const migrations = require('./migrations/')
const PortStream = require('./lib/port-stream.js')
-const notification = require('./lib/notifications.js')
+const NotificationManager = require('./lib/notification-manager.js')
const MetamaskController = require('./metamask-controller')
-const extension = require('./lib/extension')
+const extension = require('extensionizer')
const firstTimeState = require('./first-time-state')
const STORAGE_KEY = 'metamask-config'
@@ -19,44 +19,42 @@ const log = require('loglevel')
window.log = log
log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn')
+const platform = new ExtensionPlatform()
+const notificationManager = new NotificationManager()
+global.METAMASK_NOTIFIER = notificationManager
+
let popupIsOpen = false
// state persistence
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
// initialization flow
-asyncQ.waterfall([
- () => loadStateFromPersistence(),
- (initState) => setupController(initState),
-])
-.then(() => console.log('MetaMask initialization complete.'))
-.catch((err) => { console.error(err) })
+initialize().catch(console.error)
+
+async function initialize() {
+ const initState = await loadStateFromPersistence()
+ await setupController(initState)
+ console.log('MetaMask initialization complete.')
+}
//
// State and Persistence
//
-function loadStateFromPersistence() {
+async function loadStateFromPersistence () {
// migrations
- let migrator = new Migrator({ migrations })
- let initialState = migrator.generateInitialState(firstTimeState)
- return asyncQ.waterfall([
- // read from disk
- () => Promise.resolve(diskStore.getState() || initialState),
- // migrate data
- (versionedData) => migrator.migrateData(versionedData),
- // write to disk
- (versionedData) => {
- diskStore.putState(versionedData)
- return Promise.resolve(versionedData)
- },
- // resolve to just data
- (versionedData) => Promise.resolve(versionedData.data),
- ])
+ const migrator = new Migrator({ migrations })
+ // read from disk
+ let versionedData = diskStore.getState() || migrator.generateInitialState(firstTimeState)
+ // migrate data
+ versionedData = await migrator.migrateData(versionedData)
+ // write to disk
+ diskStore.putState(versionedData)
+ // return just the data
+ return versionedData.data
}
function setupController (initState) {
-
//
// MetaMask Controller
//
@@ -68,6 +66,8 @@ function setupController (initState) {
showUnapprovedTx: triggerUi,
// initial state
initState,
+ // platform specific api
+ platform,
})
global.metamaskController = controller
@@ -78,8 +78,8 @@ function setupController (initState) {
diskStore
)
- function versionifyData(state) {
- let versionedData = diskStore.getState()
+ function versionifyData (state) {
+ const versionedData = diskStore.getState()
versionedData.data = state
return versionedData
}
@@ -114,13 +114,13 @@ function setupController (initState) {
//
updateBadge()
- controller.txManager.on('updateBadge', updateBadge)
+ controller.txController.on('updateBadge', updateBadge)
controller.messageManager.on('updateBadge', updateBadge)
// plugin badge text
function updateBadge () {
var label = ''
- var unapprovedTxCount = controller.txManager.unapprovedTxCount
+ var unapprovedTxCount = controller.txController.unapprovedTxCount
var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
var count = unapprovedTxCount + unapprovedMsgCount
if (count) {
@@ -131,7 +131,6 @@ function setupController (initState) {
}
return Promise.resolve()
-
}
//
@@ -140,7 +139,7 @@ function setupController (initState) {
// popup trigger
function triggerUi () {
- if (!popupIsOpen) notification.show()
+ if (!popupIsOpen) notificationManager.showPopup()
}
// On first install, open a window to MetaMask website to how-it-works.
diff --git a/app/scripts/config.js b/app/scripts/config.js
index b4541a04a..8e28db80e 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -1,14 +1,15 @@
const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
-const TESTNET_RPC_URL = 'https://ropsten.infura.io/metamask'
-const DEFAULT_RPC_URL = TESTNET_RPC_URL
+const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask'
+const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
+const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
module.exports = {
network: {
- default: DEFAULT_RPC_URL,
mainnet: MAINET_RPC_URL,
- testnet: TESTNET_RPC_URL,
- morden: TESTNET_RPC_URL,
+ ropsten: ROPSTEN_RPC_URL,
+ kovan: KOVAN_RPC_URL,
+ rinkeby: RINKEBY_RPC_URL,
},
}
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index ab64dc9fa..291b922e8 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -2,7 +2,7 @@ const LocalMessageDuplexStream = require('post-message-stream')
const PongStream = require('ping-pong-stream/pong')
const PortStream = require('./lib/port-stream.js')
const ObjectMultiplex = require('./lib/obj-multiplex')
-const extension = require('./lib/extension')
+const extension = require('extensionizer')
const fs = require('fs')
const path = require('path')
@@ -61,14 +61,22 @@ function setupStreams () {
// ignore unused channels (handled by background)
mx.ignoreStream('provider')
mx.ignoreStream('publicConfig')
- mx.ignoreStream('reload')
}
function shouldInjectWeb3 () {
- return isAllowedSuffix(window.location.href)
+ return doctypeCheck() || suffixCheck()
}
-function isAllowedSuffix (testCase) {
+function doctypeCheck () {
+ const doctype = window.document.doctype
+ if (doctype) {
+ return doctype.name === 'html'
+ } else {
+ return false
+ }
+}
+
+function suffixCheck () {
var prohibitedTypes = ['xml', 'pdf']
var currentUrl = window.location.href
var currentRegex
diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js
index c66eb2bd4..6fb4ee114 100644
--- a/app/scripts/controllers/address-book.js
+++ b/app/scripts/controllers/address-book.js
@@ -39,11 +39,11 @@ class AddressBookController {
// 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()
+ const addressBook = this._getAddressBook()
+ const 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() })
+ const addressBookIndex = addressBook.findIndex((element) => { return element.address.toLowerCase() === address.toLowerCase() || element.name === name })
+ const 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)
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
index c4904f8ac..1f20dc005 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -45,15 +45,17 @@ class CurrencyController {
updateConversionRate () {
const currentCurrency = this.getCurrentCurrency()
- return fetch(`https://www.cryptonator.com/api/ticker/eth-${currentCurrency}`)
+ return fetch(`https://api.cryptonator.com/api/ticker/eth-${currentCurrency}`)
.then(response => response.json())
.then((parsedResponse) => {
this.setConversionRate(Number(parsedResponse.ticker.price))
this.setConversionDate(Number(parsedResponse.timestamp))
}).catch((err) => {
- console.warn('MetaMask - Failed to query currency conversion.')
- this.setConversionRate(0)
- this.setConversionDate('N/A')
+ if (err) {
+ console.warn('MetaMask - Failed to query currency conversion.')
+ this.setConversionRate(0)
+ this.setConversionDate('N/A')
+ }
})
}
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js
new file mode 100644
index 000000000..c07f13b8d
--- /dev/null
+++ b/app/scripts/controllers/network.js
@@ -0,0 +1,129 @@
+const EventEmitter = require('events')
+const MetaMaskProvider = require('web3-provider-engine/zero.js')
+const ObservableStore = require('obs-store')
+const ComposedStore = require('obs-store/lib/composed')
+const extend = require('xtend')
+const EthQuery = require('eth-query')
+const RPC_ADDRESS_LIST = require('../config.js').network
+const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
+
+module.exports = class NetworkController extends EventEmitter {
+ constructor (config) {
+ super()
+ this.networkStore = new ObservableStore('loading')
+ config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
+ this.providerStore = new ObservableStore(config.provider)
+ this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
+ this._providerListeners = {}
+
+ this.on('networkDidChange', this.lookupNetwork)
+ this.providerStore.subscribe((state) => this.switchNetwork({rpcUrl: state.rpcTarget}))
+ }
+
+ get provider () {
+ return this._proxy
+ }
+
+ set provider (provider) {
+ this._provider = provider
+ }
+
+ initializeProvider (opts) {
+ this.providerInit = opts
+ this._provider = MetaMaskProvider(opts)
+ this._proxy = new Proxy(this._provider, {
+ get: (obj, name) => {
+ if (name === 'on') return this._on.bind(this)
+ return this._provider[name]
+ },
+ set: (obj, name, value) => {
+ this._provider[name] = value
+ },
+ })
+ this.provider.on('block', this._logBlock.bind(this))
+ this.provider.on('error', this.verifyNetwork.bind(this))
+ this.ethQuery = new EthQuery(this.provider)
+ this.lookupNetwork()
+ return this.provider
+ }
+
+ switchNetwork (providerInit) {
+ this.setNetworkState('loading')
+ const newInit = extend(this.providerInit, providerInit)
+ this.providerInit = newInit
+
+ this._provider.removeAllListeners()
+ this._provider.stop()
+ this.provider = MetaMaskProvider(newInit)
+ // apply the listners created by other controllers
+ Object.keys(this._providerListeners).forEach((key) => {
+ this._providerListeners[key].forEach((handler) => this._provider.addListener(key, handler))
+ })
+ this.emit('networkDidChange')
+ }
+
+
+ verifyNetwork () {
+ // Check network when restoring connectivity:
+ if (this.isNetworkLoading()) this.lookupNetwork()
+ }
+
+ getNetworkState () {
+ return this.networkStore.getState()
+ }
+
+ setNetworkState (network) {
+ return this.networkStore.putState(network)
+ }
+
+ isNetworkLoading () {
+ return this.getNetworkState() === 'loading'
+ }
+
+ lookupNetwork () {
+ this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
+ if (err) return this.setNetworkState('loading')
+ log.info('web3.getNetwork returned ' + network)
+ this.setNetworkState(network)
+ })
+ }
+
+ setRpcTarget (rpcUrl) {
+ this.providerStore.updateState({
+ type: 'rpc',
+ rpcTarget: rpcUrl,
+ })
+ }
+
+ getCurrentRpcAddress () {
+ const provider = this.getProviderConfig()
+ if (!provider) return null
+ return this.getRpcAddressForType(provider.type)
+ }
+
+ setProviderType (type) {
+ if (type === this.getProviderConfig().type) return
+ const rpcTarget = this.getRpcAddressForType(type)
+ this.providerStore.updateState({type, rpcTarget})
+ }
+
+ getProviderConfig () {
+ return this.providerStore.getState()
+ }
+
+ getRpcAddressForType (type, provider = this.getProviderConfig()) {
+ if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type]
+ return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC
+ }
+
+ _logBlock (block) {
+ log.info(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
+ this.verifyNetwork()
+ }
+
+ _on (event, handler) {
+ if (!this._providerListeners[event]) this._providerListeners[event] = []
+ this._providerListeners[event].push(handler)
+ this._provider.on(event, handler)
+ }
+}
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index c7f675a41..7212c7c43 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -36,8 +36,8 @@ class PreferencesController {
}
addToFrequentRpcList (_url) {
- let rpcList = this.getFrequentRpcList()
- let index = rpcList.findIndex((element) => { return element === _url })
+ const rpcList = this.getFrequentRpcList()
+ const index = rpcList.findIndex((element) => { return element === _url })
if (index !== -1) {
rpcList.splice(index, 1)
}
@@ -53,13 +53,9 @@ class PreferencesController {
getFrequentRpcList () {
return this.store.getState().frequentRpcList
}
-
//
// PRIVATE METHODS
//
-
-
-
}
module.exports = PreferencesController
diff --git a/app/scripts/transaction-manager.js b/app/scripts/controllers/transactions.js
index c6cfdf11d..faccf1ab1 100644
--- a/app/scripts/transaction-manager.js
+++ b/app/scripts/controllers/transactions.js
@@ -4,11 +4,14 @@ const extend = require('xtend')
const Semaphore = require('semaphore')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
-const BN = require('ethereumjs-util').BN
-const TxProviderUtil = require('./lib/tx-utils')
-const createId = require('./lib/random-id')
+const TxProviderUtil = require('../lib/tx-utils')
+const createId = require('../lib/random-id')
+const denodeify = require('denodeify')
-module.exports = class TransactionManager extends EventEmitter {
+const RETRY_LIMIT = 200
+const RESUBMIT_INTERVAL = 10000 // Ten seconds
+
+module.exports = class TransactionController extends EventEmitter {
constructor (opts) {
super()
this.store = new ObservableStore(extend({
@@ -20,16 +23,19 @@ module.exports = class TransactionManager extends EventEmitter {
this.txHistoryLimit = opts.txHistoryLimit
this.provider = opts.provider
this.blockTracker = opts.blockTracker
- this.txProviderUtils = new TxProviderUtil(this.provider)
+ this.query = opts.ethQuery
+ this.txProviderUtils = new TxProviderUtil(this.query)
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
this.signEthTx = opts.signTransaction
this.nonceLock = Semaphore(1)
// memstore is computed from a few different stores
this._updateMemstore()
- this.store.subscribe(() => this._updateMemstore() )
- this.networkStore.subscribe(() => this._updateMemstore() )
- this.preferencesStore.subscribe(() => this._updateMemstore() )
+ this.store.subscribe(() => this._updateMemstore())
+ this.networkStore.subscribe(() => this._updateMemstore())
+ this.preferencesStore.subscribe(() => this._updateMemstore())
+
+ this.continuallyResubmitPendingTxs()
}
getState () {
@@ -37,7 +43,7 @@ module.exports = class TransactionManager extends EventEmitter {
}
getNetwork () {
- return this.networkStore.getState().network
+ return this.networkStore.getState()
}
getSelectedAddress () {
@@ -46,28 +52,41 @@ module.exports = class TransactionManager extends EventEmitter {
// Returns the tx list
getTxList () {
- let network = this.getNetwork()
- let fullTxList = this.store.getState().transactions
+ const network = this.getNetwork()
+ const fullTxList = this.getFullTxList()
return fullTxList.filter(txMeta => txMeta.metamaskNetworkId === network)
}
+ // Returns the number of txs for the current network.
+ getTxCount () {
+ return this.getTxList().length
+ }
+
+ // Returns the full tx list across all networks
+ getFullTxList () {
+ return this.store.getState().transactions
+ }
+
// Adds a tx to the txlist
addTx (txMeta) {
- var txList = this.getTxList()
- var txHistoryLimit = this.txHistoryLimit
+ const txCount = this.getTxCount()
+ const network = this.getNetwork()
+ const fullTxList = this.getFullTxList()
+ const txHistoryLimit = this.txHistoryLimit
- // checks if the length of th tx history is
+ // checks if the length of the tx history is
// longer then desired persistence limit
// and then if it is removes only confirmed
// or rejected tx's.
// not tx's that are pending or unapproved
- if (txList.length > txHistoryLimit - 1) {
- var index = txList.findIndex((metaTx) => metaTx.status === 'confirmed' || metaTx.status === 'rejected')
- txList.splice(index, 1)
+ if (txCount > txHistoryLimit - 1) {
+ var index = fullTxList.findIndex((metaTx) => ((metaTx.status === 'confirmed' || metaTx.status === 'rejected') && network === txMeta.metamaskNetworkId))
+ fullTxList.splice(index, 1)
}
- txList.push(txMeta)
+ fullTxList.push(txMeta)
+ this._saveTxList(fullTxList)
+ this.emit('update')
- this._saveTxList(txList)
this.once(`${txMeta.id}:signed`, function (txId) {
this.removeAllListeners(`${txMeta.id}:rejected`)
})
@@ -89,7 +108,7 @@ module.exports = class TransactionManager extends EventEmitter {
//
updateTx (txMeta) {
var txId = txMeta.id
- var txList = this.getTxList()
+ var txList = this.getFullTxList()
var index = txList.findIndex(txData => txData.id === txId)
txList[index] = txMeta
this._saveTxList(txList)
@@ -109,44 +128,38 @@ module.exports = class TransactionManager extends EventEmitter {
async.waterfall([
// validate
(cb) => this.txProviderUtils.validateTxParams(txParams, cb),
- // prepare txMeta
+ // construct txMeta
(cb) => {
- // create txMeta obj with parameters and meta data
- let time = (new Date()).getTime()
- let txId = createId()
- txParams.metamaskId = txId
- txParams.metamaskNetworkId = this.getNetwork()
txMeta = {
- id: txId,
- time: time,
+ id: createId(),
+ time: (new Date()).getTime(),
status: 'unapproved',
metamaskNetworkId: this.getNetwork(),
txParams: txParams,
}
- // calculate metadata for tx
- this.txProviderUtils.analyzeGasUsage(txMeta, cb)
+ cb()
},
+ // add default tx params
+ (cb) => this.addTxDefaults(txMeta, cb),
// save txMeta
(cb) => {
this.addTx(txMeta)
- this.setMaxTxCostAndFee(txMeta)
cb(null, txMeta)
},
], done)
}
- setMaxTxCostAndFee (txMeta) {
- var txParams = txMeta.txParams
- var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16)
- var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
- var txFee = gasCost.mul(gasPrice)
- var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
- var maxCost = txValue.add(txFee)
- txMeta.txFee = txFee
- txMeta.txValue = txValue
- txMeta.maxCost = maxCost
- txMeta.gasPrice = gasPrice
- this.updateTx(txMeta)
+ addTxDefaults (txMeta, cb) {
+ const txParams = txMeta.txParams
+ // ensure value
+ txParams.value = txParams.value || '0x0'
+ this.query.gasPrice((err, gasPrice) => {
+ if (err) return cb(err)
+ // set gasPrice
+ txParams.gasPrice = gasPrice
+ // set gasLimit
+ this.txProviderUtils.analyzeGasUsage(txMeta, cb)
+ })
}
getUnapprovedTxList () {
@@ -172,7 +185,10 @@ module.exports = class TransactionManager extends EventEmitter {
], (err) => {
self.nonceLock.leave()
if (err) {
- this.setTxStatusFailed(txId)
+ this.setTxStatusFailed(txId, {
+ errCode: err.errCode || err,
+ message: err.message || 'Transaction failed during approval',
+ })
return cb(err)
}
cb()
@@ -186,7 +202,7 @@ module.exports = class TransactionManager extends EventEmitter {
}
fillInTxParams (txId, cb) {
- let txMeta = this.getTx(txId)
+ const txMeta = this.getTx(txId)
this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => {
if (err) return cb(err)
this.updateTx(txMeta)
@@ -194,11 +210,23 @@ module.exports = class TransactionManager extends EventEmitter {
})
}
+ getChainId () {
+ const networkState = this.networkStore.getState()
+ const getChainId = parseInt(networkState.network)
+ if (Number.isNaN(getChainId)) {
+ return 0
+ } else {
+ return getChainId
+ }
+ }
+
signTransaction (txId, cb) {
- let txMeta = this.getTx(txId)
- let txParams = txMeta.txParams
- let fromAddress = txParams.from
- let ethTx = this.txProviderUtils.buildEthTxFromParams(txParams)
+ const txMeta = this.getTx(txId)
+ const txParams = txMeta.txParams
+ const fromAddress = txParams.from
+ // add network/chain id
+ txParams.chainId = this.getChainId()
+ const ethTx = this.txProviderUtils.buildEthTxFromParams(txParams)
this.signEthTx(ethTx, fromAddress).then(() => {
this.setTxStatusSigned(txMeta.id)
cb(null, ethUtil.bufferToHex(ethTx.serialize()))
@@ -207,7 +235,11 @@ module.exports = class TransactionManager extends EventEmitter {
})
}
- publishTransaction (txId, rawTx, cb) {
+ publishTransaction (txId, rawTx, cb = warn) {
+ const txMeta = this.getTx(txId)
+ txMeta.rawTx = rawTx
+ this.updateTx(txMeta)
+
this.txProviderUtils.publishTransaction(rawTx, (err, txHash) => {
if (err) return cb(err)
this.setTxHash(txId, txHash)
@@ -219,7 +251,7 @@ module.exports = class TransactionManager extends EventEmitter {
// receives a txHash records the tx as signed
setTxHash (txId, txHash) {
// Add the tx hash to the persisted meta-tx object
- let txMeta = this.getTx(txId)
+ const txMeta = this.getTx(txId)
txMeta.hash = txHash
this.updateTx(txMeta)
}
@@ -291,7 +323,10 @@ module.exports = class TransactionManager extends EventEmitter {
this._setTxStatus(txId, 'confirmed')
}
- setTxStatusFailed (txId) {
+ setTxStatusFailed (txId, reason) {
+ const txMeta = this.getTx(txId)
+ txMeta.err = reason
+ this.updateTx(txMeta)
this._setTxStatus(txId, 'failed')
}
@@ -312,14 +347,13 @@ module.exports = class TransactionManager extends EventEmitter {
var txHash = txMeta.hash
var txId = txMeta.id
if (!txHash) {
- txMeta.err = {
+ const errReason = {
errCode: 'No hash was provided',
message: 'We had an error while submitting this transaction, please try again.',
}
- this.updateTx(txMeta)
- return this.setTxStatusFailed(txId)
+ return this.setTxStatusFailed(txId, errReason)
}
- this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
+ this.query.getTransactionByHash(txHash, (err, txParams) => {
if (err || !txParams) {
if (!txParams) return
txMeta.err = {
@@ -328,7 +362,7 @@ module.exports = class TransactionManager extends EventEmitter {
message: 'There was a problem loading this transaction.',
}
this.updateTx(txMeta)
- return console.error(err)
+ return log.error(err)
}
if (txParams.blockNumber) {
this.setTxStatusConfirmed(txId)
@@ -354,6 +388,7 @@ module.exports = class TransactionManager extends EventEmitter {
this.emit(`${txMeta.id}:${status}`, txId)
if (status === 'submitted' || status === 'rejected') {
this.emit(`${txMeta.id}:finished`, txMeta)
+
}
this.updateTx(txMeta)
this.emit('updateBadge')
@@ -373,7 +408,47 @@ module.exports = class TransactionManager extends EventEmitter {
})
this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
}
+
+ continuallyResubmitPendingTxs () {
+ const pending = this.getTxsByMetaData('status', 'submitted')
+ const resubmit = denodeify(this.resubmitTx.bind(this))
+ Promise.all(pending.map(txMeta => resubmit(txMeta)))
+ .catch((reason) => {
+ log.info('Problem resubmitting tx', reason)
+ })
+ .then(() => {
+ global.setTimeout(() => {
+ this.continuallyResubmitPendingTxs()
+ }, RESUBMIT_INTERVAL)
+ })
+ }
+
+ resubmitTx (txMeta, cb) {
+ // Increment a try counter.
+ if (!('retryCount' in txMeta)) {
+ txMeta.retryCount = 0
+ }
+
+ // Only auto-submit already-signed txs:
+ if (!('rawTx' in txMeta)) {
+ return cb()
+ }
+
+ if (txMeta.retryCount > RETRY_LIMIT) {
+ txMeta.err = {
+ isWarning: true,
+ message: 'Gave up submitting tx.',
+ }
+ this.updateTx(txMeta)
+ return log.error(txMeta.err.message)
+ }
+
+ txMeta.retryCount++
+ const rawTx = txMeta.rawTx
+ this.txProviderUtils.publishTransaction(rawTx, cb)
+ }
+
}
-const warn = () => console.warn('warn was used no cb provided')
+const warn = () => log.warn('warn was used no cb provided')
diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js
index 46fcde998..5d89a0a21 100644
--- a/app/scripts/first-time-state.js
+++ b/app/scripts/first-time-state.js
@@ -6,9 +6,11 @@ const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
// The default state of MetaMask
//
module.exports = {
- config: {
+ config: {},
+ NetworkController: {
provider: {
- type: (METAMASK_DEBUG || env === 'test') ? 'testnet' : 'mainnet',
+ type: (METAMASK_DEBUG || env === 'test') ? 'rinkeby' : 'mainnet',
+ type: 'rinkeby',
},
},
}
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 419f78cd6..ec764535e 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -31,26 +31,11 @@ web3.setProvider = function () {
console.log('MetaMask - overrode web3.setProvider')
}
console.log('MetaMask - injected web3')
-// export global web3, with usage-detection reload fn
-var triggerReload = setupDappAutoReload(web3)
-
-// listen for reset requests from metamask
-var reloadStream = inpageProvider.multiStream.createStream('reload')
-reloadStream.once('data', triggerReload)
-
-// setup ping timeout autoreload
-// LocalMessageDuplexStream does not self-close, so reload if pingStream fails
-// var pingChannel = inpageProvider.multiStream.createStream('pingpong')
-// var pingStream = new PingStream({ objectMode: true })
-// wait for first successful reponse
-
-// disable pingStream until https://github.com/MetaMask/metamask-plugin/issues/746 is resolved more gracefully
-// metamaskStream.once('data', function(){
-// pingStream.pipe(pingChannel).pipe(pingStream)
-// })
-// endOfStream(pingStream, triggerReload)
+// export global web3, with usage-detection
+setupDappAutoReload(web3, inpageProvider.publicConfigStore)
// set web3 defaultAccount
+
inpageProvider.publicConfigStore.subscribe(function (state) {
web3.eth.defaultAccount = state.selectedAddress
})
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index 72f613641..5b3c80e40 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -187,7 +187,7 @@ class KeyringController extends EventEmitter {
.then((accounts) => {
switch (type) {
case 'Simple Key Pair':
- let isNotIncluded = !accounts.find((key) => key === newAccount[0] || key === ethUtil.stripHexPrefix(newAccount[0]))
+ const isNotIncluded = !accounts.find((key) => key === newAccount[0] || key === ethUtil.stripHexPrefix(newAccount[0]))
return (isNotIncluded) ? Promise.resolve(newAccount) : Promise.reject(new Error('The account you\'re are trying to import is a duplicate'))
default:
return Promise.resolve(newAccount)
@@ -324,6 +324,7 @@ class KeyringController extends EventEmitter {
if (!firstAccount) throw new Error('KeyringController - No account found on keychain.')
const hexAccount = normalizeAddress(firstAccount)
this.emit('newAccount', hexAccount)
+ this.emit('newVault', hexAccount)
return this.setupAccounts(accounts)
})
.then(this.persistAllKeyrings.bind(this))
@@ -581,7 +582,7 @@ class KeyringController extends EventEmitter {
})
}
- _updateMemStoreKeyrings() {
+ _updateMemStoreKeyrings () {
Promise.all(this.keyrings.map(this.displayForKeyring))
.then((keyrings) => {
this.memStore.updateState({ keyrings })
diff --git a/app/scripts/lib/auto-faucet.js b/app/scripts/lib/auto-faucet.js
index 1e86f735e..38d54ba5e 100644
--- a/app/scripts/lib/auto-faucet.js
+++ b/app/scripts/lib/auto-faucet.js
@@ -3,10 +3,18 @@ const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
const env = process.env.METAMASK_ENV
module.exports = function (address) {
- if (METAMASK_DEBUG || env === 'test') return // Don't faucet in development or test
- var http = new XMLHttpRequest()
- var data = address
- http.open('POST', uri, true)
- http.setRequestHeader('Content-type', 'application/rawdata')
- http.send(data)
+ // Don't faucet in development or test
+ if (METAMASK_DEBUG === true || env === 'test') return
+ global.log.info('auto-fauceting:', address)
+ const data = address
+ const headers = new Headers()
+ headers.append('Content-type', 'application/rawdata')
+ fetch(uri, {
+ method: 'POST',
+ headers,
+ body: data,
+ })
+ .catch((err) => {
+ console.error(err)
+ })
}
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
index 1302df35f..534047330 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -1,30 +1,33 @@
-const once = require('once')
-const ensnare = require('ensnare')
-
module.exports = setupDappAutoReload
-function setupDappAutoReload (web3) {
+function setupDappAutoReload (web3, observable) {
// export web3 as a global, checking for usage
- var pageIsUsingWeb3 = false
- var resetWasRequested = false
- global.web3 = ensnare(web3, once(function () {
- // if web3 usage happened after a reset request, trigger reset late
- if (resetWasRequested) return triggerReset()
- // mark web3 as used
- pageIsUsingWeb3 = true
- // reset web3 reference
- global.web3 = web3
- }))
+ global.web3 = new Proxy(web3, {
+ get: (_web3, name) => {
+ // get the time of use
+ if (name !== '_used') _web3._used = Date.now()
+ return _web3[name]
+ },
+ set: (_web3, name, value) => {
+ _web3[name] = value
+ },
+ })
+ var networkVersion
- return handleResetRequest
+ observable.subscribe(function (state) {
+ // get the initial network
+ const curentNetVersion = state.networkVersion
+ if (!networkVersion) networkVersion = curentNetVersion
- function handleResetRequest () {
- resetWasRequested = true
- // ignore if web3 was not used
- if (!pageIsUsingWeb3) return
- // reload after short timeout
- setTimeout(triggerReset, 500)
- }
+ if (curentNetVersion !== networkVersion && web3._used) {
+ const timeSinceUse = Date.now() - web3._used
+ // if web3 was recently used then delay the reloading of the page
+ timeSinceUse > 500 ? triggerReset() : setTimeout(triggerReset, 500)
+ // prevent reentry into if statement if state updates again before
+ // reload
+ networkVersion = curentNetVersion
+ }
+ })
}
// reload the page
diff --git a/app/scripts/lib/buy-eth-url.js b/app/scripts/lib/buy-eth-url.js
new file mode 100644
index 000000000..b9dde3c28
--- /dev/null
+++ b/app/scripts/lib/buy-eth-url.js
@@ -0,0 +1,23 @@
+module.exports = getBuyEthUrl
+
+function getBuyEthUrl ({ network, amount, address }) {
+ let url
+ switch (network) {
+ case '1':
+ url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
+ break
+
+ case '3':
+ url = 'https://faucet.metamask.io/'
+ break
+
+ case '4':
+ url = 'https://www.rinkeby.io/'
+ break
+
+ case '42':
+ url = 'https://github.com/kovan-testnet/faucet'
+ break
+ }
+ return url
+}
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 6868637e5..9c0dffe9c 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -1,10 +1,12 @@
-const MetamaskConfig = require('../config.js')
const ethUtil = require('ethereumjs-util')
const normalize = require('eth-sig-util').normalize
+const MetamaskConfig = require('../config.js')
+
-const TESTNET_RPC = MetamaskConfig.network.testnet
const MAINNET_RPC = MetamaskConfig.network.mainnet
-const MORDEN_RPC = MetamaskConfig.network.morden
+const ROPSTEN_RPC = MetamaskConfig.network.ropsten
+const KOVAN_RPC = MetamaskConfig.network.kovan
+const RINKEBY_RPC = MetamaskConfig.network.rinkeby
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
@@ -32,36 +34,6 @@ ConfigManager.prototype.getConfig = function () {
return data.config
}
-ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
- var config = this.getConfig()
- config.provider = {
- type: 'rpc',
- rpcTarget: rpcUrl,
- }
- this.setConfig(config)
-}
-
-ConfigManager.prototype.setProviderType = function (type) {
- var config = this.getConfig()
- config.provider = {
- type: type,
- }
- this.setConfig(config)
-}
-
-ConfigManager.prototype.useEtherscanProvider = function () {
- var config = this.getConfig()
- config.provider = {
- type: 'etherscan',
- }
- this.setConfig(config)
-}
-
-ConfigManager.prototype.getProvider = function () {
- var config = this.getConfig()
- return config.provider
-}
-
ConfigManager.prototype.setData = function (data) {
this.store.putState(data)
}
@@ -135,6 +107,35 @@ ConfigManager.prototype.getSeedWords = function () {
var data = this.getData()
return data.seedWords
}
+ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
+ var config = this.getConfig()
+ config.provider = {
+ type: 'rpc',
+ rpcTarget: rpcUrl,
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.setProviderType = function (type) {
+ var config = this.getConfig()
+ config.provider = {
+ type: type,
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.useEtherscanProvider = function () {
+ var config = this.getConfig()
+ config.provider = {
+ type: 'etherscan',
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.getProvider = function () {
+ var config = this.getConfig()
+ return config.provider
+}
ConfigManager.prototype.getCurrentRpcAddress = function () {
var provider = this.getProvider()
@@ -144,14 +145,17 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
case 'mainnet':
return MAINNET_RPC
- case 'testnet':
- return TESTNET_RPC
+ case 'ropsten':
+ return ROPSTEN_RPC
+
+ case 'kovan':
+ return KOVAN_RPC
- case 'morden':
- return MORDEN_RPC
+ case 'rinkeby':
+ return RINKEBY_RPC
default:
- return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
+ return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC
}
}
diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js
index 8812a507b..ebba98f5c 100644
--- a/app/scripts/lib/eth-store.js
+++ b/app/scripts/lib/eth-store.js
@@ -10,7 +10,7 @@
const async = require('async')
const EthQuery = require('eth-query')
const ObservableStore = require('obs-store')
-function noop() {}
+function noop () {}
class EthereumStore extends ObservableStore {
@@ -19,6 +19,9 @@ class EthereumStore extends ObservableStore {
super({
accounts: {},
transactions: {},
+ currentBlockNumber: '0',
+ currentBlockHash: '',
+ currentBlockGasLimit: '',
})
this._provider = opts.provider
this._query = new EthQuery(this._provider)
@@ -69,6 +72,9 @@ class EthereumStore extends ObservableStore {
_updateForBlock (block) {
const blockNumber = '0x' + block.number.toString('hex')
this._currentBlockNumber = blockNumber
+ this.updateState({ currentBlockNumber: parseInt(blockNumber) })
+ this.updateState({ currentBlockHash: `0x${block.hash.toString('hex')}`})
+ this.updateState({ currentBlockGasLimit: `0x${block.gasLimit.toString('hex')}` })
async.parallel([
this._updateAccounts.bind(this),
this._updateTransactions.bind(this, blockNumber),
@@ -129,4 +135,4 @@ class EthereumStore extends ObservableStore {
}
-module.exports = EthereumStore \ No newline at end of file
+module.exports = EthereumStore
diff --git a/app/scripts/lib/extension-instance.js b/app/scripts/lib/extension-instance.js
deleted file mode 100644
index 628b62e3f..000000000
--- a/app/scripts/lib/extension-instance.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const apis = [
- 'alarms',
- 'bookmarks',
- 'browserAction',
- 'commands',
- 'contextMenus',
- 'cookies',
- 'downloads',
- 'events',
- 'extension',
- 'extensionTypes',
- 'history',
- 'i18n',
- 'idle',
- 'notifications',
- 'pageAction',
- 'runtime',
- 'storage',
- 'tabs',
- 'webNavigation',
- 'webRequest',
- 'windows',
-]
-
-function Extension () {
- const _this = this
-
- apis.forEach(function (api) {
-
- _this[api] = null
-
- try {
- if (chrome[api]) {
- _this[api] = chrome[api]
- }
- } catch (e) {}
-
- try {
- if (window[api]) {
- _this[api] = window[api]
- }
- } catch (e) {}
-
- try {
- if (browser[api]) {
- _this[api] = browser[api]
- }
- } catch (e) {}
- try {
- _this.api = browser.extension[api]
- } catch (e) {}
- })
-
- try {
- if (browser && browser.runtime) {
- this.runtime = browser.runtime
- }
- } catch (e) {}
-
- try {
- if (browser && browser.browserAction) {
- this.browserAction = browser.browserAction
- }
- } catch (e) {}
-
-}
-
-module.exports = Extension
diff --git a/app/scripts/lib/extension.js b/app/scripts/lib/extension.js
deleted file mode 100644
index 4b670490f..000000000
--- a/app/scripts/lib/extension.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/* Extension.js
- *
- * A module for unifying browser differences in the WebExtension API.
- *
- * Initially implemented because Chrome hides all of their WebExtension API
- * behind a global `chrome` variable, but we'd like to start grooming
- * the code-base for cross-browser extension support.
- *
- * You can read more about the WebExtension API here:
- * https://developer.mozilla.org/en-US/Add-ons/WebExtensions
- */
-
-const Extension = require('./extension-instance')
-module.exports = new Extension()
diff --git a/app/scripts/lib/hex-to-bn.js b/app/scripts/lib/hex-to-bn.js
new file mode 100644
index 000000000..184217279
--- /dev/null
+++ b/app/scripts/lib/hex-to-bn.js
@@ -0,0 +1,7 @@
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+
+module.exports = function hexToBn (hex) {
+ return new BN(ethUtil.stripHexPrefix(hex), 16)
+}
+
diff --git a/app/scripts/lib/id-management.js b/app/scripts/lib/id-management.js
deleted file mode 100644
index 90b3fdb13..000000000
--- a/app/scripts/lib/id-management.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* ID Management
- *
- * This module exists to hold the decrypted credentials for the current session.
- * It therefore exposes sign methods, because it is able to perform these
- * with noa dditional authentication, because its very instantiation
- * means the vault is unlocked.
- */
-
-const ethUtil = require('ethereumjs-util')
-const Transaction = require('ethereumjs-tx')
-
-module.exports = IdManagement
-
-function IdManagement (opts) {
- if (!opts) opts = {}
-
- this.keyStore = opts.keyStore
- this.derivedKey = opts.derivedKey
- this.configManager = opts.configManager
- this.hdPathString = "m/44'/60'/0'/0"
-
- this.getAddresses = function () {
- return this.keyStore.getAddresses(this.hdPathString).map(function (address) { return '0x' + address })
- }
-
- this.signTx = function (txParams) {
-
- // normalize values
- txParams.gasPrice = ethUtil.intToHex(txParams.gasPrice)
- txParams.to = ethUtil.addHexPrefix(txParams.to)
- txParams.from = ethUtil.addHexPrefix(txParams.from.toLowerCase())
- txParams.value = ethUtil.addHexPrefix(txParams.value)
- txParams.data = ethUtil.addHexPrefix(txParams.data)
- txParams.gasLimit = ethUtil.addHexPrefix(txParams.gasLimit || txParams.gas)
- txParams.nonce = ethUtil.addHexPrefix(txParams.nonce)
- var tx = new Transaction(txParams)
-
- // sign tx
- var privKeyHex = this.exportPrivateKey(txParams.from)
- var privKey = ethUtil.toBuffer(privKeyHex)
- tx.sign(privKey)
-
- // Add the tx hash to the persisted meta-tx object
- var txHash = ethUtil.bufferToHex(tx.hash())
- var metaTx = this.configManager.getTx(txParams.metamaskId)
- metaTx.hash = txHash
- this.configManager.updateTx(metaTx)
-
- // return raw serialized tx
- var rawTx = ethUtil.bufferToHex(tx.serialize())
- return rawTx
- }
-
- this.signMsg = function (address, message) {
- // sign message
- var privKeyHex = this.exportPrivateKey(address.toLowerCase())
- var privKey = ethUtil.toBuffer(privKeyHex)
- var msgSig = ethUtil.ecsign(new Buffer(message.replace('0x', ''), 'hex'), privKey)
- var rawMsgSig = ethUtil.bufferToHex(concatSig(msgSig.v, msgSig.r, msgSig.s))
- return rawMsgSig
- }
-
- this.getSeed = function () {
- return this.keyStore.getSeed(this.derivedKey)
- }
-
- this.exportPrivateKey = function (address) {
- var privKeyHex = ethUtil.addHexPrefix(this.keyStore.exportPrivateKey(address, this.derivedKey, this.hdPathString))
- return privKeyHex
- }
-}
-
-function padWithZeroes (number, length) {
- var myString = '' + number
- while (myString.length < length) {
- myString = '0' + myString
- }
- return myString
-}
-
-function concatSig (v, r, s) {
- const rSig = ethUtil.fromSigned(r)
- const sSig = ethUtil.fromSigned(s)
- const vSig = ethUtil.bufferToInt(v)
- const rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64)
- const sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64)
- const vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig))
- return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex')
-}
-
diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js
deleted file mode 100644
index 62d21eee7..000000000
--- a/app/scripts/lib/idStore-migrator.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const IdentityStore = require('./idStore')
-const HdKeyring = require('eth-hd-keyring')
-const sigUtil = require('eth-sig-util')
-const normalize = sigUtil.normalize
-const denodeify = require('denodeify')
-
-module.exports = class IdentityStoreMigrator {
-
- constructor ({ configManager }) {
- this.configManager = configManager
- const hasOldVault = this.hasOldVault()
- if (!hasOldVault) {
- this.idStore = new IdentityStore({ configManager })
- }
- }
-
- migratedVaultForPassword (password) {
- const hasOldVault = this.hasOldVault()
- const configManager = this.configManager
-
- if (!this.idStore) {
- this.idStore = new IdentityStore({ configManager })
- }
-
- if (!hasOldVault) {
- return Promise.resolve(null)
- }
-
- const idStore = this.idStore
- const submitPassword = denodeify(idStore.submitPassword.bind(idStore))
-
- return submitPassword(password)
- .then(() => {
- const serialized = this.serializeVault()
- return this.checkForLostAccounts(serialized)
- })
- }
-
- serializeVault () {
- const mnemonic = this.idStore._idmgmt.getSeed()
- const numberOfAccounts = this.idStore._getAddresses().length
-
- return {
- type: 'HD Key Tree',
- data: { mnemonic, numberOfAccounts },
- }
- }
-
- checkForLostAccounts (serialized) {
- const hd = new HdKeyring()
- return hd.deserialize(serialized.data)
- .then((hexAccounts) => {
- const newAccounts = hexAccounts.map(normalize)
- const oldAccounts = this.idStore._getAddresses().map(normalize)
- const lostAccounts = oldAccounts.reduce((result, account) => {
- if (newAccounts.includes(account)) {
- return result
- } else {
- result.push(account)
- return result
- }
- }, [])
-
- return {
- serialized,
- lostAccounts: lostAccounts.map((address) => {
- return {
- address,
- privateKey: this.idStore.exportAccount(address),
- }
- }),
- }
- })
- }
-
- hasOldVault () {
- const wallet = this.configManager.getWallet()
- return wallet
- }
-}
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
deleted file mode 100644
index 01474035e..000000000
--- a/app/scripts/lib/idStore.js
+++ /dev/null
@@ -1,343 +0,0 @@
-const EventEmitter = require('events').EventEmitter
-const inherits = require('util').inherits
-const ethUtil = require('ethereumjs-util')
-const KeyStore = require('eth-lightwallet').keystore
-const clone = require('clone')
-const extend = require('xtend')
-const autoFaucet = require('./auto-faucet')
-const DEFAULT_RPC = 'https://testrpc.metamask.io/'
-const IdManagement = require('./id-management')
-
-
-module.exports = IdentityStore
-
-inherits(IdentityStore, EventEmitter)
-function IdentityStore (opts = {}) {
- EventEmitter.call(this)
-
- // we just use the ethStore to auto-add accounts
- this._ethStore = opts.ethStore
- this.configManager = opts.configManager
- // lightwallet key store
- this._keyStore = null
- // lightwallet wrapper
- this._idmgmt = null
-
- this.hdPathString = "m/44'/60'/0'/0"
-
- this._currentState = {
- selectedAddress: null,
- identities: {},
- }
- // not part of serilized metamask state - only kept in memory
-}
-
-//
-// public
-//
-
-IdentityStore.prototype.createNewVault = function (password, cb) {
- delete this._keyStore
- var serializedKeystore = this.configManager.getWallet()
-
- if (serializedKeystore) {
- this.configManager.setData({})
- }
-
- this.purgeCache()
- this._createVault(password, null, (err) => {
- if (err) return cb(err)
-
- this._autoFaucet()
-
- this.configManager.setShowSeedWords(true)
- var seedWords = this._idmgmt.getSeed()
-
- this._loadIdentities()
-
- cb(null, seedWords)
- })
-}
-
-IdentityStore.prototype.recoverSeed = function (cb) {
- this.configManager.setShowSeedWords(true)
- if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.'))
- var seedWords = this._idmgmt.getSeed()
- cb(null, seedWords)
-}
-
-IdentityStore.prototype.recoverFromSeed = function (password, seed, cb) {
- this.purgeCache()
-
- this._createVault(password, seed, (err) => {
- if (err) return cb(err)
-
- this._loadIdentities()
- cb(null, this.getState())
- })
-}
-
-IdentityStore.prototype.setStore = function (store) {
- this._ethStore = store
-}
-
-IdentityStore.prototype.clearSeedWordCache = function (cb) {
- const configManager = this.configManager
- configManager.setShowSeedWords(false)
- cb(null, configManager.getSelectedAccount())
-}
-
-IdentityStore.prototype.getState = function () {
- const configManager = this.configManager
- var seedWords = this.getSeedIfUnlocked()
- return clone(extend(this._currentState, {
- isInitialized: !!configManager.getWallet() && !seedWords,
- isUnlocked: this._isUnlocked(),
- seedWords: seedWords,
- selectedAddress: configManager.getSelectedAccount(),
- }))
-}
-
-IdentityStore.prototype.getSeedIfUnlocked = function () {
- const configManager = this.configManager
- var showSeed = configManager.getShouldShowSeedWords()
- var idmgmt = this._idmgmt
- var shouldShow = showSeed && !!idmgmt
- var seedWords = shouldShow ? idmgmt.getSeed() : null
- return seedWords
-}
-
-IdentityStore.prototype.getSelectedAddress = function () {
- const configManager = this.configManager
- return configManager.getSelectedAccount()
-}
-
-IdentityStore.prototype.setSelectedAddressSync = function (address) {
- const configManager = this.configManager
- if (!address) {
- var addresses = this._getAddresses()
- address = addresses[0]
- }
-
- configManager.setSelectedAccount(address)
- return address
-}
-
-IdentityStore.prototype.setSelectedAddress = function (address, cb) {
- const resultAddress = this.setSelectedAddressSync(address)
- if (cb) return cb(null, resultAddress)
-}
-
-IdentityStore.prototype.revealAccount = function (cb) {
- const derivedKey = this._idmgmt.derivedKey
- const keyStore = this._keyStore
- const configManager = this.configManager
-
- keyStore.setDefaultHdDerivationPath(this.hdPathString)
- keyStore.generateNewAddress(derivedKey, 1)
- const addresses = keyStore.getAddresses()
- const address = addresses[ addresses.length - 1 ]
-
- this._ethStore.addAccount(ethUtil.addHexPrefix(address))
-
- configManager.setWallet(keyStore.serialize())
-
- this._loadIdentities()
- this._didUpdate()
- cb(null)
-}
-
-IdentityStore.prototype.getNetwork = function (err) {
- if (err) {
- this._currentState.network = 'loading'
- this._didUpdate()
- }
-
- this.web3.version.getNetwork((err, network) => {
- if (err) {
- this._currentState.network = 'loading'
- return this._didUpdate()
- }
- if (global.METAMASK_DEBUG) {
- console.log('web3.getNetwork returned ' + network)
- }
- this._currentState.network = network
- this._didUpdate()
- })
-}
-
-IdentityStore.prototype.setLocked = function (cb) {
- delete this._keyStore
- delete this._idmgmt
- cb()
-}
-
-IdentityStore.prototype.submitPassword = function (password, cb) {
- const configManager = this.configManager
- this.tryPassword(password, (err) => {
- if (err) return cb(err)
- // load identities before returning...
- this._loadIdentities()
- cb(null, configManager.getSelectedAccount())
- })
-}
-
-IdentityStore.prototype.exportAccount = function (address, cb) {
- var privateKey = this._idmgmt.exportPrivateKey(address)
- if (cb) cb(null, privateKey)
- return privateKey
-}
-
-// private
-//
-
-IdentityStore.prototype._didUpdate = function () {
- this.emit('update', this.getState())
-}
-
-IdentityStore.prototype._isUnlocked = function () {
- var result = Boolean(this._keyStore) && Boolean(this._idmgmt)
- return result
-}
-
-// load identities from keyStoreet
-IdentityStore.prototype._loadIdentities = function () {
- const configManager = this.configManager
- if (!this._isUnlocked()) throw new Error('not unlocked')
-
- var addresses = this._getAddresses()
- addresses.forEach((address, i) => {
- // // add to ethStore
- if (this._ethStore) {
- this._ethStore.addAccount(ethUtil.addHexPrefix(address))
- }
- // add to identities
- const defaultLabel = 'Account ' + (i + 1)
- const nickname = configManager.nicknameForWallet(address)
- var identity = {
- name: nickname || defaultLabel,
- address: address,
- mayBeFauceting: this._mayBeFauceting(i),
- }
- this._currentState.identities[address] = identity
- })
- this._didUpdate()
-}
-
-IdentityStore.prototype.saveAccountLabel = function (account, label, cb) {
- const configManager = this.configManager
- configManager.setNicknameForWallet(account, label)
- this._loadIdentities()
- cb(null, label)
-}
-
-// mayBeFauceting
-// If on testnet, index 0 may be fauceting.
-// The UI will have to check the balance to know.
-// If there is no balance and it mayBeFauceting,
-// then it is in fact fauceting.
-IdentityStore.prototype._mayBeFauceting = function (i) {
- const configManager = this.configManager
- var config = configManager.getProvider()
- if (i === 0 &&
- config.type === 'rpc' &&
- config.rpcTarget === DEFAULT_RPC) {
- return true
- }
- return false
-}
-
-//
-// keyStore managment - unlocking + deserialization
-//
-
-IdentityStore.prototype.tryPassword = function (password, cb) {
- var serializedKeystore = this.configManager.getWallet()
- var keyStore = KeyStore.deserialize(serializedKeystore)
-
- keyStore.keyFromPassword(password, (err, pwDerivedKey) => {
- if (err) return cb(err)
-
- const isCorrect = keyStore.isDerivedKeyCorrect(pwDerivedKey)
- if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
-
- this._keyStore = keyStore
- this._createIdMgmt(pwDerivedKey)
- cb()
- })
-}
-
-IdentityStore.prototype._createVault = function (password, seedPhrase, cb) {
- const opts = {
- password,
- hdPathString: this.hdPathString,
- }
-
- if (seedPhrase) {
- opts.seedPhrase = seedPhrase
- }
-
- KeyStore.createVault(opts, (err, keyStore) => {
- if (err) return cb(err)
-
- this._keyStore = keyStore
-
- keyStore.keyFromPassword(password, (err, derivedKey) => {
- if (err) return cb(err)
-
- this.purgeCache()
-
- keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
-
- this._createFirstWallet(derivedKey)
- this._createIdMgmt(derivedKey)
- this.setSelectedAddressSync()
-
- cb()
- })
- })
-}
-
-IdentityStore.prototype._createIdMgmt = function (derivedKey) {
- this._idmgmt = new IdManagement({
- keyStore: this._keyStore,
- derivedKey: derivedKey,
- configManager: this.configManager,
- })
-}
-
-IdentityStore.prototype.purgeCache = function () {
- this._currentState.identities = {}
- let accounts
- try {
- accounts = Object.keys(this._ethStore._currentState.accounts)
- } catch (e) {
- accounts = []
- }
- accounts.forEach((address) => {
- this._ethStore.removeAccount(address)
- })
-}
-
-IdentityStore.prototype._createFirstWallet = function (derivedKey) {
- const keyStore = this._keyStore
- keyStore.setDefaultHdDerivationPath(this.hdPathString)
- keyStore.generateNewAddress(derivedKey, 1)
- this.configManager.setWallet(keyStore.serialize())
- var addresses = keyStore.getAddresses()
- this._ethStore.addAccount(ethUtil.addHexPrefix(addresses[0]))
-}
-
-// get addresses and normalize address hexString
-IdentityStore.prototype._getAddresses = function () {
- return this._keyStore.getAddresses(this.hdPathString).map((address) => {
- return ethUtil.addHexPrefix(address)
- })
-}
-
-IdentityStore.prototype._autoFaucet = function () {
- var addresses = this._getAddresses()
- autoFaucet(addresses[0])
-}
-
-// util
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index 92936de2f..8b8623974 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -34,6 +34,7 @@ function MetamaskInpageProvider (connectionStream) {
asyncProvider,
(err) => logStreamDisconnectWarning('MetaMask RpcProvider', err)
)
+ // start and stop polling to unblock first block lock
self.idMap = {}
// handle sendAsync requests via asyncProvider
@@ -85,7 +86,7 @@ MetamaskInpageProvider.prototype.send = function (payload) {
break
case 'net_version':
- let networkVersion = self.publicConfigStore.getState().networkVersion
+ const networkVersion = self.publicConfigStore.getState().networkVersion
result = networkVersion
break
@@ -125,7 +126,7 @@ function eachJsonMessage (payload, transformFn) {
}
}
-function logStreamDisconnectWarning(remoteLabel, err){
+function logStreamDisconnectWarning (remoteLabel, err) {
let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}`
if (err) warningMsg += '\n' + err.stack
console.warn(warningMsg)
diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js
index 711d5f159..f52e048e0 100644
--- a/app/scripts/lib/message-manager.js
+++ b/app/scripts/lib/message-manager.js
@@ -4,7 +4,7 @@ const ethUtil = require('ethereumjs-util')
const createId = require('./random-id')
-module.exports = class MessageManager extends EventEmitter{
+module.exports = class MessageManager extends EventEmitter {
constructor (opts) {
super()
this.memStore = new ObservableStore({
@@ -108,7 +108,7 @@ module.exports = class MessageManager extends EventEmitter{
}
-function normalizeMsgData(data) {
+function normalizeMsgData (data) {
if (data.slice(0, 2) === '0x') {
// data is already hex
return data
diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js
index 312345263..de6f5d5cd 100644
--- a/app/scripts/lib/migrator/index.js
+++ b/app/scripts/lib/migrator/index.js
@@ -1,42 +1,35 @@
-const asyncQ = require('async-q')
-
class Migrator {
constructor (opts = {}) {
- let migrations = opts.migrations || []
+ const migrations = opts.migrations || []
+ // sort migrations by version
this.migrations = migrations.sort((a, b) => a.version - b.version)
- let lastMigration = this.migrations.slice(-1)[0]
+ // grab migration with highest version
+ const lastMigration = this.migrations.slice(-1)[0]
// use specified defaultVersion or highest migration version
this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0
}
// run all pending migrations on meta in place
- migrateData (versionedData = this.generateInitialState()) {
- let remaining = this.migrations.filter(migrationIsPending)
-
- return (
- asyncQ.eachSeries(remaining, (migration) => this.runMigration(versionedData, migration))
- .then(() => versionedData)
- )
-
- // migration is "pending" if hit has a higher
+ async migrateData (versionedData = this.generateInitialState()) {
+ const pendingMigrations = this.migrations.filter(migrationIsPending)
+
+ for (let index in pendingMigrations) {
+ let migration = pendingMigrations[index]
+ versionedData = await migration.migrate(versionedData)
+ if (!versionedData.data) throw new Error('Migrator - migration returned empty data')
+ if (versionedData.version !== undefined && versionedData.meta.version !== migration.version) throw new Error('Migrator - Migration did not update version number correctly')
+ }
+
+ return versionedData
+
+ // migration is "pending" if it has a higher
// version number than currentVersion
- function migrationIsPending(migration) {
+ function migrationIsPending (migration) {
return migration.version > versionedData.meta.version
}
}
- runMigration(versionedData, migration) {
- return (
- migration.migrate(versionedData)
- .then((versionedData) => {
- if (!versionedData.data) return Promise.reject(new Error('Migrator - Migration returned empty data'))
- if (migration.version !== undefined && versionedData.meta.version !== migration.version) return Promise.reject(new Error('Migrator - Migration did not update version number correctly'))
- return Promise.resolve(versionedData)
- })
- )
- }
-
generateInitialState (initState) {
return {
meta: {
diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js
new file mode 100644
index 000000000..7846ef7f0
--- /dev/null
+++ b/app/scripts/lib/notification-manager.js
@@ -0,0 +1,71 @@
+const extension = require('extensionizer')
+const height = 520
+const width = 360
+
+
+class NotificationManager {
+
+ //
+ // Public
+ //
+
+ showPopup () {
+ this._getPopup((err, popup) => {
+ if (err) throw err
+
+ if (popup) {
+ // bring focus to existing popup
+ extension.windows.update(popup.id, { focused: true })
+ } else {
+ // create new popup
+ extension.windows.create({
+ url: 'notification.html',
+ type: 'popup',
+ width,
+ height,
+ })
+ }
+ })
+ }
+
+ closePopup () {
+ this._getPopup((err, popup) => {
+ if (err) throw err
+ if (!popup) return
+ extension.windows.remove(popup.id, console.error)
+ })
+ }
+
+ //
+ // Private
+ //
+
+ _getPopup (cb) {
+ this._getWindows((err, windows) => {
+ if (err) throw err
+ cb(null, this._getPopupIn(windows))
+ })
+ }
+
+ _getWindows (cb) {
+ // Ignore in test environment
+ if (!extension.windows) {
+ return cb()
+ }
+
+ extension.windows.getAll({}, (windows) => {
+ cb(null, windows)
+ })
+ }
+
+ _getPopupIn (windows) {
+ return windows ? windows.find((win) => {
+ return (win && win.type === 'popup' &&
+ win.height === height &&
+ win.width === width)
+ }) : null
+ }
+
+}
+
+module.exports = NotificationManager
diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js
deleted file mode 100644
index 3db1ac6b5..000000000
--- a/app/scripts/lib/notifications.js
+++ /dev/null
@@ -1,65 +0,0 @@
-const extension = require('./extension')
-const height = 520
-const width = 360
-
-const notifications = {
- show,
- getPopup,
- closePopup,
-}
-module.exports = notifications
-window.METAMASK_NOTIFIER = notifications
-
-function show () {
- getPopup((err, popup) => {
- if (err) throw err
-
- if (popup) {
- // bring focus to existing popup
- extension.windows.update(popup.id, { focused: true })
- } else {
- // create new popup
- extension.windows.create({
- url: 'notification.html',
- type: 'popup',
- focused: true,
- width,
- height,
- })
- }
- })
-}
-
-function getWindows (cb) {
- // Ignore in test environment
- if (!extension.windows) {
- return cb()
- }
-
- extension.windows.getAll({}, (windows) => {
- cb(null, windows)
- })
-}
-
-function getPopup (cb) {
- getWindows((err, windows) => {
- if (err) throw err
- cb(null, getPopupIn(windows))
- })
-}
-
-function getPopupIn (windows) {
- return windows ? windows.find((win) => {
- return (win && win.type === 'popup' &&
- win.height === height &&
- win.width === width)
- }) : null
-}
-
-function closePopup () {
- getPopup((err, popup) => {
- if (err) throw err
- if (!popup) return
- extension.windows.remove(popup.id, console.error)
- })
-}
diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js
index bbc978446..6602f5aa8 100644
--- a/app/scripts/lib/personal-message-manager.js
+++ b/app/scripts/lib/personal-message-manager.js
@@ -5,7 +5,7 @@ const createId = require('./random-id')
const hexRe = /^[0-9A-Fa-f]+$/g
-module.exports = class PersonalMessageManager extends EventEmitter{
+module.exports = class PersonalMessageManager extends EventEmitter {
constructor (opts) {
super()
this.memStore = new ObservableStore({
@@ -108,7 +108,7 @@ module.exports = class PersonalMessageManager extends EventEmitter{
this.emit('updateBadge')
}
- normalizeMsgData(data) {
+ normalizeMsgData (data) {
try {
const stripped = ethUtil.stripHexPrefix(data)
if (stripped.match(hexRe)) {
diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js
index c6814c05f..149d93102 100644
--- a/app/scripts/lib/tx-utils.js
+++ b/app/scripts/lib/tx-utils.js
@@ -1,5 +1,4 @@
const async = require('async')
-const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const Transaction = require('ethereumjs-tx')
const normalize = require('eth-sig-util').normalize
@@ -7,53 +6,55 @@ const BN = ethUtil.BN
/*
tx-utils are utility methods for Transaction manager
-its passed a provider and that is passed to ethquery
+its passed ethquery
and used to do things like calculate gas of a tx.
*/
module.exports = class txProviderUtils {
- constructor (provider) {
- this.provider = provider
- this.query = new EthQuery(provider)
+
+ constructor (ethQuery) {
+ this.query = ethQuery
}
- analyzeGasUsage (txData, cb) {
+ analyzeGasUsage (txMeta, cb) {
var self = this
this.query.getBlockByNumber('latest', true, (err, block) => {
if (err) return cb(err)
async.waterfall([
- self.estimateTxGas.bind(self, txData, block.gasLimit),
- self.setTxGas.bind(self, txData, block.gasLimit),
+ self.estimateTxGas.bind(self, txMeta, block.gasLimit),
+ self.setTxGas.bind(self, txMeta, block.gasLimit),
], cb)
})
}
- estimateTxGas (txData, blockGasLimitHex, cb) {
- const txParams = txData.txParams
+ estimateTxGas (txMeta, blockGasLimitHex, cb) {
+ const txParams = txMeta.txParams
// check if gasLimit is already specified
- txData.gasLimitSpecified = Boolean(txParams.gas)
+ txMeta.gasLimitSpecified = Boolean(txParams.gas)
// if not, fallback to block gasLimit
- if (!txData.gasLimitSpecified) {
- txParams.gas = blockGasLimitHex
+ if (!txMeta.gasLimitSpecified) {
+ const blockGasLimitBN = hexToBn(blockGasLimitHex)
+ const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20)
+ txParams.gas = bnToHex(saferGasLimitBN)
}
// run tx, see if it will OOG
this.query.estimateGas(txParams, cb)
}
- setTxGas (txData, blockGasLimitHex, estimatedGasHex, cb) {
- txData.estimatedGas = estimatedGasHex
- const txParams = txData.txParams
+ setTxGas (txMeta, blockGasLimitHex, estimatedGasHex, cb) {
+ txMeta.estimatedGas = estimatedGasHex
+ const txParams = txMeta.txParams
// if gasLimit was specified and doesnt OOG,
// use original specified amount
- if (txData.gasLimitSpecified) {
- txData.estimatedGas = txParams.gas
+ if (txMeta.gasLimitSpecified) {
+ txMeta.estimatedGas = txParams.gas
cb()
return
}
// if gasLimit not originally specified,
// try adding an additional gas buffer to our estimation for safety
- const recommendedGasHex = this.addGasBuffer(txData.estimatedGas, blockGasLimitHex)
+ const recommendedGasHex = this.addGasBuffer(txMeta.estimatedGas, blockGasLimitHex)
txParams.gas = recommendedGasHex
cb()
return
@@ -62,25 +63,26 @@ module.exports = class txProviderUtils {
addGasBuffer (initialGasLimitHex, blockGasLimitHex) {
const initialGasLimitBn = hexToBn(initialGasLimitHex)
const blockGasLimitBn = hexToBn(blockGasLimitHex)
+ const upperGasLimitBn = blockGasLimitBn.muln(0.9)
const bufferedGasLimitBn = initialGasLimitBn.muln(1.5)
-
+
// if initialGasLimit is above blockGasLimit, dont modify it
- if (initialGasLimitBn.gt(blockGasLimitBn)) return bnToHex(initialGasLimitBn)
+ if (initialGasLimitBn.gt(upperGasLimitBn)) return bnToHex(initialGasLimitBn)
// if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
- if (bufferedGasLimitBn.lt(blockGasLimitBn)) return bnToHex(bufferedGasLimitBn)
+ if (bufferedGasLimitBn.lt(upperGasLimitBn)) return bnToHex(bufferedGasLimitBn)
// otherwise use blockGasLimit
- return bnToHex(blockGasLimitBn)
+ return bnToHex(upperGasLimitBn)
}
fillInTxParams (txParams, cb) {
- let fromAddress = txParams.from
- let reqs = {}
+ const fromAddress = txParams.from
+ const reqs = {}
if (isUndef(txParams.gas)) reqs.gas = (cb) => this.query.estimateGas(txParams, cb)
if (isUndef(txParams.gasPrice)) reqs.gasPrice = (cb) => this.query.gasPrice(cb)
if (isUndef(txParams.nonce)) reqs.nonce = (cb) => this.query.getTransactionCount(fromAddress, 'pending', cb)
- async.parallel(reqs, function(err, result) {
+ async.parallel(reqs, function (err, result) {
if (err) return cb(err)
// write results to txParams obj
Object.assign(txParams, result)
@@ -90,16 +92,13 @@ module.exports = class txProviderUtils {
// builds ethTx from txParams object
buildEthTxFromParams (txParams) {
- // apply gas multiplyer
- let gasPrice = hexToBn(txParams.gasPrice)
- // multiply and divide by 100 so as to add percision to integer mul
- txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
// normalize values
txParams.to = normalize(txParams.to)
txParams.from = normalize(txParams.from)
txParams.value = normalize(txParams.value)
txParams.data = normalize(txParams.data)
- txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas)
+ txParams.gas = normalize(txParams.gas || txParams.gasLimit)
+ txParams.gasPrice = normalize(txParams.gasPrice)
txParams.nonce = normalize(txParams.nonce)
// build ethTx
log.info(`Prepared tx for signing: ${JSON.stringify(txParams)}`)
@@ -124,14 +123,20 @@ module.exports = class txProviderUtils {
// util
-function isUndef(value) {
+function isUndef (value) {
return value === undefined
}
-function bnToHex(inputBn) {
+function bnToHex (inputBn) {
return ethUtil.addHexPrefix(inputBn.toString(16))
}
-function hexToBn(inputHex) {
+function hexToBn (inputHex) {
return new BN(ethUtil.stripHexPrefix(inputHex), 16)
-} \ No newline at end of file
+}
+
+function BnMultiplyByFraction (targetBN, numerator, denominator) {
+ const numBN = new BN(numerator)
+ const denomBN = new BN(denominator)
+ return targetBN.mul(numBN).div(denomBN)
+}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 25f9d9e5d..a7eb3d056 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -4,13 +4,12 @@ const promiseToCallback = require('promise-to-callback')
const pipe = require('pump')
const Dnode = require('dnode')
const ObservableStore = require('obs-store')
-const storeTransform = require('obs-store/lib/transform')
const EthStore = require('./lib/eth-store')
const EthQuery = require('eth-query')
const streamIntoProvider = require('web3-stream-provider/handler')
-const MetaMaskProvider = require('web3-provider-engine/zero.js')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const KeyringController = require('./keyring-controller')
+const NetworkController = require('./controllers/network')
const PreferencesController = require('./controllers/preferences')
const CurrencyController = require('./controllers/currency')
const NoticeController = require('./notice-controller')
@@ -18,13 +17,12 @@ const ShapeShiftController = require('./controllers/shapeshift')
const AddressBookController = require('./controllers/address-book')
const MessageManager = require('./lib/message-manager')
const PersonalMessageManager = require('./lib/personal-message-manager')
-const TxManager = require('./transaction-manager')
+const TransactionController = require('./controllers/transactions')
const ConfigManager = require('./lib/config-manager')
-const extension = require('./lib/extension')
const autoFaucet = require('./lib/auto-faucet')
const nodeify = require('./lib/nodeify')
-const IdStoreMigrator = require('./lib/idStore-migrator')
const accountImporter = require('./account-import-strategies')
+const getBuyEthUrl = require('./lib/buy-eth-url')
const version = require('../manifest.json').version
@@ -33,14 +31,17 @@ module.exports = class MetamaskController extends EventEmitter {
constructor (opts) {
super()
this.opts = opts
- let initState = opts.initState || {}
+ const initState = opts.initState || {}
+
+ // platform-specific api
+ this.platform = opts.platform
// observable state store
this.store = new ObservableStore(initState)
// network store
- this.networkStore = new ObservableStore({ network: 'loading' })
+ this.networkController = new NetworkController(initState.NetworkController)
// config manager
this.configManager = new ConfigManager({
store: this.store,
@@ -60,8 +61,6 @@ module.exports = class MetamaskController extends EventEmitter {
// rpc provider
this.provider = this.initializeProvider()
- this.provider.on('block', this.logBlock.bind(this))
- this.provider.on('error', this.verifyNetwork.bind(this))
// eth data query tools
this.ethQuery = new EthQuery(this.provider)
@@ -74,10 +73,12 @@ module.exports = class MetamaskController extends EventEmitter {
this.keyringController = new KeyringController({
initState: initState.KeyringController,
ethStore: this.ethStore,
- getNetwork: this.getNetworkState.bind(this),
+ getNetwork: this.networkController.getNetworkState.bind(this.networkController),
})
this.keyringController.on('newAccount', (address) => {
this.preferencesController.setSelectedAddress(address)
+ })
+ this.keyringController.on('newVault', (address) => {
autoFaucet(address)
})
@@ -87,15 +88,16 @@ module.exports = class MetamaskController extends EventEmitter {
}, this.keyringController)
// tx mgmt
- this.txManager = new TxManager({
- initState: initState.TransactionManager,
- networkStore: this.networkStore,
+ this.txController = new TransactionController({
+ initState: initState.TransactionController || initState.TransactionManager,
+ networkStore: this.networkController.networkStore,
preferencesStore: this.preferencesController.store,
txHistoryLimit: 40,
- getNetwork: this.getNetworkState.bind(this),
+ getNetwork: this.networkController.getNetworkState.bind(this),
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
provider: this.provider,
blockTracker: this.provider,
+ ethQuery: this.ethQuery,
})
// notices
@@ -110,19 +112,14 @@ module.exports = class MetamaskController extends EventEmitter {
initState: initState.ShapeShiftController,
})
- this.lookupNetwork()
+ this.networkController.lookupNetwork()
this.messageManager = new MessageManager()
this.personalMessageManager = new PersonalMessageManager()
this.publicConfigStore = this.initPublicConfigStore()
- // TEMPORARY UNTIL FULL DEPRECATION:
- this.idStoreMigrator = new IdStoreMigrator({
- configManager: this.configManager,
- })
-
// manual disk state subscriptions
- this.txManager.store.subscribe((state) => {
- this.store.updateState({ TransactionManager: state })
+ this.txController.store.subscribe((state) => {
+ this.store.updateState({ TransactionController: state })
})
this.keyringController.store.subscribe((state) => {
this.store.updateState({ KeyringController: state })
@@ -142,11 +139,14 @@ module.exports = class MetamaskController extends EventEmitter {
this.shapeshiftController.store.subscribe((state) => {
this.store.updateState({ ShapeShiftController: state })
})
+ this.networkController.store.subscribe((state) => {
+ this.store.updateState({ NetworkController: state })
+ })
// manual mem state subscriptions
- this.networkStore.subscribe(this.sendUpdate.bind(this))
+ this.networkController.store.subscribe(this.sendUpdate.bind(this))
this.ethStore.subscribe(this.sendUpdate.bind(this))
- this.txManager.memStore.subscribe(this.sendUpdate.bind(this))
+ this.txController.memStore.subscribe(this.sendUpdate.bind(this))
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
@@ -162,17 +162,21 @@ module.exports = class MetamaskController extends EventEmitter {
//
initializeProvider () {
-
- let provider = MetaMaskProvider({
+ return this.networkController.initializeProvider({
static: {
eth_syncing: false,
web3_clientVersion: `MetaMask/v${version}`,
},
- rpcUrl: this.configManager.getCurrentRpcAddress(),
+ rpcUrl: this.networkController.getCurrentRpcAddress(),
// account mgmt
getAccounts: (cb) => {
- let selectedAddress = this.preferencesController.getSelectedAddress()
- let result = selectedAddress ? [selectedAddress] : []
+ const isUnlocked = this.keyringController.memStore.getState().isUnlocked
+ const result = []
+ const selectedAddress = this.preferencesController.getSelectedAddress()
+ // only show address if account is unlocked
+ if (isUnlocked && selectedAddress) {
+ result.push(selectedAddress)
+ }
cb(null, result)
},
// tx signing
@@ -183,26 +187,23 @@ module.exports = class MetamaskController extends EventEmitter {
// new style msg signing
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
})
- return provider
}
initPublicConfigStore () {
// get init state
const publicConfigStore = new ObservableStore()
- // sync publicConfigStore with transform
- pipe(
- this.store,
- storeTransform(selectPublicState.bind(this)),
- publicConfigStore
- )
+ // memStore -> transform -> publicConfigStore
+ this.on('update', (memState) => {
+ const publicState = selectPublicState(memState)
+ publicConfigStore.putState(publicState)
+ })
- function selectPublicState(state) {
- const result = { selectedAddress: undefined }
- try {
- result.selectedAddress = state.PreferencesController.selectedAddress
- result.networkVersion = this.getNetworkState()
- } catch (_) {}
+ function selectPublicState (memState) {
+ const result = {
+ selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
+ networkVersion: memState.network,
+ }
return result
}
@@ -214,7 +215,6 @@ module.exports = class MetamaskController extends EventEmitter {
//
getState () {
-
const wallet = this.configManager.getWallet()
const vault = this.keyringController.store.getState().vault
const isInitialized = (!!wallet || !!vault)
@@ -222,9 +222,9 @@ module.exports = class MetamaskController extends EventEmitter {
{
isInitialized,
},
- this.networkStore.getState(),
+ this.networkController.store.getState(),
this.ethStore.getState(),
- this.txManager.memStore.getState(),
+ this.txController.memStore.getState(),
this.messageManager.memStore.getState(),
this.personalMessageManager.memStore.getState(),
this.keyringController.memStore.getState(),
@@ -249,62 +249,61 @@ module.exports = class MetamaskController extends EventEmitter {
getApi () {
const keyringController = this.keyringController
const preferencesController = this.preferencesController
- const txManager = this.txManager
+ const txController = this.txController
const noticeController = this.noticeController
const addressBookController = this.addressBookController
return {
// etc
- getState: (cb) => cb(null, this.getState()),
- setProviderType: this.setProviderType.bind(this),
- useEtherscanProvider: this.useEtherscanProvider.bind(this),
- setCurrentCurrency: this.setCurrentCurrency.bind(this),
- markAccountsFound: this.markAccountsFound.bind(this),
+ getState: (cb) => cb(null, this.getState()),
+ setProviderType: this.networkController.setProviderType.bind(this.networkController),
+ setCurrentCurrency: this.setCurrentCurrency.bind(this),
+ markAccountsFound: this.markAccountsFound.bind(this),
// coinbase
buyEth: this.buyEth.bind(this),
// shapeshift
createShapeShiftTx: this.createShapeShiftTx.bind(this),
// primary HD keyring management
- addNewAccount: this.addNewAccount.bind(this),
- placeSeedWords: this.placeSeedWords.bind(this),
- clearSeedWordCache: this.clearSeedWordCache.bind(this),
- importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
+ addNewAccount: this.addNewAccount.bind(this),
+ placeSeedWords: this.placeSeedWords.bind(this),
+ clearSeedWordCache: this.clearSeedWordCache.bind(this),
+ importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
// vault management
submitPassword: this.submitPassword.bind(this),
// PreferencesController
- setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
- setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
- setCustomRpc: nodeify(this.setCustomRpc).bind(this),
+ setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
+ setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
+ setCustomRpc: nodeify(this.setCustomRpc).bind(this),
// AddressController
- setAddressBook: nodeify(addressBookController.setAddressBook).bind(addressBookController),
+ setAddressBook: nodeify(addressBookController.setAddressBook).bind(addressBookController),
// KeyringController
- setLocked: nodeify(keyringController.setLocked).bind(keyringController),
+ setLocked: nodeify(keyringController.setLocked).bind(keyringController),
createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
- createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore).bind(keyringController),
- addNewKeyring: nodeify(keyringController.addNewKeyring).bind(keyringController),
- saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
- exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
-
- // txManager
- approveTransaction: txManager.approveTransaction.bind(txManager),
- cancelTransaction: txManager.cancelTransaction.bind(txManager),
+ createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore).bind(keyringController),
+ addNewKeyring: nodeify(keyringController.addNewKeyring).bind(keyringController),
+ saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
+ exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
+
+ // txController
+ approveTransaction: txController.approveTransaction.bind(txController),
+ cancelTransaction: txController.cancelTransaction.bind(txController),
updateAndApproveTransaction: this.updateAndApproveTx.bind(this),
// messageManager
- signMessage: nodeify(this.signMessage).bind(this),
- cancelMessage: this.cancelMessage.bind(this),
+ signMessage: nodeify(this.signMessage).bind(this),
+ cancelMessage: this.cancelMessage.bind(this),
// personalMessageManager
- signPersonalMessage: nodeify(this.signPersonalMessage).bind(this),
- cancelPersonalMessage: this.cancelPersonalMessage.bind(this),
+ signPersonalMessage: nodeify(this.signPersonalMessage).bind(this),
+ cancelPersonalMessage: this.cancelPersonalMessage.bind(this),
// notices
- checkNotices: noticeController.updateNoticesList.bind(noticeController),
+ checkNotices: noticeController.updateNoticesList.bind(noticeController),
markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
}
}
@@ -344,9 +343,7 @@ module.exports = class MetamaskController extends EventEmitter {
console.error('Error in RPC response:\n', response.error)
}
if (request.isMetamaskInternal) return
- if (global.METAMASK_DEBUG) {
- console.log(`RPC (${originDomain}):`, request, '->', response)
- }
+ log.info(`RPC (${originDomain}):`, request, '->', response)
}
}
@@ -366,8 +363,7 @@ module.exports = class MetamaskController extends EventEmitter {
//
submitPassword (password, cb) {
- this.migrateOldVaultIfAny(password)
- .then(this.keyringController.submitPassword.bind(this.keyringController, password))
+ return this.keyringController.submitPassword(password)
.then((newState) => { cb(null, newState) })
.catch((reason) => { cb(reason) })
}
@@ -393,7 +389,7 @@ module.exports = class MetamaskController extends EventEmitter {
.then((serialized) => {
const seedWords = serialized.mnemonic
this.configManager.setSeedWords(seedWords)
- cb()
+ cb(null, seedWords)
})
}
@@ -425,12 +421,12 @@ module.exports = class MetamaskController extends EventEmitter {
newUnapprovedTransaction (txParams, cb) {
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
const self = this
- self.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => {
+ self.txController.addUnapprovedTransaction(txParams, (err, txMeta) => {
if (err) return cb(err)
self.sendUpdate()
self.opts.showUnapprovedTx(txMeta)
// listen for tx completion (success, fail)
- self.txManager.once(`${txMeta.id}:finished`, (completedTx) => {
+ self.txController.once(`${txMeta.id}:finished`, (completedTx) => {
switch (completedTx.status) {
case 'submitted':
return cb(null, completedTx.hash)
@@ -444,7 +440,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
newUnsignedMessage (msgParams, cb) {
- let msgId = this.messageManager.addUnapprovedMessage(msgParams)
+ const msgId = this.messageManager.addUnapprovedMessage(msgParams)
this.sendUpdate()
this.opts.showUnconfirmedMessage()
this.messageManager.once(`${msgId}:finished`, (data) => {
@@ -464,7 +460,7 @@ module.exports = class MetamaskController extends EventEmitter {
return cb(new Error('MetaMask Message Signature: from field is required.'))
}
- let msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
+ const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
this.sendUpdate()
this.opts.showUnconfirmedMessage()
this.personalMessageManager.once(`${msgId}:finished`, (data) => {
@@ -479,11 +475,11 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
- updateAndApproveTx(txMeta, cb) {
+ updateAndApproveTx (txMeta, cb) {
log.debug(`MetaMaskController - updateAndApproveTx: ${JSON.stringify(txMeta)}`)
- const txManager = this.txManager
- txManager.updateTx(txMeta)
- txManager.approveTransaction(txMeta.id, cb)
+ const txController = this.txController
+ txController.updateTx(txMeta)
+ txController.approveTransaction(txMeta.id, cb)
}
signMessage (msgParams, cb) {
@@ -505,7 +501,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
- cancelMessage(msgId, cb) {
+ cancelMessage (msgId, cb) {
const messageManager = this.messageManager
messageManager.rejectMsg(msgId)
if (cb && typeof cb === 'function') {
@@ -515,7 +511,7 @@ module.exports = class MetamaskController extends EventEmitter {
// Prefixed Style Message Signing Methods:
approvePersonalMessage (msgParams, cb) {
- let msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
+ const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
this.sendUpdate()
this.opts.showUnconfirmedMessage()
this.personalMessageManager.once(`${msgId}:finished`, (data) => {
@@ -548,7 +544,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
- cancelPersonalMessage(msgId, cb) {
+ cancelPersonalMessage (msgId, cb) {
const messageManager = this.personalMessageManager
messageManager.rejectMsg(msgId)
if (cb && typeof cb === 'function') {
@@ -562,42 +558,13 @@ module.exports = class MetamaskController extends EventEmitter {
cb(null, this.getState())
}
- // Migrate Old Vault If Any
- // @string password
- //
- // returns Promise()
- //
- // Temporary step used when logging in.
- // Checks if old style (pre-3.0.0) Metamask Vault exists.
- // If so, persists that vault in the new vault format
- // with the provided password, so the other unlock steps
- // may be completed without interruption.
- migrateOldVaultIfAny (password) {
-
- if (!this.checkIfShouldMigrate()) {
- return Promise.resolve(password)
- }
-
- const keyringController = this.keyringController
-
- return this.idStoreMigrator.migratedVaultForPassword(password)
- .then(this.restoreOldVaultAccounts.bind(this))
- .then(this.restoreOldLostAccounts.bind(this))
- .then(keyringController.persistAllKeyrings.bind(keyringController, password))
- .then(() => password)
- }
-
- checkIfShouldMigrate() {
- return !!this.configManager.getWallet() && !this.configManager.getVault()
- }
-
- restoreOldVaultAccounts(migratorOutput) {
+ restoreOldVaultAccounts (migratorOutput) {
const { serialized } = migratorOutput
return this.keyringController.restoreKeyring(serialized)
.then(() => migratorOutput)
}
- restoreOldLostAccounts(migratorOutput) {
+ restoreOldLostAccounts (migratorOutput) {
const { lostAccounts } = migratorOutput
if (lostAccounts) {
this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
@@ -623,12 +590,6 @@ module.exports = class MetamaskController extends EventEmitter {
//
// Log blocks
- logBlock (block) {
- if (global.METAMASK_DEBUG) {
- console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
- }
- this.verifyNetwork()
- }
setCurrentCurrency (currencyCode, cb) {
try {
@@ -647,91 +608,29 @@ module.exports = class MetamaskController extends EventEmitter {
buyEth (address, amount) {
if (!amount) amount = '5'
-
- const network = this.getNetworkState()
- let url
-
- switch (network) {
- case '1':
- url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
- break
-
- case '3':
- url = 'https://faucet.metamask.io/'
- break
- }
-
- if (url) extension.tabs.create({ url })
+ const network = this.networkController.getNetworkState()
+ const url = getBuyEthUrl({ network, address, amount })
+ if (url) this.platform.openWindow({ url })
}
createShapeShiftTx (depositAddress, depositType) {
this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
}
-
- //
- // network
- //
-
- verifyNetwork () {
- // Check network when restoring connectivity:
- if (this.isNetworkLoading()) this.lookupNetwork()
- }
+// network
setDefaultRpc () {
- this.configManager.setRpcTarget('http://localhost:8545')
- extension.runtime.reload()
- this.lookupNetwork()
+ this.networkController.setRpcTarget('http://localhost:8545')
return Promise.resolve('http://localhost:8545')
}
setCustomRpc (rpcTarget, rpcList) {
- this.configManager.setRpcTarget(rpcTarget)
- return this.preferencesController.updateFrequentRpcList(rpcTarget)
- .then(() => {
- extension.runtime.reload()
- this.lookupNetwork()
- return Promise.resolve(rpcTarget)
- })
- }
+ this.networkController.setRpcTarget(rpcTarget)
- setProviderType (type) {
- this.configManager.setProviderType(type)
- extension.runtime.reload()
- this.lookupNetwork()
- }
-
- useEtherscanProvider () {
- this.configManager.useEtherscanProvider()
- extension.runtime.reload()
- }
-
- getNetworkState () {
- return this.networkStore.getState().network
- }
-
- setNetworkState (network) {
- return this.networkStore.updateState({ network })
- }
-
- isNetworkLoading () {
- return this.getNetworkState() === 'loading'
- }
-
- lookupNetwork (err) {
- if (err) {
- this.setNetworkState('loading')
- }
-
- this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
- if (err) {
- this.setNetworkState('loading')
- return
- }
- if (global.METAMASK_DEBUG) {
- console.log('web3.getNetwork returned ' + network)
- }
- this.setNetworkState(network)
+ return this.preferencesController.updateFrequentRpcList(rpcTarget)
+ .then(() => {
+ return Promise.resolve(rpcTarget)
})
}
+
}
diff --git a/app/scripts/migrations/002.js b/app/scripts/migrations/002.js
index 36a870342..b1d88f2ef 100644
--- a/app/scripts/migrations/002.js
+++ b/app/scripts/migrations/002.js
@@ -7,7 +7,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
if (versionedData.data.config.provider.type === 'etherscan') {
diff --git a/app/scripts/migrations/003.js b/app/scripts/migrations/003.js
index 1893576ad..140f81d40 100644
--- a/app/scripts/migrations/003.js
+++ b/app/scripts/migrations/003.js
@@ -8,7 +8,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
if (versionedData.data.config.provider.rpcTarget === oldTestRpc) {
diff --git a/app/scripts/migrations/004.js b/app/scripts/migrations/004.js
index 405d932f8..cd558300c 100644
--- a/app/scripts/migrations/004.js
+++ b/app/scripts/migrations/004.js
@@ -6,7 +6,7 @@ module.exports = {
version,
migrate: function (versionedData) {
- let safeVersionedData = clone(versionedData)
+ const safeVersionedData = clone(versionedData)
safeVersionedData.meta.version = version
try {
if (safeVersionedData.data.config.provider.type !== 'rpc') return Promise.resolve(safeVersionedData)
diff --git a/app/scripts/migrations/005.js b/app/scripts/migrations/005.js
index e4b84f460..f7b68dfe4 100644
--- a/app/scripts/migrations/005.js
+++ b/app/scripts/migrations/005.js
@@ -14,7 +14,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/006.js b/app/scripts/migrations/006.js
index 94d1b6ecd..51ea6e3e7 100644
--- a/app/scripts/migrations/006.js
+++ b/app/scripts/migrations/006.js
@@ -13,7 +13,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/007.js b/app/scripts/migrations/007.js
index 236e35224..d9887b9c8 100644
--- a/app/scripts/migrations/007.js
+++ b/app/scripts/migrations/007.js
@@ -13,7 +13,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/008.js b/app/scripts/migrations/008.js
index cd5e95d22..da7cb2e60 100644
--- a/app/scripts/migrations/008.js
+++ b/app/scripts/migrations/008.js
@@ -13,7 +13,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/009.js b/app/scripts/migrations/009.js
index 4612fefdc..f47db55ac 100644
--- a/app/scripts/migrations/009.js
+++ b/app/scripts/migrations/009.js
@@ -13,7 +13,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/010.js b/app/scripts/migrations/010.js
index c0cc56ae4..e4b9ac07e 100644
--- a/app/scripts/migrations/010.js
+++ b/app/scripts/migrations/010.js
@@ -13,7 +13,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/011.js b/app/scripts/migrations/011.js
index 0d5d6d307..782ec809d 100644
--- a/app/scripts/migrations/011.js
+++ b/app/scripts/migrations/011.js
@@ -12,7 +12,7 @@ module.exports = {
version,
migrate: function (originalVersionedData) {
- let versionedData = clone(originalVersionedData)
+ const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
diff --git a/app/scripts/migrations/012.js b/app/scripts/migrations/012.js
new file mode 100644
index 000000000..f69ccbb02
--- /dev/null
+++ b/app/scripts/migrations/012.js
@@ -0,0 +1,36 @@
+const version = 12
+
+/*
+
+This migration modifies our notices to delete their body after being read.
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ newState.NoticeController.noticesList.forEach((notice) => {
+ if (notice.read) {
+ notice.body = ''
+ }
+ })
+ return newState
+}
diff --git a/app/scripts/migrations/013.js b/app/scripts/migrations/013.js
new file mode 100644
index 000000000..8f11e510e
--- /dev/null
+++ b/app/scripts/migrations/013.js
@@ -0,0 +1,34 @@
+const version = 13
+
+/*
+
+This migration modifies the network config from ambiguous 'testnet' to explicit 'ropsten'
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ if (newState.config.provider.type === 'testnet') {
+ newState.config.provider.type = 'ropsten'
+ }
+ return newState
+}
diff --git a/app/scripts/migrations/014.js b/app/scripts/migrations/014.js
new file mode 100644
index 000000000..0fe92125b
--- /dev/null
+++ b/app/scripts/migrations/014.js
@@ -0,0 +1,34 @@
+const version = 14
+
+/*
+
+This migration removes provider from config and moves it too NetworkController.
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ newState.NetworkController = {}
+ newState.NetworkController.provider = newState.config.provider
+ delete newState.config.provider
+ return newState
+}
diff --git a/app/scripts/migrations/_multi-keyring.js b/app/scripts/migrations/_multi-keyring.js
index 04c966d4d..253aa3d9d 100644
--- a/app/scripts/migrations/_multi-keyring.js
+++ b/app/scripts/migrations/_multi-keyring.js
@@ -15,15 +15,15 @@ const KeyringController = require('../../app/scripts/lib/keyring-controller')
const password = 'obviously not correct'
module.exports = {
- version,
+ version,
migrate: function (versionedData) {
versionedData.meta.version = version
- let store = new ObservableStore(versionedData.data)
- let configManager = new ConfigManager({ store })
- let idStoreMigrator = new IdentityStoreMigrator({ configManager })
- let keyringController = new KeyringController({
+ const store = new ObservableStore(versionedData.data)
+ const configManager = new ConfigManager({ store })
+ const idStoreMigrator = new IdentityStoreMigrator({ configManager })
+ const keyringController = new KeyringController({
configManager: configManager,
})
@@ -46,6 +46,5 @@ module.exports = {
return Promise.resolve(versionedData)
})
})
-
},
}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index a3dd48c17..fb1ad7863 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -22,4 +22,7 @@ module.exports = [
require('./009'),
require('./010'),
require('./011'),
+ require('./012'),
+ require('./013'),
+ require('./014'),
]
diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js
index 0d72760fe..57aad40c5 100644
--- a/app/scripts/notice-controller.js
+++ b/app/scripts/notice-controller.js
@@ -41,6 +41,7 @@ module.exports = class NoticeController extends EventEmitter {
var notices = this.getNoticesList()
var index = notices.findIndex((currentNotice) => currentNotice.id === noticeToMark.id)
notices[index].read = true
+ notices[index].body = ''
this.setNoticesList(notices)
const latestNotice = this.getLatestUnreadNotice()
cb(null, latestNotice)
diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js
new file mode 100644
index 000000000..00c2aa275
--- /dev/null
+++ b/app/scripts/platforms/extension.js
@@ -0,0 +1,23 @@
+const extension = require('extensionizer')
+
+class ExtensionPlatform {
+
+ //
+ // Public
+ //
+
+ reload () {
+ extension.runtime.reload()
+ }
+
+ openWindow ({ url }) {
+ extension.tabs.create({ url })
+ }
+
+ getVersion () {
+ return extension.runtime.getManifest().version
+ }
+
+}
+
+module.exports = ExtensionPlatform
diff --git a/app/scripts/platforms/sw.js b/app/scripts/platforms/sw.js
new file mode 100644
index 000000000..007d8dc5b
--- /dev/null
+++ b/app/scripts/platforms/sw.js
@@ -0,0 +1,24 @@
+
+class SwPlatform {
+
+ //
+ // Public
+ //
+
+ reload () {
+ // you cant actually do this
+ global.location.reload()
+ }
+
+ openWindow ({ url }) {
+ // this doesnt actually work
+ global.open(url, '_blank')
+ }
+
+ getVersion () {
+ return '<unable to read version>'
+ }
+
+}
+
+module.exports = SwPlatform
diff --git a/app/scripts/platforms/window.js b/app/scripts/platforms/window.js
new file mode 100644
index 000000000..1527c008b
--- /dev/null
+++ b/app/scripts/platforms/window.js
@@ -0,0 +1,22 @@
+
+class WindowPlatform {
+
+ //
+ // Public
+ //
+
+ reload () {
+ global.location.reload()
+ }
+
+ openWindow ({ url }) {
+ global.open(url, '_blank')
+ }
+
+ getVersion () {
+ return '<unable to read version>'
+ }
+
+}
+
+module.exports = WindowPlatform
diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js
index b1e521a7a..f1eb394d7 100644
--- a/app/scripts/popup-core.js
+++ b/app/scripts/popup-core.js
@@ -1,7 +1,8 @@
const EventEmitter = require('events').EventEmitter
+const async = require('async')
const Dnode = require('dnode')
-const Web3 = require('web3')
-const MetaMaskUi = require('../../ui')
+const EthQuery = require('eth-query')
+const launchMetamaskUi = require('../../ui')
const StreamProvider = require('web3-stream-provider')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
@@ -9,9 +10,12 @@ const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
module.exports = initializePopup
-function initializePopup (connectionStream) {
+function initializePopup ({ container, connectionStream }, cb) {
// setup app
- connectToAccountManager(connectionStream, setupApp)
+ async.waterfall([
+ (cb) => connectToAccountManager(connectionStream, cb),
+ (accountManager, cb) => launchMetamaskUi({ container, accountManager }, cb),
+ ], cb)
}
function connectToAccountManager (connectionStream, cb) {
@@ -28,7 +32,8 @@ function setupWeb3Connection (connectionStream) {
providerStream.pipe(connectionStream).pipe(providerStream)
connectionStream.on('error', console.error.bind(console))
providerStream.on('error', console.error.bind(console))
- global.web3 = new Web3(providerStream)
+ global.ethereumProvider = providerStream
+ global.ethQuery = new EthQuery(providerStream)
}
function setupControllerConnection (connectionStream, cb) {
@@ -47,19 +52,3 @@ function setupControllerConnection (connectionStream, cb) {
cb(null, accountManager)
})
}
-
-function setupApp (err, accountManager) {
- var container = document.getElementById('app-content')
- if (err) {
- container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
- container.style.height = '80px'
- log.error(err.stack)
- throw err
- }
-
-
- MetaMaskUi({
- container: container,
- accountManager: accountManager,
- })
-}
diff --git a/app/scripts/popup.js b/app/scripts/popup.js
index 62db68c10..5f17f0651 100644
--- a/app/scripts/popup.js
+++ b/app/scripts/popup.js
@@ -3,23 +3,47 @@ const MetaMaskUiCss = require('../../ui/css')
const startPopup = require('./popup-core')
const PortStream = require('./lib/port-stream.js')
const isPopupOrNotification = require('./lib/is-popup-or-notification')
-const extension = require('./lib/extension')
-const notification = require('./lib/notifications')
+const extension = require('extensionizer')
+const ExtensionPlatform = require('./platforms/extension')
+const NotificationManager = require('./lib/notification-manager')
+const notificationManager = new NotificationManager()
-var css = MetaMaskUiCss()
+// create platform global
+global.platform = new ExtensionPlatform()
+
+// inject css
+const css = MetaMaskUiCss()
injectCss(css)
-var name = isPopupOrNotification()
-closePopupIfOpen(name)
-window.METAMASK_UI_TYPE = name
+// identify window type (popup, notification)
+const windowType = isPopupOrNotification()
+global.METAMASK_UI_TYPE = windowType
+closePopupIfOpen(windowType)
+
+// setup stream to background
+const extensionPort = extension.runtime.connect({ name: windowType })
+const connectionStream = new PortStream(extensionPort)
-var pluginPort = extension.runtime.connect({ name })
-var portStream = new PortStream(pluginPort)
+// start ui
+const container = document.getElementById('app-content')
+startPopup({ container, connectionStream }, (err, store) => {
+ if (err) return displayCriticalError(err)
+ store.subscribe(() => {
+ const state = store.getState()
+ if (state.appState.shouldClose) notificationManager.closePopup()
+ })
+})
-startPopup(portStream)
-function closePopupIfOpen (name) {
- if (name !== 'notification') {
- notification.closePopup()
+function closePopupIfOpen (windowType) {
+ if (windowType !== 'notification') {
+ notificationManager.closePopup()
}
}
+
+function displayCriticalError (err) {
+ container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
+ container.style.height = '80px'
+ log.error(err.stack)
+ throw err
+}