2016-05-23 00:23:16 +02:00
const Streams = require ( 'mississippi' )
2016-05-23 03:02:27 +02:00
const StreamProvider = require ( 'web3-stream-provider' )
2016-10-12 21:35:55 +02:00
const ObjectMultiplex = require ( './obj-multiplex' )
2016-05-23 00:23:16 +02:00
const RemoteStore = require ( './remote-store.js' ) . RemoteStore
module . exports = MetamaskInpageProvider
2016-06-21 22:18:32 +02:00
function MetamaskInpageProvider ( connectionStream ) {
2016-05-23 00:23:16 +02:00
const self = this
2016-06-21 22:18:32 +02:00
// setup connectionStream multiplexing
2016-05-23 00:23:16 +02:00
var multiStream = ObjectMultiplex ( )
2016-06-21 22:18:32 +02:00
Streams . pipe ( connectionStream , multiStream , connectionStream , function ( err ) {
2016-10-12 21:35:55 +02:00
let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask'
if ( err ) warningMsg += '\n' + err . stack
console . warn ( warningMsg )
2016-05-23 00:23:16 +02:00
} )
self . multiStream = multiStream
// subscribe to metamask public config
var publicConfigStore = remoteStoreWithLocalStorageCache ( 'MetaMask-Config' )
var storeStream = publicConfigStore . createStream ( )
2016-06-21 22:18:32 +02:00
Streams . pipe ( storeStream , multiStream . createStream ( 'publicConfig' ) , storeStream , function ( err ) {
2016-10-12 21:35:55 +02:00
let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask publicConfig'
if ( err ) warningMsg += '\n' + err . stack
console . warn ( warningMsg )
2016-05-23 00:23:16 +02:00
} )
self . publicConfigStore = publicConfigStore
// connect to async provider
var asyncProvider = new StreamProvider ( )
2016-06-21 22:18:32 +02:00
Streams . pipe ( asyncProvider , multiStream . createStream ( 'provider' ) , asyncProvider , function ( err ) {
2016-10-12 21:35:55 +02:00
let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask provider'
if ( err ) warningMsg += '\n' + err . stack
console . warn ( warningMsg )
2016-05-23 00:23:16 +02:00
} )
asyncProvider . on ( 'error' , console . error . bind ( console ) )
self . asyncProvider = asyncProvider
2016-10-05 20:10:39 +02:00
2016-09-01 00:40:05 +02:00
self . idMap = { }
2016-08-23 03:59:15 +02:00
// handle sendAsync requests via asyncProvider
self . sendAsync = function ( payload , cb ) {
// rewrite request ids
2016-09-01 00:40:05 +02:00
var request = eachJsonMessage ( payload , ( message ) => {
var newId = createRandomId ( )
self . idMap [ newId ] = message . id
message . id = newId
2016-08-23 03:59:15 +02:00
return message
} )
// forward to asyncProvider
2016-09-01 00:40:05 +02:00
asyncProvider . sendAsync ( request , function ( err , res ) {
if ( err ) return cb ( err )
// transform messages to original ids
eachJsonMessage ( res , ( message ) => {
var oldId = self . idMap [ message . id ]
delete self . idMap [ message . id ]
message . id = oldId
return message
} )
cb ( null , res )
} )
2016-08-23 03:59:15 +02:00
}
2016-05-23 00:23:16 +02:00
}
2016-06-21 22:18:32 +02:00
MetamaskInpageProvider . prototype . send = function ( payload ) {
2016-05-23 00:23:16 +02:00
const self = this
2016-10-05 20:10:39 +02:00
2016-06-21 22:56:04 +02:00
let selectedAddress
2016-08-11 22:31:00 +02:00
let result = null
2016-05-23 00:23:16 +02:00
switch ( payload . method ) {
case 'eth_accounts' :
// read from localStorage
2016-06-21 22:56:04 +02:00
selectedAddress = self . publicConfigStore . get ( 'selectedAddress' )
2016-05-23 00:23:16 +02:00
result = selectedAddress ? [ selectedAddress ] : [ ]
break
case 'eth_coinbase' :
// read from localStorage
2016-06-21 22:56:04 +02:00
selectedAddress = self . publicConfigStore . get ( 'selectedAddress' )
2016-05-23 00:23:16 +02:00
result = selectedAddress || '0x0000000000000000000000000000000000000000'
break
2016-08-11 22:23:49 +02:00
// throw not-supported Error
2016-05-23 00:23:16 +02:00
default :
2016-10-05 20:10:39 +02:00
var link = 'https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#dizzy-all-async---think-of-metamask-as-a-light-client'
2016-10-20 00:16:27 +02:00
var message = ` The MetaMask Web3 object does not support synchronous methods like ${ payload . method } without a callback parameter. See ${ link } for details. `
2016-08-11 22:23:49 +02:00
throw new Error ( message )
2016-05-23 00:23:16 +02:00
}
// return the result
return {
id : payload . id ,
jsonrpc : payload . jsonrpc ,
result : result ,
}
}
2016-06-21 22:18:32 +02:00
MetamaskInpageProvider . prototype . sendAsync = function ( ) {
2016-05-23 00:23:16 +02:00
throw new Error ( 'MetamaskInpageProvider - sendAsync not overwritten' )
}
2016-06-21 22:18:32 +02:00
MetamaskInpageProvider . prototype . isConnected = function ( ) {
2016-05-23 00:23:16 +02:00
return true
}
// util
2016-06-21 22:18:32 +02:00
function remoteStoreWithLocalStorageCache ( storageKey ) {
2016-05-23 00:23:16 +02:00
// read local cache
var initState = JSON . parse ( localStorage [ storageKey ] || '{}' )
var store = new RemoteStore ( initState )
// cache the latest state locally
2016-06-21 22:18:32 +02:00
store . subscribe ( function ( state ) {
2016-05-23 00:23:16 +02:00
localStorage [ storageKey ] = JSON . stringify ( state )
} )
return store
2016-06-21 22:18:32 +02:00
}
2016-08-23 03:59:15 +02:00
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
}
2016-09-01 00:40:05 +02:00
function eachJsonMessage ( payload , transformFn ) {
2016-08-23 03:59:15 +02:00
if ( Array . isArray ( payload ) ) {
return payload . map ( transformFn )
} else {
return transformFn ( payload )
}
2016-09-06 05:20:22 +02:00
}