inpage - refactor for modularity

feature/default_network_editable
kumavis 9 years ago
parent 27790b38a9
commit 00e9f3c6ae
  1. 155
      app/scripts/inpage.js
  2. 37
      app/scripts/lib/auto-reload.js
  3. 24
      app/scripts/lib/ensnare.js
  4. 123
      app/scripts/lib/inpage-provider.js
  5. 1
      package.json

@ -1,17 +1,12 @@
cleanContextForImports() 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 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() restoreContextAfterImports()
// rename on window // remove from window
delete window.Web3 delete window.Web3
window.MetamaskWeb3 = Web3
// //
@ -19,166 +14,40 @@ window.MetamaskWeb3 = Web3
// //
// setup background connection // setup background connection
var pluginStream = new LocalMessageDuplexStream({ var metamaskStream = new LocalMessageDuplexStream({
name: 'inpage', name: 'inpage',
target: 'contentscript', target: 'contentscript',
}) })
var mx = setupMultiplex(pluginStream)
// connect to provider // compose the inpage provider
var remoteProvider = new StreamProvider() var inpageProvider = new MetamaskInpageProvider(metamaskStream)
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)
})
// //
// setup web3 // setup web3
// //
var web3 = new Web3(remoteProvider) var web3 = new Web3(inpageProvider)
web3.setProvider = function(){ web3.setProvider = function(){
console.log('MetaMask - overrode web3.setProvider') console.log('MetaMask - overrode web3.setProvider')
} }
console.log('MetaMask - injected web3') console.log('MetaMask - injected web3')
// //
// automatic dapp reset // export global web3 with auto dapp reload
//
// 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
// //
global.publicConfigStore = publicConfigStore var controlStream = inpageProvider.multiStream.createStream('control')
setupDappAutoReload(web3, controlStream)
// set web3 defaultAcount // set web3 defaultAcount
publicConfigStore.subscribe(function(state){ inpageProvider.publicConfigStore.subscribe(function(state){
web3.eth.defaultAccount = state.selectedAddress 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 // 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 // need to make sure we aren't affected by overlapping namespaces
// and that we dont affect the app with our namespace // and that we dont affect the app with our namespace
// mostly a fix for web3's BigNumber if AMD's "define" is defined... // mostly a fix for web3's BigNumber if AMD's "define" is defined...

@ -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)
}
}

@ -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
}

@ -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
}

@ -39,6 +39,7 @@
"jazzicon": "^1.1.3", "jazzicon": "^1.1.3",
"menu-droppo": "^1.1.0", "menu-droppo": "^1.1.0",
"metamask-logo": "^1.1.5", "metamask-logo": "^1.1.5",
"mississippi": "^1.2.0",
"multiplex": "^6.7.0", "multiplex": "^6.7.0",
"once": "^1.3.3", "once": "^1.3.3",
"pojo-migrator": "^2.1.0", "pojo-migrator": "^2.1.0",

Loading…
Cancel
Save