aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorkumavis <kumavis@users.noreply.github.com>2017-02-03 16:17:17 +0800
committerGitHub <noreply@github.com>2017-02-03 16:17:17 +0800
commit66be5ff2756e611c7af6973e704d27ce9bbd0d45 (patch)
tree43b5dcab6686e3db06218bfff17a943c72e6ddf3 /app
parent270808c206934cf7d75fefcdd092c9595de45f29 (diff)
parent970d4fd69519bab1de972839190a4ede888914bb (diff)
downloadtangerine-wallet-browser-66be5ff2756e611c7af6973e704d27ce9bbd0d45.tar.gz
tangerine-wallet-browser-66be5ff2756e611c7af6973e704d27ce9bbd0d45.tar.zst
tangerine-wallet-browser-66be5ff2756e611c7af6973e704d27ce9bbd0d45.zip
Merge pull request #1083 from MetaMask/kumavis-refactor6
Refactor round 6
Diffstat (limited to 'app')
-rw-r--r--app/scripts/keyring-controller.js39
-rw-r--r--app/scripts/lib/config-manager.js47
-rw-r--r--app/scripts/lib/controllers/currency.js70
-rw-r--r--app/scripts/lib/eth-store.js214
-rw-r--r--app/scripts/lib/idStore.js3
-rw-r--r--app/scripts/lib/message-manager.js52
-rw-r--r--app/scripts/metamask-controller.js168
-rw-r--r--app/scripts/migrations/007.js38
-rw-r--r--app/scripts/migrations/008.js36
-rw-r--r--app/scripts/migrations/009.js40
-rw-r--r--app/scripts/migrations/index.js3
-rw-r--r--app/scripts/notice-controller.js50
-rw-r--r--app/scripts/transaction-manager.js65
13 files changed, 478 insertions, 347 deletions
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index d1d706165..348f81fc9 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -29,11 +29,11 @@ class KeyringController extends EventEmitter {
this.keyringTypes = keyringTypes
this.store = new ObservableStore(initState)
this.memStore = new ObservableStore({
+ isUnlocked: false,
keyringTypes: this.keyringTypes.map(krt => krt.type),
keyrings: [],
identities: {},
})
- this.configManager = opts.configManager
this.ethStore = opts.ethStore
this.encryptor = encryptor
this.keyrings = []
@@ -53,37 +53,7 @@ class KeyringController extends EventEmitter {
// Not all methods end with this, that might be a nice refactor.
fullUpdate () {
this.emit('update')
- return Promise.resolve(this.getState())
- }
-
- // Get State
- // returns @object state
- //
- // This method returns a hash representing the current state
- // that the keyringController manages.
- //
- // It is extended in the MetamaskController along with the EthStore
- // state, and its own state, to create the metamask state branch
- // that is passed to the UI.
- //
- // This is currently a rare example of a synchronously resolving method
- // in this class, but will need to be Promisified when we move our
- // persistence to an async model.
- getState () {
-
- // old wallet
- const memState = this.memStore.getState()
- const result = {
- // computed
- isUnlocked: (!!this.password),
- // memStore
- keyringTypes: memState.keyringTypes,
- identities: memState.identities,
- keyrings: memState.keyrings,
- // configManager
- seedWords: this.configManager.getSeedWords(),
- }
- return result
+ return Promise.resolve(this.memStore.getState())
}
// Create New Vault And Keychain
@@ -147,7 +117,10 @@ class KeyringController extends EventEmitter {
//
// This method deallocates all secrets, and effectively locks metamask.
setLocked () {
+ // set locked
this.password = null
+ this.memStore.updateState({ isUnlocked: false })
+ // remove keyrings
this.keyrings = []
this._updateMemStoreKeyrings()
return this.fullUpdate()
@@ -385,6 +358,7 @@ class KeyringController extends EventEmitter {
persistAllKeyrings (password = this.password) {
if (typeof password === 'string') {
this.password = password
+ this.memStore.updateState({ isUnlocked: true })
}
return Promise.all(this.keyrings.map((keyring) => {
return Promise.all([keyring.type, keyring.serialize()])
@@ -421,6 +395,7 @@ class KeyringController extends EventEmitter {
return this.encryptor.decrypt(password, encryptedVault)
.then((vault) => {
this.password = password
+ this.memStore.updateState({ isUnlocked: true })
vault.forEach(this.restoreKeyring.bind(this))
return this.keyrings
})
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 357e081b1..a9b86ca8c 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -250,53 +250,6 @@ ConfigManager.prototype.getTOSHash = function () {
return data.TOSHash
}
-ConfigManager.prototype.setCurrentFiat = function (currency) {
- var data = this.getData()
- data.fiatCurrency = currency
- this.setData(data)
-}
-
-ConfigManager.prototype.getCurrentFiat = function () {
- var data = this.getData()
- return data.fiatCurrency || 'USD'
-}
-
-ConfigManager.prototype.updateConversionRate = function () {
- var data = this.getData()
- return fetch(`https://www.cryptonator.com/api/ticker/eth-${data.fiatCurrency}`)
- .then(response => response.json())
- .then((parsedResponse) => {
- this.setConversionPrice(parsedResponse.ticker.price)
- this.setConversionDate(parsedResponse.timestamp)
- }).catch((err) => {
- console.warn('MetaMask - Failed to query currency conversion.')
- this.setConversionPrice(0)
- this.setConversionDate('N/A')
- })
-}
-
-ConfigManager.prototype.setConversionPrice = function (price) {
- var data = this.getData()
- data.conversionRate = Number(price)
- this.setData(data)
-}
-
-ConfigManager.prototype.setConversionDate = function (datestring) {
- var data = this.getData()
- data.conversionDate = datestring
- this.setData(data)
-}
-
-ConfigManager.prototype.getConversionRate = function () {
- var data = this.getData()
- return (data.conversionRate) || 0
-}
-
-ConfigManager.prototype.getConversionDate = function () {
- var data = this.getData()
- return (data.conversionDate) || 'N/A'
-}
-
ConfigManager.prototype.getShapeShiftTxList = function () {
var data = this.getData()
var shapeShiftTxList = data.shapeShiftTxList ? data.shapeShiftTxList : []
diff --git a/app/scripts/lib/controllers/currency.js b/app/scripts/lib/controllers/currency.js
new file mode 100644
index 000000000..c4904f8ac
--- /dev/null
+++ b/app/scripts/lib/controllers/currency.js
@@ -0,0 +1,70 @@
+const ObservableStore = require('obs-store')
+const extend = require('xtend')
+
+// every ten minutes
+const POLLING_INTERVAL = 600000
+
+class CurrencyController {
+
+ constructor (opts = {}) {
+ const initState = extend({
+ currentCurrency: 'USD',
+ conversionRate: 0,
+ conversionDate: 'N/A',
+ }, opts.initState)
+ this.store = new ObservableStore(initState)
+ }
+
+ //
+ // PUBLIC METHODS
+ //
+
+ getCurrentCurrency () {
+ return this.store.getState().currentCurrency
+ }
+
+ setCurrentCurrency (currentCurrency) {
+ this.store.updateState({ currentCurrency })
+ }
+
+ getConversionRate () {
+ return this.store.getState().conversionRate
+ }
+
+ setConversionRate (conversionRate) {
+ this.store.updateState({ conversionRate })
+ }
+
+ getConversionDate () {
+ return this.store.getState().conversionDate
+ }
+
+ setConversionDate (conversionDate) {
+ this.store.updateState({ conversionDate })
+ }
+
+ updateConversionRate () {
+ const currentCurrency = this.getCurrentCurrency()
+ return fetch(`https://www.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')
+ })
+ }
+
+ scheduleConversionInterval () {
+ if (this.conversionInterval) {
+ clearInterval(this.conversionInterval)
+ }
+ this.conversionInterval = setInterval(() => {
+ this.updateConversionRate()
+ }, POLLING_INTERVAL)
+ }
+}
+
+module.exports = CurrencyController
diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js
index 7e2caf884..96b4a60f2 100644
--- a/app/scripts/lib/eth-store.js
+++ b/app/scripts/lib/eth-store.js
@@ -7,140 +7,122 @@
* on each new block.
*/
-const EventEmitter = require('events').EventEmitter
-const inherits = require('util').inherits
const async = require('async')
-const clone = require('clone')
const EthQuery = require('eth-query')
+const ObservableStore = require('obs-store')
+function noop() {}
-module.exports = EthereumStore
+class EthereumStore extends ObservableStore {
-inherits(EthereumStore, EventEmitter)
-function EthereumStore(engine) {
- const self = this
- EventEmitter.call(self)
- self._currentState = {
- accounts: {},
- transactions: {},
+ constructor (opts = {}) {
+ super({
+ accounts: {},
+ transactions: {},
+ })
+ this._provider = opts.provider
+ this._query = new EthQuery(this._provider)
+ this._blockTracker = opts.blockTracker
+ // subscribe to latest block
+ this._blockTracker.on('block', this._updateForBlock.bind(this))
}
- self._query = new EthQuery(engine)
-
- engine.on('block', self._updateForBlock.bind(self))
-}
-
-//
-// public
-//
-EthereumStore.prototype.getState = function () {
- const self = this
- return clone(self._currentState)
-}
-
-EthereumStore.prototype.addAccount = function (address) {
- const self = this
- self._currentState.accounts[address] = {}
- self._didUpdate()
- if (!self.currentBlockNumber) return
- self._updateAccount(address, () => {
- self._didUpdate()
- })
-}
+ //
+ // public
+ //
-EthereumStore.prototype.removeAccount = function (address) {
- const self = this
- delete self._currentState.accounts[address]
- self._didUpdate()
-}
+ addAccount (address) {
+ const accounts = this.getState().accounts
+ accounts[address] = {}
+ this.updateState({ accounts })
+ if (!this._currentBlockNumber) return
+ this._updateAccount(address)
+ }
-EthereumStore.prototype.addTransaction = function (txHash) {
- const self = this
- self._currentState.transactions[txHash] = {}
- self._didUpdate()
- if (!self.currentBlockNumber) return
- self._updateTransaction(self.currentBlockNumber, txHash, noop)
-}
+ removeAccount (address) {
+ const accounts = this.getState().accounts
+ delete accounts[address]
+ this.updateState({ accounts })
+ }
-EthereumStore.prototype.removeTransaction = function (address) {
- const self = this
- delete self._currentState.transactions[address]
- self._didUpdate()
-}
+ addTransaction (txHash) {
+ const transactions = this.getState().transactions
+ transactions[txHash] = {}
+ this.updateState({ transactions })
+ if (!this._currentBlockNumber) return
+ this._updateTransaction(this._currentBlockNumber, txHash, noop)
+ }
+ removeTransaction (txHash) {
+ const transactions = this.getState().transactions
+ delete transactions[txHash]
+ this.updateState({ transactions })
+ }
-//
-// private
-//
-EthereumStore.prototype._didUpdate = function () {
- const self = this
- var state = self.getState()
- self.emit('update', state)
-}
+ //
+ // private
+ //
+
+ _updateForBlock (block) {
+ const blockNumber = '0x' + block.number.toString('hex')
+ this._currentBlockNumber = blockNumber
+ async.parallel([
+ this._updateAccounts.bind(this),
+ this._updateTransactions.bind(this, blockNumber),
+ ], (err) => {
+ if (err) return console.error(err)
+ this.emit('block', this.getState())
+ })
+ }
-EthereumStore.prototype._updateForBlock = function (block) {
- const self = this
- var blockNumber = '0x' + block.number.toString('hex')
- self.currentBlockNumber = blockNumber
- async.parallel([
- self._updateAccounts.bind(self),
- self._updateTransactions.bind(self, blockNumber),
- ], function (err) {
- if (err) return console.error(err)
- self.emit('block', self.getState())
- self._didUpdate()
- })
-}
+ _updateAccounts (cb = noop) {
+ const accounts = this.getState().accounts
+ const addresses = Object.keys(accounts)
+ async.each(addresses, this._updateAccount.bind(this), cb)
+ }
-EthereumStore.prototype._updateAccounts = function (cb) {
- var accountsState = this._currentState.accounts
- var addresses = Object.keys(accountsState)
- async.each(addresses, this._updateAccount.bind(this), cb)
-}
+ _updateAccount (address, cb = noop) {
+ const accounts = this.getState().accounts
+ this._getAccount(address, (err, result) => {
+ if (err) return cb(err)
+ result.address = address
+ // only populate if the entry is still present
+ if (accounts[address]) {
+ accounts[address] = result
+ }
+ cb(null, result)
+ })
+ }
-EthereumStore.prototype._updateAccount = function (address, cb) {
- var accountsState = this._currentState.accounts
- this.getAccount(address, function (err, result) {
- if (err) return cb(err)
- result.address = address
- // only populate if the entry is still present
- if (accountsState[address]) {
- accountsState[address] = result
- }
- cb(null, result)
- })
-}
+ _updateTransactions (block, cb = noop) {
+ const transactions = this.getState().transactions
+ const txHashes = Object.keys(transactions)
+ async.each(txHashes, this._updateTransaction.bind(this, block), cb)
+ }
-EthereumStore.prototype.getAccount = function (address, cb) {
- const query = this._query
- async.parallel({
- balance: query.getBalance.bind(query, address),
- nonce: query.getTransactionCount.bind(query, address),
- code: query.getCode.bind(query, address),
- }, cb)
-}
+ _updateTransaction (block, txHash, cb = noop) {
+ // would use the block here to determine how many confirmations the tx has
+ const transactions = this.getState().transactions
+ this._query.getTransaction(txHash, (err, result) => {
+ if (err) return cb(err)
+ // only populate if the entry is still present
+ if (transactions[txHash]) {
+ transactions[txHash] = result
+ }
+ cb(null, result)
+ })
+ }
-EthereumStore.prototype._updateTransactions = function (block, cb) {
- const self = this
- var transactionsState = self._currentState.transactions
- var txHashes = Object.keys(transactionsState)
- async.each(txHashes, self._updateTransaction.bind(self, block), cb)
-}
+ _getAccount (address, cb = noop) {
+ const query = this._query
+ async.parallel({
+ balance: query.getBalance.bind(query, address),
+ nonce: query.getTransactionCount.bind(query, address),
+ code: query.getCode.bind(query, address),
+ }, cb)
+ }
-EthereumStore.prototype._updateTransaction = function (block, txHash, cb) {
- const self = this
- // would use the block here to determine how many confirmations the tx has
- var transactionsState = self._currentState.transactions
- self._query.getTransaction(txHash, function (err, result) {
- if (err) return cb(err)
- // only populate if the entry is still present
- if (transactionsState[txHash]) {
- transactionsState[txHash] = result
- self._didUpdate()
- }
- cb(null, result)
- })
}
-function noop() {}
+module.exports = EthereumStore \ No newline at end of file
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index e4cbca456..ac395440d 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -97,9 +97,6 @@ IdentityStore.prototype.getState = function () {
isDisclaimerConfirmed: configManager.getConfirmedDisclaimer(),
selectedAddress: configManager.getSelectedAccount(),
shapeShiftTxList: configManager.getShapeShiftTxList(),
- currentFiat: configManager.getCurrentFiat(),
- conversionRate: configManager.getConversionRate(),
- conversionDate: configManager.getConversionDate(),
gasMultiplier: configManager.getGasMultiplier(),
}))
}
diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js
index 18bf54ae1..38fa42017 100644
--- a/app/scripts/lib/message-manager.js
+++ b/app/scripts/lib/message-manager.js
@@ -6,18 +6,11 @@ const createId = require('./random-id')
module.exports = class MessageManager extends EventEmitter{
constructor (opts) {
super()
- this.memStore = new ObservableStore({ messages: [] })
- }
-
- getState() {
- return {
- unapprovedMsgs: this.getUnapprovedMsgs(),
- messages: this.getMsgList(),
- }
- }
-
- getMsgList () {
- return this.memStore.getState().messages
+ this.memStore = new ObservableStore({
+ unapprovedMsgs: {},
+ unapprovedMsgCount: 0,
+ })
+ this.messages = []
}
get unapprovedMsgCount () {
@@ -25,8 +18,7 @@ module.exports = class MessageManager extends EventEmitter{
}
getUnapprovedMsgs () {
- let messages = this.getMsgList()
- return messages.filter(msg => msg.status === 'unapproved')
+ return this.messages.filter(msg => msg.status === 'unapproved')
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
}
@@ -41,10 +33,6 @@ module.exports = class MessageManager extends EventEmitter{
status: 'unapproved',
}
this.addMsg(msgData)
- console.log('addUnapprovedMessage:', msgData)
-
- // keep the cb around for after approval (requires user interaction)
- // This cb fires completion to the Dapp's write operation.
// signal update
this.emit('update')
@@ -52,15 +40,12 @@ module.exports = class MessageManager extends EventEmitter{
}
addMsg (msg) {
- let messages = this.getMsgList()
- messages.push(msg)
- this._saveMsgList(messages)
+ this.messages.push(msg)
+ this._saveMsgList()
}
getMsg (msgId) {
- let messages = this.getMsgList()
- let matching = messages.filter(msg => msg.id === msgId)
- return matching.length > 0 ? matching[0] : null
+ return this.messages.find(msg => msg.id === msgId)
}
approveMessage (msgParams) {
@@ -85,7 +70,10 @@ module.exports = class MessageManager extends EventEmitter{
brodcastMessage (rawSig, msgId, status) {
this.emit(`${msgId}:finished`, {status, rawSig})
}
-// PRIVATE METHODS
+
+ //
+ // PRIVATE METHODS
+ //
_setMsgStatus (msgId, status) {
let msg = this.getMsg(msgId)
@@ -94,18 +82,18 @@ module.exports = class MessageManager extends EventEmitter{
}
_updateMsg (msg) {
- let messages = this.getMsgList()
- let index = messages.findIndex((message) => message.id === msg.id)
+ let index = this.messages.findIndex((message) => message.id === msg.id)
if (index !== -1) {
- messages[index] = msg
+ this.messages[index] = msg
}
- this._saveMsgList(messages)
+ this._saveMsgList()
}
- _saveMsgList (msgList) {
+ _saveMsgList () {
+ const unapprovedMsgs = this.getUnapprovedMsgs()
+ const unapprovedMsgCount = Object.keys(unapprovedMsgs).length
+ this.memStore.updateState({ unapprovedMsgs, unapprovedMsgCount })
this.emit('updateBadge')
- this.memStore.updateState({ messages: msgList })
}
-
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index a56203a88..c0910014f 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -12,6 +12,7 @@ const MetaMaskProvider = require('web3-provider-engine/zero.js')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const KeyringController = require('./keyring-controller')
const PreferencesController = require('./lib/controllers/preferences')
+const CurrencyController = require('./lib/controllers/currency')
const NoticeController = require('./notice-controller')
const MessageManager = require('./lib/message-manager')
const TxManager = require('./transaction-manager')
@@ -29,38 +30,48 @@ module.exports = class MetamaskController extends EventEmitter {
constructor (opts) {
super()
this.opts = opts
- this.state = { network: 'loading' }
let initState = opts.initState || {}
// observable state store
this.store = new ObservableStore(initState)
+ // network store
+ this.networkStore = new ObservableStore({ network: 'loading' })
+
// config manager
this.configManager = new ConfigManager({
store: this.store,
})
- this.configManager.updateConversionRate()
// preferences controller
this.preferencesController = new PreferencesController({
initState: initState.PreferencesController,
})
+ // currency controller
+ this.currencyController = new CurrencyController({
+ initState: initState.CurrencyController,
+ })
+ this.currencyController.updateConversionRate()
+ this.currencyController.scheduleConversionInterval()
+
// rpc provider
this.provider = this.initializeProvider(opts)
this.provider.on('block', this.logBlock.bind(this))
- this.provider.on('error', this.getNetwork.bind(this))
+ this.provider.on('error', this.verifyNetwork.bind(this))
// eth data query tools
this.ethQuery = new EthQuery(this.provider)
- this.ethStore = new EthStore(this.provider)
+ this.ethStore = new EthStore({
+ provider: this.provider,
+ blockTracker: this.provider,
+ })
// key mgmt
this.keyringController = new KeyringController({
initState: initState.KeyringController,
ethStore: this.ethStore,
- configManager: this.configManager,
- getNetwork: this.getStateNetwork.bind(this),
+ getNetwork: this.getNetworkState.bind(this),
})
this.keyringController.on('newAccount', (address) => {
this.preferencesController.setSelectedAddress(address)
@@ -69,12 +80,11 @@ module.exports = class MetamaskController extends EventEmitter {
// tx mgmt
this.txManager = new TxManager({
- txList: this.configManager.getTxList(),
+ initState: initState.TransactionManager,
+ networkStore: this.networkStore,
+ preferencesStore: this.preferencesController.store,
txHistoryLimit: 40,
- setTxList: this.configManager.setTxList.bind(this.configManager),
- getSelectedAddress: this.preferencesController.getSelectedAddress.bind(this.preferencesController),
- getGasMultiplier: this.configManager.getGasMultiplier.bind(this.configManager),
- getNetwork: this.getStateNetwork.bind(this),
+ getNetwork: this.getNetworkState.bind(this),
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
provider: this.provider,
blockTracker: this.provider,
@@ -82,36 +92,49 @@ module.exports = class MetamaskController extends EventEmitter {
// notices
this.noticeController = new NoticeController({
- configManager: this.configManager,
+ initState: initState.NoticeController,
})
this.noticeController.updateNoticesList()
// to be uncommented when retrieving notices from a remote server.
// this.noticeController.startPolling()
- this.getNetwork()
+ this.lookupNetwork()
this.messageManager = new MessageManager()
this.publicConfigStore = this.initPublicConfigStore()
this.checkTOSChange()
- this.scheduleConversionInterval()
-
// TEMPORARY UNTIL FULL DEPRECATION:
this.idStoreMigrator = new IdStoreMigrator({
configManager: this.configManager,
})
- // manual state subscriptions
- this.ethStore.on('update', this.sendUpdate.bind(this))
- this.keyringController.on('update', this.sendUpdate.bind(this))
- this.txManager.on('update', this.sendUpdate.bind(this))
- this.messageManager.on('update', this.sendUpdate.bind(this))
+ // manual disk state subscriptions
+ this.txManager.store.subscribe((state) => {
+ this.store.updateState({ TransactionManager: state })
+ })
this.keyringController.store.subscribe((state) => {
this.store.updateState({ KeyringController: state })
})
this.preferencesController.store.subscribe((state) => {
this.store.updateState({ PreferencesController: state })
})
+ this.currencyController.store.subscribe((state) => {
+ this.store.updateState({ CurrencyController: state })
+ })
+ this.noticeController.store.subscribe((state) => {
+ this.store.updateState({ NoticeController: state })
+ })
+
+ // manual mem state subscriptions
+ this.networkStore.subscribe(this.sendUpdate.bind(this))
+ this.ethStore.subscribe(this.sendUpdate.bind(this))
+ this.txManager.memStore.subscribe(this.sendUpdate.bind(this))
+ this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
+ this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
+ this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
+ this.currencyController.store.subscribe(this.sendUpdate.bind(this))
+ this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
}
//
@@ -174,22 +197,21 @@ module.exports = class MetamaskController extends EventEmitter {
{
isInitialized,
},
- this.state,
+ this.networkStore.getState(),
this.ethStore.getState(),
- this.txManager.getState(),
- this.messageManager.getState(),
- this.keyringController.getState(),
+ this.txManager.memStore.getState(),
+ this.messageManager.memStore.getState(),
+ this.keyringController.memStore.getState(),
this.preferencesController.store.getState(),
- this.noticeController.getState(),
+ this.currencyController.store.getState(),
+ this.noticeController.memStore.getState(),
// config manager
this.configManager.getConfig(),
{
shapeShiftTxList: this.configManager.getShapeShiftTxList(),
lostAccounts: this.configManager.getLostAccounts(),
- currentFiat: this.configManager.getCurrentFiat(),
- conversionRate: this.configManager.getConversionRate(),
- conversionDate: this.configManager.getConversionDate(),
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
+ seedWords: this.configManager.getSeedWords(),
}
)
}
@@ -213,7 +235,7 @@ module.exports = class MetamaskController extends EventEmitter {
useEtherscanProvider: this.useEtherscanProvider.bind(this),
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
resetDisclaimer: this.resetDisclaimer.bind(this),
- setCurrentFiat: this.setCurrentFiat.bind(this),
+ setCurrentCurrency: this.setCurrentCurrency.bind(this),
setTOSHash: this.setTOSHash.bind(this),
checkTOSChange: this.checkTOSChange.bind(this),
setGasMultiplier: this.setGasMultiplier.bind(this),
@@ -243,11 +265,13 @@ module.exports = class MetamaskController extends EventEmitter {
saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
- // signing methods
+ // txManager
approveTransaction: txManager.approveTransaction.bind(txManager),
cancelTransaction: txManager.cancelTransaction.bind(txManager),
- signMessage: this.signMessage.bind(this),
- cancelMessage: messageManager.rejectMsg.bind(messageManager),
+
+ // messageManager
+ signMessage: this.signMessage.bind(this),
+ cancelMessage: messageManager.rejectMsg.bind(messageManager),
// notices
checkNotices: noticeController.updateNoticesList.bind(noticeController),
@@ -339,7 +363,7 @@ module.exports = class MetamaskController extends EventEmitter {
.then((serialized) => {
const seedWords = serialized.mnemonic
this.configManager.setSeedWords(seedWords)
- promiseToCallback(this.keyringController.fullUpdate())(cb)
+ cb()
})
}
@@ -538,44 +562,38 @@ module.exports = class MetamaskController extends EventEmitter {
this.verifyNetwork()
}
- setCurrentFiat (fiat, cb) {
+ setCurrentCurrency (currencyCode, cb) {
try {
- this.configManager.setCurrentFiat(fiat)
- this.configManager.updateConversionRate()
- this.scheduleConversionInterval()
+ this.currencyController.setCurrentCurrency(currencyCode)
+ this.currencyController.updateConversionRate()
const data = {
- conversionRate: this.configManager.getConversionRate(),
- currentFiat: this.configManager.getCurrentFiat(),
- conversionDate: this.configManager.getConversionDate(),
+ conversionRate: this.currencyController.getConversionRate(),
+ currentFiat: this.currencyController.getCurrentCurrency(),
+ conversionDate: this.currencyController.getConversionDate(),
}
- cb(data)
+ cb(null, data)
} catch (err) {
- cb(null, err)
- }
- }
-
- scheduleConversionInterval () {
- if (this.conversionInterval) {
- clearInterval(this.conversionInterval)
+ cb(err)
}
- this.conversionInterval = setInterval(() => {
- this.configManager.updateConversionRate()
- }, 300000)
}
buyEth (address, amount) {
if (!amount) amount = '5'
- var network = this.state.network
- var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
+ 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
- if (network === '3') {
- url = 'https://faucet.metamask.io/'
+ case '3':
+ url = 'https://faucet.metamask.io/'
+ break
}
- extension.tabs.create({
- url,
- })
+ if (url) extension.tabs.create({ url })
}
createShapeShiftTx (depositAddress, depositType) {
@@ -584,7 +602,7 @@ module.exports = class MetamaskController extends EventEmitter {
setGasMultiplier (gasMultiplier, cb) {
try {
- this.configManager.setGasMultiplier(gasMultiplier)
+ this.txManager.setGasMultiplier(gasMultiplier)
cb()
} catch (err) {
cb(err)
@@ -597,21 +615,19 @@ module.exports = class MetamaskController extends EventEmitter {
verifyNetwork () {
// Check network when restoring connectivity:
- if (this.state.network === 'loading') {
- this.getNetwork()
- }
+ if (this.isNetworkLoading()) this.lookupNetwork()
}
setRpcTarget (rpcTarget) {
this.configManager.setRpcTarget(rpcTarget)
extension.runtime.reload()
- this.getNetwork()
+ this.lookupNetwork()
}
setProviderType (type) {
this.configManager.setProviderType(type)
extension.runtime.reload()
- this.getNetwork()
+ this.lookupNetwork()
}
useEtherscanProvider () {
@@ -619,26 +635,32 @@ module.exports = class MetamaskController extends EventEmitter {
extension.runtime.reload()
}
- getStateNetwork () {
- return this.state.network
+ getNetworkState () {
+ return this.networkStore.getState().network
+ }
+
+ setNetworkState (network) {
+ return this.networkStore.updateState({ network })
+ }
+
+ isNetworkLoading () {
+ return this.getNetworkState() === 'loading'
}
- getNetwork (err) {
+ lookupNetwork (err) {
if (err) {
- this.state.network = 'loading'
- this.sendUpdate()
+ this.setNetworkState('loading')
}
this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) {
- this.state.network = 'loading'
- return this.sendUpdate()
+ this.setNetworkState('loading')
+ return
}
if (global.METAMASK_DEBUG) {
console.log('web3.getNetwork returned ' + network)
}
- this.state.network = network
- this.sendUpdate()
+ this.setNetworkState(network)
})
}
diff --git a/app/scripts/migrations/007.js b/app/scripts/migrations/007.js
new file mode 100644
index 000000000..3ae8cdc2d
--- /dev/null
+++ b/app/scripts/migrations/007.js
@@ -0,0 +1,38 @@
+const version = 7
+
+/*
+
+This migration breaks out the TransactionManager substate
+
+*/
+
+const extend = require('xtend')
+
+module.exports = {
+ version,
+
+ migrate: function (versionedData) {
+ 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 = extend(state, {
+ TransactionManager: {
+ transactions: state.transactions || [],
+ gasMultiplier: state.gasMultiplier || 1,
+ },
+ })
+ delete newState.transactions
+ delete newState.gasMultiplier
+
+ return newState
+}
diff --git a/app/scripts/migrations/008.js b/app/scripts/migrations/008.js
new file mode 100644
index 000000000..7f6e72ee6
--- /dev/null
+++ b/app/scripts/migrations/008.js
@@ -0,0 +1,36 @@
+const version = 8
+
+/*
+
+This migration breaks out the NoticeController substate
+
+*/
+
+const extend = require('xtend')
+
+module.exports = {
+ version,
+
+ migrate: function (versionedData) {
+ 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 = extend(state, {
+ NoticeController: {
+ noticesList: state.noticesList || [],
+ },
+ })
+ delete newState.noticesList
+
+ return newState
+}
diff --git a/app/scripts/migrations/009.js b/app/scripts/migrations/009.js
new file mode 100644
index 000000000..61b8b7fa2
--- /dev/null
+++ b/app/scripts/migrations/009.js
@@ -0,0 +1,40 @@
+const version = 9
+
+/*
+
+This migration breaks out the CurrencyController substate
+
+*/
+
+const merge = require('deep-merge')
+
+module.exports = {
+ version,
+
+ migrate: function (versionedData) {
+ 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 = merge(state, {
+ CurrencyController: {
+ currentCurrency: state.currentFiat || 'USD',
+ conversionRate: state.conversionRate,
+ conversionDate: state.conversionDate,
+ },
+ })
+ delete newState.currentFiat
+ delete newState.conversionRate
+ delete newState.conversionDate
+
+ return newState
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index 17c191448..22bf008ba 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -17,4 +17,7 @@ module.exports = [
require('./004'),
require('./005'),
require('./006'),
+ require('./007'),
+ require('./008'),
+ require('./009'),
]
diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js
index c3777b4b1..ba7c68df4 100644
--- a/app/scripts/notice-controller.js
+++ b/app/scripts/notice-controller.js
@@ -1,36 +1,37 @@
const EventEmitter = require('events').EventEmitter
+const extend = require('xtend')
+const ObservableStore = require('obs-store')
const hardCodedNotices = require('../../notices/notices.json')
module.exports = class NoticeController extends EventEmitter {
constructor (opts) {
super()
- this.configManager = opts.configManager
this.noticePoller = null
+ const initState = extend({
+ noticesList: [],
+ }, opts.initState)
+ this.store = new ObservableStore(initState)
+ this.memStore = new ObservableStore({})
+ this.store.subscribe(() => this._updateMemstore())
}
- getState () {
- var lastUnreadNotice = this.getLatestUnreadNotice()
+ getNoticesList () {
+ return this.store.getState().noticesList
+ }
- return {
- lastUnreadNotice: lastUnreadNotice,
- noActiveNotices: !lastUnreadNotice,
- }
+ getUnreadNotices () {
+ const notices = this.getNoticesList()
+ return notices.filter((notice) => notice.read === false)
}
- getNoticesList () {
- var data = this.configManager.getData()
- if ('noticesList' in data) {
- return data.noticesList
- } else {
- return []
- }
+ getLatestUnreadNotice () {
+ const unreadNotices = this.getUnreadNotices()
+ return unreadNotices[unreadNotices.length - 1]
}
- setNoticesList (list) {
- var data = this.configManager.getData()
- data.noticesList = list
- this.configManager.setData(data)
+ setNoticesList (noticesList) {
+ this.store.updateState({ noticesList })
return Promise.resolve(true)
}
@@ -56,14 +57,6 @@ module.exports = class NoticeController extends EventEmitter {
})
}
- getLatestUnreadNotice () {
- var notices = this.getNoticesList()
- var filteredNotices = notices.filter((notice) => {
- return notice.read === false
- })
- return filteredNotices[filteredNotices.length - 1]
- }
-
startPolling () {
if (this.noticePoller) {
clearInterval(this.noticePoller)
@@ -92,5 +85,10 @@ module.exports = class NoticeController extends EventEmitter {
return Promise.resolve(hardCodedNotices)
}
+ _updateMemstore () {
+ const lastUnreadNotice = this.getLatestUnreadNotice()
+ const noActiveNotices = !lastUnreadNotice
+ this.memStore.updateState({ lastUnreadNotice, noActiveNotices })
+ }
}
diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js
index 6fecdba39..6299091f2 100644
--- a/app/scripts/transaction-manager.js
+++ b/app/scripts/transaction-manager.js
@@ -2,6 +2,7 @@ const EventEmitter = require('events')
const async = require('async')
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')
@@ -10,33 +11,53 @@ const createId = require('./lib/random-id')
module.exports = class TransactionManager extends EventEmitter {
constructor (opts) {
super()
- this.txList = opts.txList || []
- this._setTxList = opts.setTxList
+ this.store = new ObservableStore(extend({
+ transactions: [],
+ gasMultiplier: 1,
+ }, opts.initState))
+ this.memStore = new ObservableStore({})
+ this.networkStore = opts.networkStore || new ObservableStore({})
+ this.preferencesStore = opts.preferencesStore || new ObservableStore({})
this.txHistoryLimit = opts.txHistoryLimit
- this.getSelectedAddress = opts.getSelectedAddress
this.provider = opts.provider
this.blockTracker = opts.blockTracker
this.txProviderUtils = new TxProviderUtil(this.provider)
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
- this.getGasMultiplier = opts.getGasMultiplier
- this.getNetwork = opts.getNetwork
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() )
}
getState () {
- var selectedAddress = this.getSelectedAddress()
- return {
- transactions: this.getTxList(),
- unapprovedTxs: this.getUnapprovedTxList(),
- selectedAddressTxList: this.getFilteredTxList({metamaskNetworkId: this.getNetwork(), from: selectedAddress}),
- }
+ return this.memStore.getState()
+ }
+
+ getNetwork () {
+ return this.networkStore.getState().network
}
-// Returns the tx list
+ getSelectedAddress () {
+ return this.preferencesStore.getState().selectedAddress
+ }
+
+ // Returns the tx list
getTxList () {
let network = this.getNetwork()
- return this.txList.filter(txMeta => txMeta.metamaskNetworkId === network)
+ let fullTxList = this.store.getState().transactions
+ return fullTxList.filter(txMeta => txMeta.metamaskNetworkId === network)
+ }
+
+ getGasMultiplier () {
+ return this.store.getState().gasMultiplier
+ }
+
+ setGasMultiplier (gasMultiplier) {
+ return this.store.updateState({ gasMultiplier })
}
// Adds a tx to the txlist
@@ -108,7 +129,7 @@ module.exports = class TransactionManager extends EventEmitter {
id: txId,
time: time,
status: 'unapproved',
- gasMultiplier: this.getGasMultiplier() || 1,
+ gasMultiplier: this.getGasMultiplier(),
metamaskNetworkId: this.getNetwork(),
txParams: txParams,
}
@@ -239,7 +260,7 @@ module.exports = class TransactionManager extends EventEmitter {
getTxsByMetaData (key, value, txList = this.getTxList()) {
return txList.filter((txMeta) => {
- if (key in txMeta.txParams) {
+ if (txMeta.txParams[key]) {
return txMeta.txParams[key] === value
} else {
return txMeta[key] === value
@@ -351,9 +372,17 @@ module.exports = class TransactionManager extends EventEmitter {
// Saves the new/updated txList.
// Function is intended only for internal use
- _saveTxList (txList) {
- this.txList = txList
- this._setTxList(txList)
+ _saveTxList (transactions) {
+ this.store.updateState({ transactions })
+ }
+
+ _updateMemstore () {
+ const unapprovedTxs = this.getUnapprovedTxList()
+ const selectedAddressTxList = this.getFilteredTxList({
+ from: this.getSelectedAddress(),
+ metamaskNetworkId: this.getNetwork(),
+ })
+ this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
}
}