aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/scripts/inpage.js155
-rw-r--r--app/scripts/lib/auto-reload.js37
-rw-r--r--app/scripts/lib/ensnare.js24
-rw-r--r--app/scripts/lib/inpage-provider.js123
4 files changed, 196 insertions, 143 deletions
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 91d782a32..652a95d7c 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -1,17 +1,12 @@
cleanContextForImports()
-const createPayload = require('web3-provider-engine/util/create-payload')
-const StreamProvider = require('./lib/stream-provider.js')
-const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
-const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
-const RemoteStore = require('./lib/remote-store.js').RemoteStore
-const MetamaskConfig = require('./config.js')
const Web3 = require('web3')
-const once = require('once')
+const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
+const setupDappAutoReload = require('./lib/auto-reload.js')
+const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
-// rename on window
+// remove from window
delete window.Web3
-window.MetamaskWeb3 = Web3
//
@@ -19,166 +14,40 @@ window.MetamaskWeb3 = Web3
//
// setup background connection
-var pluginStream = new LocalMessageDuplexStream({
+var metamaskStream = new LocalMessageDuplexStream({
name: 'inpage',
target: 'contentscript',
})
-var mx = setupMultiplex(pluginStream)
-// connect to provider
-var remoteProvider = new StreamProvider()
-remoteProvider.pipe(mx.createStream('provider')).pipe(remoteProvider)
-remoteProvider.on('error', console.error.bind(console))
-
-// subscribe to metamask public config
-var initState = JSON.parse(localStorage['MetaMask-Config'] || '{}')
-var publicConfigStore = new RemoteStore(initState)
-var storeStream = publicConfigStore.createStream()
-storeStream.pipe(mx.createStream('publicConfig')).pipe(storeStream)
-publicConfigStore.subscribe(function(state){
- localStorage['MetaMask-Config'] = JSON.stringify(state)
-})
+// compose the inpage provider
+var inpageProvider = new MetamaskInpageProvider(metamaskStream)
//
// setup web3
//
-var web3 = new Web3(remoteProvider)
+var web3 = new Web3(inpageProvider)
web3.setProvider = function(){
console.log('MetaMask - overrode web3.setProvider')
}
console.log('MetaMask - injected web3')
//
-// automatic dapp reset
-//
-
-// export web3 as a global, checking for usage
-var pageIsUsingWeb3 = false
-var resetWasRequested = false
-window.web3 = ensnare(web3, once(function(){
- // if web3 usage happened after a reset request, trigger reset late
- if (resetWasRequested) return triggerReset()
- // mark web3 as used
- pageIsUsingWeb3 = true
- // reset web3 reference
- window.web3 = web3
-}))
-
-// listen for reset requests
-mx.createStream('control').once('data', function(){
- resetWasRequested = true
- // ignore if web3 was not used
- if (!pageIsUsingWeb3) return
- // reload after short timeout
- triggerReset()
-})
-
-function triggerReset(){
- setTimeout(function(){
- window.location.reload()
- }, 500)
-}
-
-//
-// handle synchronous requests
+// export global web3 with auto dapp reload
//
-global.publicConfigStore = publicConfigStore
+var controlStream = inpageProvider.multiStream.createStream('control')
+setupDappAutoReload(web3, controlStream)
// set web3 defaultAcount
-publicConfigStore.subscribe(function(state){
+inpageProvider.publicConfigStore.subscribe(function(state){
web3.eth.defaultAccount = state.selectedAddress
})
-// setup sync http provider
-updateProvider({ provider: publicConfigStore.get('provider') })
-publicConfigStore.subscribe(updateProvider)
-
-var syncProvider = null
-var syncProviderUrl = null
-
-function updateProvider(state){
- var providerConfig = state.provider || {}
- var newSyncProviderUrl = undefined
-
- if (providerConfig.rpcTarget) {
- newSyncProviderUrl = providerConfig.rpcTarget
- } else {
- switch(providerConfig.type) {
- case 'testnet':
- newSyncProviderUrl = MetamaskConfig.network.testnet
- break
- case 'mainnet':
- newSyncProviderUrl = MetamaskConfig.network.mainnet
- break
- default:
- newSyncProviderUrl = MetamaskConfig.network.default
- }
- }
- if (newSyncProviderUrl === syncProviderUrl) return
- syncProvider = new Web3.providers.HttpProvider(newSyncProviderUrl)
-}
-
-// handle sync methods
-remoteProvider.send = function(payload){
- var result = null
- switch (payload.method) {
-
- case 'eth_accounts':
- // read from localStorage
- var selectedAddress = publicConfigStore.get('selectedAddress')
- result = selectedAddress ? [selectedAddress] : []
- break
-
- case 'eth_coinbase':
- // read from localStorage
- var selectedAddress = publicConfigStore.get('selectedAddress')
- result = selectedAddress || '0x0000000000000000000000000000000000000000'
- break
-
- // fallback to normal rpc
- default:
- return syncProvider.send(payload)
-
- }
-
- // return the result
- return {
- id: payload.id,
- jsonrpc: payload.jsonrpc,
- result: result,
- }
-}
-
-
//
// util
//
-// 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(); return obj[key] = val },
- })
- return
- }
- })
- return proxy
-}
-
// need to make sure we aren't affected by overlapping namespaces
// and that we dont affect the app with our namespace
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
new file mode 100644
index 000000000..95a744b2c
--- /dev/null
+++ b/app/scripts/lib/auto-reload.js
@@ -0,0 +1,37 @@
+const once = require('once')
+const ensnare = require('./ensnare.js')
+
+module.exports = setupDappAutoReload
+
+
+function setupDappAutoReload(web3, controlStream){
+
+ // export web3 as a global, checking for usage
+ var pageIsUsingWeb3 = false
+ var resetWasRequested = false
+ global.web3 = ensnare(web3, once(function(){
+ // if web3 usage happened after a reset request, trigger reset late
+ if (resetWasRequested) return triggerReset()
+ // mark web3 as used
+ pageIsUsingWeb3 = true
+ // reset web3 reference
+ global.web3 = web3
+ }))
+
+ // listen for reset requests from metamask
+ controlStream.once('data', function(){
+ resetWasRequested = true
+ // ignore if web3 was not used
+ if (!pageIsUsingWeb3) return
+ // reload after short timeout
+ triggerReset()
+ })
+
+ // reload the page
+ function triggerReset(){
+ setTimeout(function(){
+ global.location.reload()
+ }, 500)
+ }
+
+} \ No newline at end of file
diff --git a/app/scripts/lib/ensnare.js b/app/scripts/lib/ensnare.js
new file mode 100644
index 000000000..b70330a5a
--- /dev/null
+++ b/app/scripts/lib/ensnare.js
@@ -0,0 +1,24 @@
+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(); return obj[key] = val },
+ })
+ return
+ }
+ })
+ return proxy
+}
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
new file mode 100644
index 000000000..66681c3a9
--- /dev/null
+++ b/app/scripts/lib/inpage-provider.js
@@ -0,0 +1,123 @@
+const HttpProvider = require('web3/lib/web3/httpprovider')
+const Streams = require('mississippi')
+const ObjectMultiplex = require('./obj-multiplex')
+const StreamProvider = require('./stream-provider.js')
+const RemoteStore = require('./remote-store.js').RemoteStore
+const MetamaskConfig = require('../config.js')
+
+module.exports = MetamaskInpageProvider
+
+
+function MetamaskInpageProvider(connectionStream){
+ const self = this
+
+ // setup connectionStream multiplexing
+ var multiStream = ObjectMultiplex()
+ Streams.pipe(connectionStream, multiStream, connectionStream, function(err){
+ console.warn('MetamaskInpageProvider - lost connection to MetaMask')
+ if (err) throw err
+ })
+ self.multiStream = multiStream
+
+ // subscribe to metamask public config
+ var publicConfigStore = remoteStoreWithLocalStorageCache('MetaMask-Config')
+ var storeStream = publicConfigStore.createStream()
+ Streams.pipe(storeStream, multiStream.createStream('publicConfig'), storeStream, function(err){
+ console.warn('MetamaskInpageProvider - lost connection to MetaMask publicConfig')
+ if (err) throw err
+ })
+ 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){
+ console.warn('MetamaskInpageProvider - lost connection to MetaMask provider')
+ if (err) throw err
+ })
+ asyncProvider.on('error', console.error.bind(console))
+ self.asyncProvider = asyncProvider
+ // overwrite own sendAsync method
+ self.sendAsync = asyncProvider.sendAsync.bind(asyncProvider)
+}
+
+MetamaskInpageProvider.prototype.send = function(payload){
+ const self = this
+
+ var result = null
+ switch (payload.method) {
+
+ case 'eth_accounts':
+ // read from localStorage
+ var selectedAddress = self.publicConfigStore.get('selectedAddress')
+ result = selectedAddress ? [selectedAddress] : []
+ break
+
+ case 'eth_coinbase':
+ // read from localStorage
+ var selectedAddress = self.publicConfigStore.get('selectedAddress')
+ result = selectedAddress || '0x0000000000000000000000000000000000000000'
+ break
+
+ // fallback to normal rpc
+ default:
+ return self.syncProvider.send(payload)
+
+ }
+
+ // return the result
+ return {
+ id: payload.id,
+ jsonrpc: payload.jsonrpc,
+ result: result,
+ }
+}
+
+MetamaskInpageProvider.prototype.sendAsync = function(){
+ throw new Error('MetamaskInpageProvider - sendAsync not overwritten')
+}
+
+MetamaskInpageProvider.prototype.isConnected = function(){
+ return true
+}
+
+// util
+
+function createSyncProvider(providerConfig){
+ providerConfig = providerConfig || {}
+ var syncProviderUrl = undefined
+
+ 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
+ }
+ }
+ return new HttpProvider(syncProviderUrl)
+}
+
+function remoteStoreWithLocalStorageCache(storageKey){
+ // read local cache
+ var initState = JSON.parse(localStorage[storageKey] || '{}')
+ var store = new RemoteStore(initState)
+ // cache the latest state locally
+ store.subscribe(function(state){
+ localStorage[storageKey] = JSON.stringify(state)
+ })
+
+ return store
+} \ No newline at end of file