mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
inpage - refactor for modularity
This commit is contained in:
parent
27790b38a9
commit
00e9f3c6ae
@ -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 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
|
||||
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...
|
||||
|
37
app/scripts/lib/auto-reload.js
Normal file
37
app/scripts/lib/auto-reload.js
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
24
app/scripts/lib/ensnare.js
Normal file
24
app/scripts/lib/ensnare.js
Normal file
@ -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
|
||||
}
|
123
app/scripts/lib/inpage-provider.js
Normal file
123
app/scripts/lib/inpage-provider.js
Normal file
@ -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",
|
||||
"menu-droppo": "^1.1.0",
|
||||
"metamask-logo": "^1.1.5",
|
||||
"mississippi": "^1.2.0",
|
||||
"multiplex": "^6.7.0",
|
||||
"once": "^1.3.3",
|
||||
"pojo-migrator": "^2.1.0",
|
||||
|
Loading…
Reference in New Issue
Block a user