aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2016-08-30 07:14:51 +0800
committerDan Finlay <dan@danfinlay.com>2016-08-30 07:14:51 +0800
commite85418b11ad14bf6f0cc3fdbcdbb5669330c4778 (patch)
treedf71d73fa1533cea8b36fb24db90e02a07766607 /app/scripts
parent40d5b446cfbea436a012d1799d0d7710caca752c (diff)
parenta9c738d4d3226f61942641a41c399c75a3e9eb3e (diff)
downloadtangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar
tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.gz
tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.bz2
tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.lz
tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.xz
tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.zst
tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.zip
Merge branch 'master' into EdgeCompatibility
Diffstat (limited to 'app/scripts')
-rw-r--r--app/scripts/background.js42
-rw-r--r--app/scripts/config.js5
-rw-r--r--app/scripts/contentscript.js2
-rw-r--r--app/scripts/inpage.js4
-rw-r--r--app/scripts/lib/auto-reload.js2
-rw-r--r--app/scripts/lib/config-manager.js101
-rw-r--r--app/scripts/lib/ensnare.js24
-rw-r--r--app/scripts/lib/extension-instance.js6
-rw-r--r--app/scripts/lib/idStore.js18
-rw-r--r--app/scripts/lib/inpage-provider.js77
-rw-r--r--app/scripts/lib/is-popup-or-notification.js8
-rw-r--r--app/scripts/lib/local-message-stream.js56
-rw-r--r--app/scripts/lib/notifications.js167
-rw-r--r--app/scripts/metamask-controller.js68
-rw-r--r--app/scripts/popup.js14
15 files changed, 271 insertions, 323 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 34c994ab7..5dae8235f 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -3,9 +3,7 @@ const extend = require('xtend')
const Dnode = require('dnode')
const eos = require('end-of-stream')
const PortStream = require('./lib/port-stream.js')
-const createUnlockRequestNotification = require('./lib/notifications.js').createUnlockRequestNotification
-const createTxNotification = require('./lib/notifications.js').createTxNotification
-const createMsgNotification = require('./lib/notifications.js').createMsgNotification
+const notification = require('./lib/notifications.js')
const messageManager = require('./lib/message-manager')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const MetamaskController = require('./metamask-controller')
@@ -13,6 +11,7 @@ const extension = require('./lib/extension')
const STORAGE_KEY = 'metamask-config'
+
const controller = new MetamaskController({
// User confirmation callbacks:
showUnconfirmedMessage,
@@ -25,41 +24,15 @@ const controller = new MetamaskController({
const idStore = controller.idStore
function unlockAccountMessage () {
- createUnlockRequestNotification({
- title: 'Account Unlock Request',
- })
+ notification.show()
}
function showUnconfirmedMessage (msgParams, msgId) {
- var controllerState = controller.getState()
-
- createMsgNotification({
- imageifyIdenticons: false,
- txData: {
- msgParams: msgParams,
- time: (new Date()).getTime(),
- },
- identities: controllerState.identities,
- accounts: controllerState.accounts,
- onConfirm: idStore.approveMessage.bind(idStore, msgId, noop),
- onCancel: idStore.cancelMessage.bind(idStore, msgId),
- })
+ notification.show()
}
function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
- var controllerState = controller.getState()
-
- createTxNotification({
- imageifyIdenticons: false,
- txData: {
- txParams: txParams,
- time: (new Date()).getTime(),
- },
- identities: controllerState.identities,
- accounts: controllerState.accounts,
- onConfirm: idStore.approveTransaction.bind(idStore, txData.id, noop),
- onCancel: idStore.cancelTransaction.bind(idStore, txData.id),
- })
+ notification.show()
}
//
@@ -68,7 +41,7 @@ function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
extension.runtime.onConnect.addListener(connectRemote)
function connectRemote (remotePort) {
- var isMetaMaskInternalProcess = (remotePort.name === 'popup')
+ var isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification'
var portStream = new PortStream(remotePort)
if (isMetaMaskInternalProcess) {
// communication with popup
@@ -108,7 +81,7 @@ function setupControllerConnection (stream) {
dnode.on('remote', (remote) => {
// push updates to popup
controller.ethStore.on('update', controller.sendUpdate.bind(controller))
- controller.remote = remote
+ controller.listeners.push(remote)
idStore.on('update', controller.sendUpdate.bind(controller))
// teardown on disconnect
@@ -188,4 +161,3 @@ function setData (data) {
window.localStorage[STORAGE_KEY] = JSON.stringify(data)
}
-function noop () {}
diff --git a/app/scripts/config.js b/app/scripts/config.js
index 5f6ffd936..04e2907d4 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -1,14 +1,13 @@
const MAINET_RPC_URL = 'https://mainnet.infura.io/'
const TESTNET_RPC_URL = 'https://morden.infura.io/'
const DEFAULT_RPC_URL = TESTNET_RPC_URL
-const CLASSIC_RPC_URL = 'https://mainnet-nf.infura.io/'
+
+global.METAMASK_DEBUG = false
module.exports = {
network: {
default: DEFAULT_RPC_URL,
mainnet: MAINET_RPC_URL,
testnet: TESTNET_RPC_URL,
- classic: CLASSIC_RPC_URL,
},
}
-
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 1eb04059d..de2cf263b 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -1,4 +1,4 @@
-const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
+const LocalMessageDuplexStream = require('post-message-stream')
const PortStream = require('./lib/port-stream.js')
const ObjectMultiplex = require('./lib/obj-multiplex')
const extension = require('./lib/extension')
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 055235671..28a1223ac 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -1,7 +1,7 @@
/*global Web3*/
cleanContextForImports()
require('web3/dist/web3.min.js')
-const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
+const LocalMessageDuplexStream = require('post-message-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
@@ -54,7 +54,7 @@ var __define
function cleanContextForImports () {
__define = global.define
try {
- delete global.define
+ global.define = undefined
} catch (_) {
console.warn('MetaMask - global.define could not be deleted.')
}
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
index b45f02009..c4c8053f0 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -1,5 +1,5 @@
const once = require('once')
-const ensnare = require('./ensnare.js')
+const ensnare = require('ensnare')
module.exports = setupDappAutoReload
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 727cd46fc..715efb42e 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -1,10 +1,11 @@
const Migrator = require('pojo-migrator')
const MetamaskConfig = require('../config.js')
const migrations = require('./migrations')
+const rp = require('request-promise')
const TESTNET_RPC = MetamaskConfig.network.testnet
const MAINNET_RPC = MetamaskConfig.network.mainnet
-const CLASSIC_RPC = MetamaskConfig.network.classic
+const txLimit = 40
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
@@ -15,6 +16,8 @@ const CLASSIC_RPC = MetamaskConfig.network.classic
*/
module.exports = ConfigManager
function ConfigManager (opts) {
+ this.txLimit = txLimit
+
// ConfigManager is observable and will emit updates
this._subs = []
@@ -145,9 +148,6 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
case 'testnet':
return TESTNET_RPC
- case 'classic':
- return CLASSIC_RPC
-
default:
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
}
@@ -184,6 +184,9 @@ ConfigManager.prototype._saveTxList = function (txList) {
ConfigManager.prototype.addTx = function (tx) {
var transactions = this.getTxList()
+ while (transactions.length > this.txLimit - 1) {
+ transactions.shift()
+ }
transactions.push(tx)
this._saveTxList(transactions)
}
@@ -274,9 +277,61 @@ ConfigManager.prototype.getConfirmed = function () {
return ('isConfirmed' in data) && data.isConfirmed
}
-ConfigManager.prototype.setShouldntShowWarning = function (confirmed) {
+ConfigManager.prototype.setCurrentFiat = function (currency) {
+ var data = this.getData()
+ data.fiatCurrency = currency
+ this.setData(data)
+}
+
+ConfigManager.prototype.getCurrentFiat = function () {
+ var data = this.getData()
+ return ('fiatCurrency' in data) && data.fiatCurrency
+}
+
+ConfigManager.prototype.updateConversionRate = function () {
+ var data = this.getData()
+ return rp(`https://www.cryptonator.com/api/ticker/eth-${data.fiatCurrency}`)
+ .then((response) => {
+ const parsedResponse = JSON.parse(response)
+ this.setConversionPrice(parsedResponse.ticker.price)
+ this.setConversionDate(parsedResponse.timestamp)
+ }).catch((err) => {
+ console.error('Error in conversion.', err)
+ 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 (('conversionRate' in data) && data.conversionRate) || 0
+}
+
+ConfigManager.prototype.getConversionDate = function () {
var data = this.getData()
- data.isEthConfirmed = confirmed
+ return (('conversionDate' in data) && data.conversionDate) || 'N/A'
+}
+
+ConfigManager.prototype.setShouldntShowWarning = function () {
+ var data = this.getData()
+ if (data.isEthConfirmed) {
+ data.isEthConfirmed = !data.isEthConfirmed
+ } else {
+ data.isEthConfirmed = true
+ }
this.setData(data)
}
@@ -284,3 +339,37 @@ ConfigManager.prototype.getShouldntShowWarning = function () {
var data = this.getData()
return ('isEthConfirmed' in data) && data.isEthConfirmed
}
+
+ConfigManager.prototype.getShapeShiftTxList = function () {
+ var data = this.getData()
+ var shapeShiftTxList = data.shapeShiftTxList ? data.shapeShiftTxList : []
+ shapeShiftTxList.forEach((tx) => {
+ if (tx.response.status !== 'complete') {
+ var requestListner = function (request) {
+ tx.response = JSON.parse(this.responseText)
+ if (tx.response.status === 'complete') {
+ tx.time = new Date().getTime()
+ }
+ }
+
+ var shapShiftReq = new XMLHttpRequest()
+ shapShiftReq.addEventListener('load', requestListner)
+ shapShiftReq.open('GET', `https://shapeshift.io/txStat/${tx.depositAddress}`, true)
+ shapShiftReq.send()
+ }
+ })
+ this.setData(data)
+ return shapeShiftTxList
+}
+
+ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositType) {
+ var data = this.getData()
+
+ var shapeShiftTx = {depositAddress, depositType, key: 'shapeshift', time: new Date().getTime(), response: {}}
+ if (!data.shapeShiftTxList) {
+ data.shapeShiftTxList = [shapeShiftTx]
+ } else {
+ data.shapeShiftTxList.push(shapeShiftTx)
+ }
+ this.setData(data)
+}
diff --git a/app/scripts/lib/ensnare.js b/app/scripts/lib/ensnare.js
deleted file mode 100644
index 6100f7c79..000000000
--- a/app/scripts/lib/ensnare.js
+++ /dev/null
@@ -1,24 +0,0 @@
-module.exports = ensnare
-
-// creates a proxy object that calls cb everytime the obj's properties/fns are accessed
-function ensnare (obj, cb) {
- var proxy = {}
- Object.keys(obj).forEach(function (key) {
- var val = obj[key]
- switch (typeof val) {
- case 'function':
- proxy[key] = function () {
- cb()
- val.apply(obj, arguments)
- }
- return
- default:
- Object.defineProperty(proxy, key, {
- get: function () { cb(); return obj[key] },
- set: function (val) { cb(); obj[key] = val; return val },
- })
- return
- }
- })
- return proxy
-}
diff --git a/app/scripts/lib/extension-instance.js b/app/scripts/lib/extension-instance.js
index eb3b8a1e9..1098130e3 100644
--- a/app/scripts/lib/extension-instance.js
+++ b/app/scripts/lib/extension-instance.js
@@ -42,6 +42,12 @@ function Extension () {
} catch (e) {}
try {
+ if (browser[api]) {
+ _this[api] = browser[api]
+ }
+ } catch (e) {}
+
+ try {
_this.api = browser.extension[api]
} catch (e) {}
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index c6ac55a03..0f36a520b 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -45,7 +45,11 @@ function IdentityStore (opts = {}) {
IdentityStore.prototype.createNewVault = function (password, entropy, cb) {
delete this._keyStore
+ var serializedKeystore = this.configManager.getWallet()
+ if (serializedKeystore) {
+ this.configManager.setData({})
+ }
this._createIdmgmt(password, null, entropy, (err) => {
if (err) return cb(err)
@@ -100,6 +104,10 @@ IdentityStore.prototype.getState = function () {
unconfMsgs: messageManager.unconfirmedMsgs(),
messages: messageManager.getMsgList(),
selectedAddress: configManager.getSelectedAccount(),
+ shapeShiftTxList: configManager.getShapeShiftTxList(),
+ currentFiat: configManager.getCurrentFiat(),
+ conversionRate: configManager.getConversionRate(),
+ conversionDate: configManager.getConversionDate(),
}))
}
@@ -153,8 +161,9 @@ IdentityStore.prototype.getNetwork = function (err) {
this._currentState.network = 'loading'
return this._didUpdate()
}
-
- console.log('web3.getNetwork returned ' + network)
+ if (global.METAMASK_DEBUG) {
+ console.log('web3.getNetwork returned ' + network)
+ }
this._currentState.network = network
this._didUpdate()
})
@@ -432,6 +441,7 @@ IdentityStore.prototype.tryPassword = function (password, cb) {
IdentityStore.prototype._createIdmgmt = function (password, seed, entropy, cb) {
const configManager = this.configManager
+
var keyStore = null
LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
if (err) return cb(err)
@@ -475,7 +485,9 @@ IdentityStore.prototype._restoreFromSeed = function (password, seed, derivedKey)
keyStore.generateNewAddress(derivedKey, 3)
configManager.setWallet(keyStore.serialize())
- console.log('restored from seed. saved to keystore')
+ if (global.METAMASK_DEBUG) {
+ console.log('restored from seed. saved to keystore')
+ }
return keyStore
}
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index e387be895..65354cd3d 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -1,9 +1,7 @@
-const HttpProvider = require('web3/lib/web3/httpprovider')
const Streams = require('mississippi')
const ObjectMultiplex = require('./obj-multiplex')
const StreamProvider = require('web3-stream-provider')
const RemoteStore = require('./remote-store.js').RemoteStore
-const MetamaskConfig = require('../config.js')
module.exports = MetamaskInpageProvider
@@ -27,13 +25,6 @@ function MetamaskInpageProvider (connectionStream) {
})
self.publicConfigStore = publicConfigStore
- // connect to sync provider
- self.syncProvider = createSyncProvider(publicConfigStore.get('provider'))
- // subscribe to publicConfig to update the syncProvider on change
- publicConfigStore.subscribe(function (state) {
- self.syncProvider = createSyncProvider(state.provider)
- })
-
// connect to async provider
var asyncProvider = new StreamProvider()
Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) {
@@ -42,15 +33,23 @@ function MetamaskInpageProvider (connectionStream) {
})
asyncProvider.on('error', console.error.bind(console))
self.asyncProvider = asyncProvider
- // overwrite own sendAsync method
- self.sendAsync = asyncProvider.sendAsync.bind(asyncProvider)
+ // handle sendAsync requests via asyncProvider
+ self.sendAsync = function(payload, cb){
+ // rewrite request ids
+ var request = jsonrpcMessageTransform(payload, (message) => {
+ message.id = createRandomId()
+ return message
+ })
+ // forward to asyncProvider
+ asyncProvider.sendAsync(request, cb)
+ }
}
MetamaskInpageProvider.prototype.send = function (payload) {
const self = this
+
let selectedAddress
-
- var result = null
+ let result = null
switch (payload.method) {
case 'eth_accounts':
@@ -65,9 +64,10 @@ MetamaskInpageProvider.prototype.send = function (payload) {
result = selectedAddress || '0x0000000000000000000000000000000000000000'
break
- // fallback to normal rpc
+ // throw not-supported Error
default:
- return self.syncProvider.send(payload)
+ var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#all-async---think-of-metamask-as-a-light-client for details.'
+ throw new Error(message)
}
@@ -89,35 +89,6 @@ MetamaskInpageProvider.prototype.isConnected = function () {
// util
-function createSyncProvider (providerConfig) {
- providerConfig = providerConfig || {}
- let syncProviderUrl
-
- if (providerConfig.rpcTarget) {
- syncProviderUrl = providerConfig.rpcTarget
- } else {
- switch (providerConfig.type) {
- case 'testnet':
- syncProviderUrl = MetamaskConfig.network.testnet
- break
- case 'mainnet':
- syncProviderUrl = MetamaskConfig.network.mainnet
- break
- default:
- syncProviderUrl = MetamaskConfig.network.default
- }
- }
-
- const provider = new HttpProvider(syncProviderUrl)
- // Stubbing out the send method to throw on sync methods:
- provider.send = function() {
- var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq#all-async---think-of-metamask-as-a-light-client for details.'
- throw new Error(message)
- }
-
- return provider
-}
-
function remoteStoreWithLocalStorageCache (storageKey) {
// read local cache
var initState = JSON.parse(localStorage[storageKey] || '{}')
@@ -129,3 +100,21 @@ function remoteStoreWithLocalStorageCache (storageKey) {
return store
}
+
+function createRandomId(){
+ const extraDigits = 3
+ // 13 time digits
+ const datePart = new Date().getTime() * Math.pow(10, extraDigits)
+ // 3 random digits
+ const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits))
+ // 16 digits
+ return datePart + extraPart
+}
+
+function jsonrpcMessageTransform(payload, transformFn){
+ if (Array.isArray(payload)) {
+ return payload.map(transformFn)
+ } else {
+ return transformFn(payload)
+ }
+} \ No newline at end of file
diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js
new file mode 100644
index 000000000..5c38ac823
--- /dev/null
+++ b/app/scripts/lib/is-popup-or-notification.js
@@ -0,0 +1,8 @@
+module.exports = function isPopupOrNotification() {
+ const url = window.location.href
+ if (url.match(/popup.html$/)) {
+ return 'popup'
+ } else {
+ return 'notification'
+ }
+}
diff --git a/app/scripts/lib/local-message-stream.js b/app/scripts/lib/local-message-stream.js
deleted file mode 100644
index 821e51046..000000000
--- a/app/scripts/lib/local-message-stream.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const Duplex = require('readable-stream').Duplex
-const inherits = require('util').inherits
-
-module.exports = LocalMessageDuplexStream
-
-inherits(LocalMessageDuplexStream, Duplex)
-
-function LocalMessageDuplexStream (opts) {
- Duplex.call(this, {
- objectMode: true,
- })
-
- // this._origin = opts.origin
- this._name = opts.name
- this._target = opts.target
-
- // console.log('LocalMessageDuplexStream ('+this._name+') - initialized...')
- window.addEventListener('message', this._onMessage.bind(this), false)
-}
-
-// private
-
-LocalMessageDuplexStream.prototype._onMessage = function (event) {
- var msg = event.data
- // console.log('LocalMessageDuplexStream ('+this._name+') - heard message...', event)
- // validate message
- if (event.origin !== location.origin) return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (event.origin !== location.origin) ')
- if (typeof msg !== 'object') return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (typeof msg !== "object") ')
- if (msg.target !== this._name) return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (msg.target !== this._name) ', msg.target, this._name)
- if (!msg.data) return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (!msg.data) ')
- // console.log('LocalMessageDuplexStream ('+this._name+') - accepted', msg.data)
- // forward message
- try {
- this.push(msg.data)
- } catch (err) {
- this.emit('error', err)
- }
-}
-
-// stream plumbing
-
-LocalMessageDuplexStream.prototype._read = noop
-
-LocalMessageDuplexStream.prototype._write = function (data, encoding, cb) {
- // console.log('LocalMessageDuplexStream ('+this._name+') - sending message...')
- var message = {
- target: this._target,
- data: data,
- }
- window.postMessage(message, location.origin)
- cb()
-}
-
-// util
-
-function noop () {}
diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js
index 6c1601df1..dcb946845 100644
--- a/app/scripts/lib/notifications.js
+++ b/app/scripts/lib/notifications.js
@@ -1,159 +1,48 @@
-const createId = require('hat')
-const extend = require('xtend')
-const unmountComponentAtNode = require('react-dom').unmountComponentAtNode
-const findDOMNode = require('react-dom').findDOMNode
-const render = require('react-dom').render
-const h = require('react-hyperscript')
-const PendingTxDetails = require('../../../ui/app/components/pending-tx-details')
-const PendingMsgDetails = require('../../../ui/app/components/pending-msg-details')
-const MetaMaskUiCss = require('../../../ui/css')
const extension = require('./extension')
-var notificationHandlers = {}
const notifications = {
- createUnlockRequestNotification: createUnlockRequestNotification,
- createTxNotification: createTxNotification,
- createMsgNotification: createMsgNotification,
+ show,
+ getPopup,
+ closePopup,
}
module.exports = notifications
window.METAMASK_NOTIFIER = notifications
-setupListeners()
-
-function setupListeners () {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
-
- // notification button press
- extension.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
- var handlers = notificationHandlers[notificationId]
- if (buttonIndex === 0) {
- handlers.confirm()
- } else {
- handlers.cancel()
+function show () {
+ getPopup((popup) => {
+ if (popup) {
+ return extension.windows.update(popup.id, { focused: true })
}
- extension.notifications.clear(notificationId)
- })
-
- // notification teardown
- extension.notifications.onClosed.addListener(function (notificationId) {
- delete notificationHandlers[notificationId]
- })
-}
-
-// creation helper
-function createUnlockRequestNotification (opts) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
- var message = 'An Ethereum app has requested a signature. Please unlock your account.'
-
- var id = createId()
- extension.notifications.create(id, {
- type: 'basic',
- iconUrl: '/images/icon-128.png',
- title: opts.title,
- message: message,
- })
-}
-
-function createTxNotification (state) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
-
- renderTxNotificationSVG(state, function (err, notificationSvgSource) {
- if (err) throw err
-
- showNotification(extend(state, {
- title: 'New Unsigned Transaction',
- imageUrl: toSvgUri(notificationSvgSource),
- }))
- })
-}
-
-function createMsgNotification (state) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
-
- renderMsgNotificationSVG(state, function (err, notificationSvgSource) {
- if (err) throw err
- showNotification(extend(state, {
- title: 'New Unsigned Message',
- imageUrl: toSvgUri(notificationSvgSource),
- }))
+ extension.windows.create({
+ url: 'notification.html',
+ type: 'detached_panel',
+ focused: true,
+ width: 360,
+ height: 500,
+ })
})
}
-function showNotification (state) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
+function getPopup(cb) {
- var id = createId()
- extension.notifications.create(id, {
- type: 'image',
- requireInteraction: true,
- iconUrl: '/images/icon-128.png',
- imageUrl: state.imageUrl,
- title: state.title,
- message: '',
- buttons: [{
- title: 'Approve',
- }, {
- title: 'Reject',
- }],
- })
- notificationHandlers[id] = {
- confirm: state.onConfirm,
- cancel: state.onCancel,
+ // Ignore in test environment
+ if (!extension.windows) {
+ return cb(null)
}
-}
-function renderTxNotificationSVG (state, cb) {
- var content = h(PendingTxDetails, state)
- renderNotificationSVG(content, cb)
-}
+ extension.windows.getAll({}, (windows) => {
+ let popup = windows.find((win) => {
+ return win.type === 'popup'
+ })
-function renderMsgNotificationSVG (state, cb) {
- var content = h(PendingMsgDetails, state)
- renderNotificationSVG(content, cb)
-}
-
-function renderNotificationSVG (content, cb) {
- var container = document.createElement('div')
- var confirmView = h('div.app-primary', {
- style: {
- width: '360px',
- height: '240px',
- padding: '16px',
- // background: '#F7F7F7',
- background: 'white',
- },
- }, [
- h('style', MetaMaskUiCss()),
- content,
- ])
-
- render(confirmView, container, function ready() {
- var rootElement = findDOMNode(this)
- var viewSource = rootElement.outerHTML
- unmountComponentAtNode(container)
- var svgSource = svgWrapper(viewSource)
- // insert content into svg wrapper
- cb(null, svgSource)
+ cb(popup)
})
}
-function svgWrapper (content) {
- var wrapperSource = `
- <svg xmlns="http://www.w3.org/2000/svg" width="360" height="240">
- <foreignObject x="0" y="0" width="100%" height="100%">
- <body xmlns="http://www.w3.org/1999/xhtml" height="100%">{{content}}</body>
- </foreignObject>
- </svg>
- `
- return wrapperSource.split('{{content}}').join(content)
-}
-
-function toSvgUri (content) {
- return 'data:image/svg+xml;utf8,' + encodeURIComponent(content)
+function closePopup() {
+ getPopup((popup) => {
+ if (!popup) return
+ extension.windows.remove(popup.id, console.error)
+ })
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index dd43ac2fc..e94db2dfd 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -12,6 +12,7 @@ module.exports = class MetamaskController {
constructor (opts) {
this.opts = opts
+ this.listeners = []
this.configManager = new ConfigManager(opts)
this.idStore = new IdentityStore({
configManager: this.configManager,
@@ -21,6 +22,9 @@ module.exports = class MetamaskController {
this.idStore.setStore(this.ethStore)
this.messageManager = messageManager
this.publicConfigStore = this.initPublicConfigStore()
+ this.configManager.setCurrentFiat('USD')
+ this.configManager.updateConversionRate()
+ this.scheduleConversionInterval()
}
getState () {
@@ -40,7 +44,9 @@ module.exports = class MetamaskController {
setProviderType: this.setProviderType.bind(this),
useEtherscanProvider: this.useEtherscanProvider.bind(this),
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
+ setCurrentFiat: this.setCurrentFiat.bind(this),
agreeToEthWarning: this.agreeToEthWarning.bind(this),
+
// forward directly to idStore
createNewVault: idStore.createNewVault.bind(idStore),
recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
@@ -59,6 +65,8 @@ module.exports = class MetamaskController {
recoverSeed: idStore.recoverSeed.bind(idStore),
// coinbase
buyEth: this.buyEth.bind(this),
+ // shapeshift
+ createShapeShiftTx: this.createShapeShiftTx.bind(this),
}
}
@@ -94,7 +102,9 @@ module.exports = class MetamaskController {
function logger (err, request, response) {
if (err) return console.error(err)
if (!request.isMetamaskInternal) {
- console.log(`RPC (${originDomain}):`, request, '->', response)
+ if (global.METAMASK_DEBUG) {
+ console.log(`RPC (${originDomain}):`, request, '->', response)
+ }
if (response.error) {
console.error('Error in RPC response:\n', response.error)
}
@@ -103,9 +113,9 @@ module.exports = class MetamaskController {
}
sendUpdate () {
- if (this.remote) {
- this.remote.sendUpdate(this.getState())
- }
+ this.listeners.forEach((remote) => {
+ remote.sendUpdate(this.getState())
+ })
}
initializeProvider (opts) {
@@ -121,10 +131,17 @@ module.exports = class MetamaskController {
},
// tx signing
approveTransaction: this.newUnsignedTransaction.bind(this),
- signTransaction: idStore.signTransaction.bind(idStore),
+ signTransaction: (...args) => {
+ idStore.signTransaction(...args)
+ this.sendUpdate()
+ },
+
// msg signing
approveMessage: this.newUnsignedMessage.bind(this),
- signMessage: idStore.signMessage.bind(idStore),
+ signMessage: (...args) => {
+ idStore.signMessage(...args)
+ this.sendUpdate()
+ },
}
var provider = MetaMaskProvider(providerOpts)
@@ -184,6 +201,8 @@ module.exports = class MetamaskController {
// It's locked
if (!state.isUnlocked) {
+
+ // Allow the environment to define an unlock message.
this.opts.unlockAccountMessage()
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
@@ -191,6 +210,7 @@ module.exports = class MetamaskController {
} else {
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
if (err) return onTxDoneCb(err)
+ this.sendUpdate()
this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
})
}
@@ -199,9 +219,11 @@ module.exports = class MetamaskController {
newUnsignedMessage (msgParams, cb) {
var state = this.idStore.getState()
if (!state.isUnlocked) {
+ this.idStore.addUnconfirmedMessage(msgParams, cb)
this.opts.unlockAccountMessage()
} else {
this.addUnconfirmedMessage(msgParams, cb)
+ this.sendUpdate()
}
}
@@ -218,7 +240,9 @@ module.exports = class MetamaskController {
// Log blocks
processBlock (block) {
- console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
+ if (global.METAMASK_DEBUG) {
+ console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
+ }
this.verifyNetwork()
}
@@ -241,9 +265,34 @@ module.exports = class MetamaskController {
}
}
+ setCurrentFiat (fiat, cb) {
+ try {
+ this.configManager.setCurrentFiat(fiat)
+ this.configManager.updateConversionRate()
+ this.scheduleConversionInterval()
+ const data = {
+ conversionRate: this.configManager.getConversionRate(),
+ currentFiat: this.configManager.getCurrentFiat(),
+ conversionDate: this.configManager.getConversionDate(),
+ }
+ cb(data)
+ } catch (e) {
+ cb(null, e)
+ }
+ }
+
+ scheduleConversionInterval () {
+ if (this.conversionInterval) {
+ clearInterval(this.conversionInterval)
+ }
+ this.conversionInterval = setInterval(() => {
+ this.configManager.updateConversionRate()
+ }, 300000)
+ }
+
agreeToEthWarning (cb) {
try {
- this.configManager.setShouldntShowWarning(true)
+ this.configManager.setShouldntShowWarning()
cb()
} catch (e) {
cb(e)
@@ -283,6 +332,9 @@ module.exports = class MetamaskController {
})
}
+ createShapeShiftTx (depositAddress, depositType) {
+ this.configManager.createShapeShiftTx(depositAddress, depositType)
+ }
}
function noop () {}
diff --git a/app/scripts/popup.js b/app/scripts/popup.js
index 20be15df7..90b90a7af 100644
--- a/app/scripts/popup.js
+++ b/app/scripts/popup.js
@@ -9,7 +9,9 @@ const injectCss = require('inject-css')
const PortStream = require('./lib/port-stream.js')
const StreamProvider = require('web3-stream-provider')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
+const isPopupOrNotification = require('./lib/is-popup-or-notification')
const extension = require('./lib/extension')
+const notification = require('./lib/notifications')
// setup app
var css = MetaMaskUiCss()
@@ -22,7 +24,11 @@ async.parallel({
function connectToAccountManager (cb) {
// setup communication with background
- var pluginPort = extension.runtime.connect({name: 'popup'})
+
+ var name = isPopupOrNotification()
+ closePopupIfOpen(name)
+ window.METAMASK_UI_TYPE = name
+ var pluginPort = extension.runtime.connect({ name })
var portStream = new PortStream(pluginPort)
// setup multiplexing
var mx = setupMultiplex(portStream)
@@ -93,3 +99,9 @@ function setupApp (err, opts) {
networkVersion: opts.networkVersion,
})
}
+
+function closePopupIfOpen(name) {
+ if (name !== 'notification') {
+ notification.closePopup()
+ }
+}