aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/lib
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts/lib')
-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
9 files changed, 186 insertions, 273 deletions
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)
+ })
}