aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2017-10-13 00:59:28 +0800
committerDan Finlay <dan@danfinlay.com>2017-10-13 01:25:19 +0800
commitd0d082d70c1e256aeb70f90fcdc864aeca00aed4 (patch)
treea487d9bb888eebeba9c68d570cc55a5541384451 /app
parent1cba6543a42561c6691736d58f45e97f4832912b (diff)
parent29ee33359e818525549b5241adb6f5903a054bba (diff)
downloadtangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.tar
tangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.tar.gz
tangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.tar.bz2
tangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.tar.lz
tangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.tar.xz
tangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.tar.zst
tangerine-wallet-browser-d0d082d70c1e256aeb70f90fcdc864aeca00aed4.zip
Merge branch 'master' into i1340-SynchronousInjection
Diffstat (limited to 'app')
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/background.js3
-rw-r--r--app/scripts/controllers/network.js54
-rw-r--r--app/scripts/controllers/transactions.js4
-rw-r--r--app/scripts/lib/events-proxy.js10
-rw-r--r--app/scripts/lib/nodeify.js12
-rw-r--r--app/scripts/lib/nonce-tracker.js10
-rw-r--r--app/scripts/lib/obj-proxy.js19
-rw-r--r--app/scripts/lib/pending-tx-tracker.js29
-rw-r--r--app/scripts/lib/tx-state-manager.js8
-rw-r--r--app/scripts/lib/typed-message-manager.js123
-rw-r--r--app/scripts/metamask-controller.js123
12 files changed, 317 insertions, 80 deletions
diff --git a/app/manifest.json b/app/manifest.json
index 0fc43c7d4..a0f449c68 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
- "version": "3.10.8",
+ "version": "3.11.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "Ethereum Browser Extension",
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 195881e15..3e560d302 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -124,7 +124,8 @@ function setupController (initState) {
var unapprovedTxCount = controller.txController.getUnapprovedTxCount()
var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
var unapprovedPersonalMsgs = controller.personalMessageManager.unapprovedPersonalMsgCount
- var count = unapprovedTxCount + unapprovedMsgCount + unapprovedPersonalMsgs
+ var unapprovedTypedMsgs = controller.typedMessageManager.unapprovedTypedMessagesCount
+ var count = unapprovedTxCount + unapprovedMsgCount + unapprovedPersonalMsgs + unapprovedTypedMsgs
if (count) {
label = String(count)
}
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js
index 0f9db4d53..64ed4b7c2 100644
--- a/app/scripts/controllers/network.js
+++ b/app/scripts/controllers/network.js
@@ -1,11 +1,12 @@
const assert = require('assert')
const EventEmitter = require('events')
-const createMetamaskProvider = 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 createEthRpcClient = require('eth-rpc-client')
const createEventEmitterProxy = require('../lib/events-proxy.js')
+const createObjectProxy = require('../lib/obj-proxy.js')
const RPC_ADDRESS_LIST = require('../config.js').network
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
@@ -17,7 +18,8 @@ module.exports = class NetworkController extends EventEmitter {
this.networkStore = new ObservableStore('loading')
this.providerStore = new ObservableStore(config.provider)
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
- this._proxy = createEventEmitterProxy()
+ this.providerProxy = createObjectProxy()
+ this.blockTrackerProxy = createEventEmitterProxy()
this.on('networkDidChange', this.lookupNetwork)
}
@@ -25,12 +27,11 @@ module.exports = class NetworkController extends EventEmitter {
initializeProvider (_providerParams) {
this._baseProviderParams = _providerParams
const rpcUrl = this.getCurrentRpcAddress()
- this._configureStandardProvider({ rpcUrl })
- this._proxy.on('block', this._logBlock.bind(this))
- this._proxy.on('error', this.verifyNetwork.bind(this))
- this.ethQuery = new EthQuery(this._proxy)
+ this._configureStandardClient({ rpcUrl })
+ this.blockTrackerProxy.on('block', this._logBlock.bind(this))
+ this.blockTrackerProxy.on('error', this.verifyNetwork.bind(this))
+ this.ethQuery = new EthQuery(this.providerProxy)
this.lookupNetwork()
- return this._proxy
}
verifyNetwork () {
@@ -76,8 +77,10 @@ module.exports = class NetworkController extends EventEmitter {
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
// skip if type already matches
if (type === this.getProviderConfig().type) return
+ // lookup rpcTarget for typecreateMetamaskProvider
const rpcTarget = this.getRpcAddressForType(type)
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
+ // update connectioncreateMetamaskProvider
this.providerStore.updateState({ type, rpcTarget })
this._switchNetwork({ rpcUrl: rpcTarget })
}
@@ -97,32 +100,29 @@ module.exports = class NetworkController extends EventEmitter {
_switchNetwork (providerParams) {
this.setNetworkState('loading')
- this._configureStandardProvider(providerParams)
+ this._configureStandardClient(providerParams)
this.emit('networkDidChange')
}
- _configureStandardProvider(_providerParams) {
+ _configureStandardClient(_providerParams) {
const providerParams = extend(this._baseProviderParams, _providerParams)
- const provider = createMetamaskProvider(providerParams)
- this._setProvider(provider)
- }
-
- _setProvider (provider) {
- // collect old block tracker events
- const oldProvider = this._provider
- let blockTrackerHandlers
- if (oldProvider) {
- // capture old block handlers
- blockTrackerHandlers = oldProvider._blockTracker.proxyEventHandlers
- // tear down
- oldProvider.removeAllListeners()
- oldProvider.stop()
+ const client = createEthRpcClient(providerParams)
+ this._setClient(client)
+ }
+
+ _setClient (newClient) {
+ // teardown old client
+ const oldClient = this._currentClient
+ if (oldClient) {
+ oldClient.blockTracker.stop()
+ // asyncEventEmitter lacks a "removeAllListeners" method
+ // oldClient.blockTracker.removeAllListeners
+ oldClient.blockTracker._events = {}
}
- // override block tracler
- provider._blockTracker = createEventEmitterProxy(provider._blockTracker, blockTrackerHandlers)
// set as new provider
- this._provider = provider
- this._proxy.setTarget(provider)
+ this._currentClient = newClient
+ this.providerProxy.setTarget(newClient.provider)
+ this.blockTrackerProxy.setTarget(newClient.blockTracker)
}
_logBlock (block) {
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 94e04c429..d46dee230 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -46,6 +46,7 @@ module.exports = class TransactionController extends EventEmitter {
this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update'))
this.nonceTracker = new NonceTracker({
provider: this.provider,
+ blockTracker: this.blockTracker,
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
getConfirmedTransactions: (address) => {
return this.txStateManager.getFilteredTxList({
@@ -59,9 +60,10 @@ module.exports = class TransactionController extends EventEmitter {
this.pendingTxTracker = new PendingTransactionTracker({
provider: this.provider,
nonceTracker: this.nonceTracker,
- retryLimit: 3500, // Retry 3500 blocks, or about 1 day.
+ retryTimePeriod: 86400000, // Retry 3500 blocks, or about 1 day.
publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
+ getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
})
this.txStateManager.store.subscribe(() => this.emit('update:badge'))
diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js
index d1199a278..840b06b1a 100644
--- a/app/scripts/lib/events-proxy.js
+++ b/app/scripts/lib/events-proxy.js
@@ -1,6 +1,5 @@
-module.exports = function createEventEmitterProxy(eventEmitter, listeners) {
+module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = {}) {
let target = eventEmitter
- const eventHandlers = listeners || {}
const proxy = new Proxy({}, {
get: (obj, name) => {
// intercept listeners
@@ -14,9 +13,12 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) {
return true
},
})
+ proxy.setTarget(eventEmitter)
+ return proxy
+
function setTarget (eventEmitter) {
target = eventEmitter
- // migrate listeners
+ // migrate eventHandlers
Object.keys(eventHandlers).forEach((name) => {
eventHandlers[name].forEach((handler) => target.on(name, handler))
})
@@ -26,6 +28,4 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) {
eventHandlers[name].push(handler)
target.on(name, handler)
}
- if (listeners) proxy.setTarget(eventEmitter)
- return proxy
} \ No newline at end of file
diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js
index 832d6c6d3..d24e92206 100644
--- a/app/scripts/lib/nodeify.js
+++ b/app/scripts/lib/nodeify.js
@@ -1,10 +1,18 @@
const promiseToCallback = require('promise-to-callback')
+const noop = function(){}
module.exports = function nodeify (fn, context) {
return function(){
const args = [].slice.call(arguments)
- const callback = args.pop()
- if (typeof callback !== 'function') throw new Error('callback is not a function')
+ const lastArg = args[args.length - 1]
+ const lastArgIsCallback = typeof lastArg === 'function'
+ let callback
+ if (lastArgIsCallback) {
+ callback = lastArg
+ args.pop()
+ } else {
+ callback = noop
+ }
promiseToCallback(fn.apply(context, args))(callback)
}
}
diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js
index 0029ac953..2af40a27f 100644
--- a/app/scripts/lib/nonce-tracker.js
+++ b/app/scripts/lib/nonce-tracker.js
@@ -4,8 +4,9 @@ const Mutex = require('await-semaphore').Mutex
class NonceTracker {
- constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) {
+ constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) {
this.provider = provider
+ this.blockTracker = blockTracker
this.ethQuery = new EthQuery(provider)
this.getPendingTransactions = getPendingTransactions
this.getConfirmedTransactions = getConfirmedTransactions
@@ -53,7 +54,7 @@ class NonceTracker {
}
async _getCurrentBlock () {
- const blockTracker = this._getBlockTracker()
+ const blockTracker = this.blockTracker
const currentBlock = blockTracker.getCurrentBlock()
if (currentBlock) return currentBlock
return await Promise((reject, resolve) => {
@@ -139,11 +140,6 @@ class NonceTracker {
return { name: 'local', nonce: highest, details: { startPoint, highest } }
}
- // this is a hotfix for the fact that the blockTracker will
- // change when the network changes
- _getBlockTracker () {
- return this.provider._blockTracker
- }
}
module.exports = NonceTracker
diff --git a/app/scripts/lib/obj-proxy.js b/app/scripts/lib/obj-proxy.js
new file mode 100644
index 000000000..29ca1269f
--- /dev/null
+++ b/app/scripts/lib/obj-proxy.js
@@ -0,0 +1,19 @@
+module.exports = function createObjectProxy(obj) {
+ let target = obj
+ const proxy = new Proxy({}, {
+ get: (obj, name) => {
+ // intercept setTarget
+ if (name === 'setTarget') return setTarget
+ return target[name]
+ },
+ set: (obj, name, value) => {
+ target[name] = value
+ return true
+ },
+ })
+ return proxy
+
+ function setTarget (obj) {
+ target = obj
+ }
+} \ No newline at end of file
diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js
index 6f1601586..df504c126 100644
--- a/app/scripts/lib/pending-tx-tracker.js
+++ b/app/scripts/lib/pending-tx-tracker.js
@@ -22,9 +22,12 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
super()
this.query = new EthQuery(config.provider)
this.nonceTracker = config.nonceTracker
- this.retryLimit = config.retryLimit || Infinity
+ // default is one day
+ this.retryTimePeriod = config.retryTimePeriod || 86400000
this.getPendingTransactions = config.getPendingTransactions
+ this.getCompletedTransactions = config.getCompletedTransactions
this.publishTransaction = config.publishTransaction
+ this._checkPendingTxs()
}
// checks if a signed tx is in a block and
@@ -99,8 +102,9 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
}
async _resubmitTx (txMeta) {
- if (txMeta.retryCount > this.retryLimit) {
- const err = new Error(`Gave up submitting after ${this.retryLimit} blocks un-mined.`)
+ if (Date.now() > txMeta.time + this.retryTimePeriod) {
+ const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1)
+ const err = new Error(`Gave up submitting after ${hours} hours.`)
return this.emit('tx:failed', txMeta.id, err)
}
@@ -118,6 +122,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
async _checkPendingTx (txMeta) {
const txHash = txMeta.hash
const txId = txMeta.id
+
// extra check in case there was an uncaught error during the
// signature and submission process
if (!txHash) {
@@ -126,6 +131,15 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
this.emit('tx:failed', txId, noTxHashErr)
return
}
+
+ // If another tx with the same nonce is mined, set as failed.
+ const taken = await this._checkIfNonceIsTaken(txMeta)
+ if (taken) {
+ const nonceTakenErr = new Error('Another transaction with this nonce has been mined.')
+ nonceTakenErr.name = 'NonceTakenErr'
+ return this.emit('tx:failed', txId, nonceTakenErr)
+ }
+
// get latest transaction status
let txParams
try {
@@ -157,4 +171,13 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
}
nonceGlobalLock.releaseLock()
}
+
+ async _checkIfNonceIsTaken (txMeta) {
+ const completed = this.getCompletedTransactions()
+ const sameNonce = completed.filter((otherMeta) => {
+ return otherMeta.txParams.nonce === txMeta.txParams.nonce
+ })
+ return sameNonce.length > 0
+ }
+
}
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index cf8117864..2250403f6 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -46,6 +46,12 @@ module.exports = class TransactionStateManger extends EventEmitter {
return this.getFilteredTxList(opts)
}
+ getConfirmedTransactions (address) {
+ const opts = { status: 'confirmed' }
+ if (address) opts.from = address
+ return this.getFilteredTxList(opts)
+ }
+
addTx (txMeta) {
this.once(`${txMeta.id}:signed`, function (txId) {
this.removeAllListeners(`${txMeta.id}:rejected`)
@@ -242,4 +248,4 @@ module.exports = class TransactionStateManger extends EventEmitter {
_saveTxList (transactions) {
this.store.updateState({ transactions })
}
-} \ No newline at end of file
+}
diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js
new file mode 100644
index 000000000..8b760790e
--- /dev/null
+++ b/app/scripts/lib/typed-message-manager.js
@@ -0,0 +1,123 @@
+const EventEmitter = require('events')
+const ObservableStore = require('obs-store')
+const createId = require('./random-id')
+const assert = require('assert')
+const sigUtil = require('eth-sig-util')
+
+
+module.exports = class TypedMessageManager extends EventEmitter {
+ constructor (opts) {
+ super()
+ this.memStore = new ObservableStore({
+ unapprovedTypedMessages: {},
+ unapprovedTypedMessagesCount: 0,
+ })
+ this.messages = []
+ }
+
+ get unapprovedTypedMessagesCount () {
+ return Object.keys(this.getUnapprovedMsgs()).length
+ }
+
+ getUnapprovedMsgs () {
+ return this.messages.filter(msg => msg.status === 'unapproved')
+ .reduce((result, msg) => { result[msg.id] = msg; return result }, {})
+ }
+
+ addUnapprovedMessage (msgParams) {
+ this.validateParams(msgParams)
+
+ log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
+ // create txData obj with parameters and meta data
+ var time = (new Date()).getTime()
+ var msgId = createId()
+ var msgData = {
+ id: msgId,
+ msgParams: msgParams,
+ time: time,
+ status: 'unapproved',
+ type: 'eth_signTypedData',
+ }
+ this.addMsg(msgData)
+
+ // signal update
+ this.emit('update')
+ return msgId
+ }
+
+ validateParams (params) {
+ assert.equal(typeof params, 'object', 'Params should ben an object.')
+ assert.ok('data' in params, 'Params must include a data field.')
+ assert.ok('from' in params, 'Params must include a from field.')
+ assert.ok(Array.isArray(params.data), 'Data should be an array.')
+ assert.equal(typeof params.from, 'string', 'From field must be a string.')
+ assert.doesNotThrow(() => {
+ sigUtil.typedSignatureHash(params.data)
+ }, 'Expected EIP712 typed data')
+ }
+
+ addMsg (msg) {
+ this.messages.push(msg)
+ this._saveMsgList()
+ }
+
+ getMsg (msgId) {
+ return this.messages.find(msg => msg.id === msgId)
+ }
+
+ approveMessage (msgParams) {
+ this.setMsgStatusApproved(msgParams.metamaskId)
+ return this.prepMsgForSigning(msgParams)
+ }
+
+ setMsgStatusApproved (msgId) {
+ this._setMsgStatus(msgId, 'approved')
+ }
+
+ setMsgStatusSigned (msgId, rawSig) {
+ const msg = this.getMsg(msgId)
+ msg.rawSig = rawSig
+ this._updateMsg(msg)
+ this._setMsgStatus(msgId, 'signed')
+ }
+
+ prepMsgForSigning (msgParams) {
+ delete msgParams.metamaskId
+ return Promise.resolve(msgParams)
+ }
+
+ rejectMsg (msgId) {
+ this._setMsgStatus(msgId, 'rejected')
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ _setMsgStatus (msgId, status) {
+ const msg = this.getMsg(msgId)
+ if (!msg) throw new Error('TypedMessageManager - Message not found for id: "${msgId}".')
+ msg.status = status
+ this._updateMsg(msg)
+ this.emit(`${msgId}:${status}`, msg)
+ if (status === 'rejected' || status === 'signed') {
+ this.emit(`${msgId}:finished`, msg)
+ }
+ }
+
+ _updateMsg (msg) {
+ const index = this.messages.findIndex((message) => message.id === msg.id)
+ if (index !== -1) {
+ this.messages[index] = msg
+ }
+ this._saveMsgList()
+ }
+
+ _saveMsgList () {
+ const unapprovedTypedMessages = this.getUnapprovedMsgs()
+ const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages).length
+ this.memStore.updateState({ unapprovedTypedMessages, unapprovedTypedMessagesCount })
+ this.emit('updateBadge')
+ }
+
+}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 1a468b6c7..a742f3cba 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -25,6 +25,7 @@ const InfuraController = require('./controllers/infura')
const BlacklistController = require('./controllers/blacklist')
const MessageManager = require('./lib/message-manager')
const PersonalMessageManager = require('./lib/personal-message-manager')
+const TypedMessageManager = require('./lib/typed-message-manager')
const TransactionController = require('./controllers/transactions')
const BalancesController = require('./controllers/computed-balances')
const ConfigManager = require('./lib/config-manager')
@@ -80,9 +81,24 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.blacklistController.scheduleUpdates()
- // rpc provider
- this.provider = this.initializeProvider()
- this.blockTracker = this.provider._blockTracker
+ // rpc provider and block tracker
+ this.networkController.initializeProvider({
+ scaffold: {
+ eth_syncing: false,
+ web3_clientVersion: `MetaMask/v${version}`,
+ },
+ // account mgmt
+ getAccounts: nodeify(this.getAccounts, this),
+ // tx signing
+ processTransaction: nodeify(this.newTransaction, this),
+ // old style msg signing
+ processMessage: this.newUnsignedMessage.bind(this),
+ // personal_sign msg signing
+ processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
+ processTypedMessage: this.newUnsignedTypedMessage.bind(this),
+ })
+ this.provider = this.networkController.providerProxy
+ this.blockTracker = this.networkController.blockTrackerProxy
// eth data query tools
this.ethQuery = new EthQuery(this.provider)
@@ -161,6 +177,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.networkController.lookupNetwork()
this.messageManager = new MessageManager()
this.personalMessageManager = new PersonalMessageManager()
+ this.typedMessageManager = new TypedMessageManager()
this.publicConfigStore = this.initPublicConfigStore()
// manual disk state subscriptions
@@ -202,6 +219,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.balancesController.store.subscribe(this.sendUpdate.bind(this))
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
+ this.typedMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
this.addressBookController.store.subscribe(this.sendUpdate.bind(this))
@@ -215,35 +233,6 @@ module.exports = class MetamaskController extends EventEmitter {
// Constructor helpers
//
- initializeProvider () {
- const providerOpts = {
- static: {
- eth_syncing: false,
- web3_clientVersion: `MetaMask/v${version}`,
- },
- // account mgmt
- getAccounts: (cb) => {
- 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
- processTransaction: nodeify(async (txParams) => await this.txController.newUnapprovedTransaction(txParams), this),
- // old style msg signing
- processMessage: this.newUnsignedMessage.bind(this),
- // personal_sign msg signing
- processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
- }
- const providerProxy = this.networkController.initializeProvider(providerOpts)
- return providerProxy
- }
-
initPublicConfigStore () {
// get init state
const publicConfigStore = new ObservableStore()
@@ -283,6 +272,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.txController.memStore.getState(),
this.messageManager.memStore.getState(),
this.personalMessageManager.memStore.getState(),
+ this.typedMessageManager.memStore.getState(),
this.keyringController.memStore.getState(),
this.balancesController.store.getState(),
this.preferencesController.store.getState(),
@@ -364,6 +354,10 @@ module.exports = class MetamaskController extends EventEmitter {
signPersonalMessage: nodeify(this.signPersonalMessage, this),
cancelPersonalMessage: this.cancelPersonalMessage.bind(this),
+ // personalMessageManager
+ signTypedMessage: nodeify(this.signTypedMessage, this),
+ cancelTypedMessage: this.cancelTypedMessage.bind(this),
+
// notices
checkNotices: noticeController.updateNoticesList.bind(noticeController),
markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
@@ -474,6 +468,18 @@ module.exports = class MetamaskController extends EventEmitter {
// Opinionated Keyring Management
//
+ async getAccounts () {
+ 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)
+ }
+ return result
+ }
+
addNewAccount (cb) {
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found'))
@@ -520,6 +526,11 @@ module.exports = class MetamaskController extends EventEmitter {
// Identity Management
//
+ // this function wrappper lets us pass the fn reference before txController is instantiated
+ async newTransaction (txParams) {
+ return await this.txController.newUnapprovedTransaction(txParams)
+ }
+
newUnsignedMessage (msgParams, cb) {
const msgId = this.messageManager.addUnapprovedMessage(msgParams)
this.sendUpdate()
@@ -556,6 +567,28 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
+ newUnsignedTypedMessage (msgParams, cb) {
+ let msgId
+ try {
+ msgId = this.typedMessageManager.addUnapprovedMessage(msgParams)
+ this.sendUpdate()
+ this.opts.showUnconfirmedMessage()
+ } catch (e) {
+ return cb(e)
+ }
+
+ this.typedMessageManager.once(`${msgId}:finished`, (data) => {
+ switch (data.status) {
+ case 'signed':
+ return cb(null, data.rawSig)
+ case 'rejected':
+ return cb(new Error('MetaMask Message Signature: User denied message signature.'))
+ default:
+ return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+ }
+ })
+ }
+
signMessage (msgParams, cb) {
log.info('MetaMaskController - signMessage')
const msgId = msgParams.metamaskId
@@ -618,6 +651,24 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
+ signTypedMessage (msgParams) {
+ log.info('MetaMaskController - signTypedMessage')
+ const msgId = msgParams.metamaskId
+ // sets the status op the message to 'approved'
+ // and removes the metamaskId for signing
+ return this.typedMessageManager.approveMessage(msgParams)
+ .then((cleanMsgParams) => {
+ // signs the message
+ return this.keyringController.signTypedMessage(cleanMsgParams)
+ })
+ .then((rawSig) => {
+ // tells the listener that the message has been signed
+ // and can be returned to the dapp
+ this.typedMessageManager.setMsgStatusSigned(msgId, rawSig)
+ return this.getState()
+ })
+ }
+
cancelPersonalMessage (msgId, cb) {
const messageManager = this.personalMessageManager
messageManager.rejectMsg(msgId)
@@ -626,6 +677,14 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ cancelTypedMessage (msgId, cb) {
+ const messageManager = this.typedMessageManager
+ messageManager.rejectMsg(msgId)
+ if (cb && typeof cb === 'function') {
+ cb(null, this.getState())
+ }
+ }
+
markAccountsFound (cb) {
this.configManager.setLostAccounts([])
this.sendUpdate()