parent
27790b38a9
commit
00e9f3c6ae
@ -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 |
||||||
|
} |
Loading…
Reference in new issue