forked from MetaMask/providers
-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
174 lines (140 loc) · 5.29 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
const pump = require('pump')
const RpcEngine = require('json-rpc-engine')
const createErrorMiddleware = require('./createErrorMiddleware')
const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware')
const createJsonRpcStream = require('json-rpc-middleware-stream')
const LocalStorageStore = require('obs-store')
const asStream = require('obs-store/lib/asStream')
const ObjectMultiplex = require('obj-multiplex')
const util = require('util')
const SafeEventEmitter = require('safe-event-emitter')
module.exports = MetamaskInpageProvider
util.inherits(MetamaskInpageProvider, SafeEventEmitter)
function MetamaskInpageProvider (connectionStream) {
const self = this
self.selectedAddress = undefined
self.networkVersion = undefined
// super constructor
SafeEventEmitter.call(self)
// setup connectionStream multiplexing
const mux = self.mux = new ObjectMultiplex()
pump(
connectionStream,
mux,
connectionStream,
logStreamDisconnectWarning.bind(this, 'MetaMask')
)
// subscribe to metamask public config (one-way)
self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' })
// Emit events for some state changes
self.publicConfigStore.subscribe(function (state) {
// Emit accountsChanged event on account change
if ('selectedAddress' in state && state.selectedAddress !== self.selectedAddress) {
self.selectedAddress = state.selectedAddress
self.emit('accountsChanged', [self.selectedAddress])
}
// Emit networkChanged event on network change
if ('networkVersion' in state && state.networkVersion !== self.networkVersion) {
self.networkVersion = state.networkVersion
self.emit('networkChanged', state.networkVersion)
}
})
pump(
mux.createStream('publicConfig'),
asStream(self.publicConfigStore),
logStreamDisconnectWarning.bind(this, 'MetaMask PublicConfigStore')
)
// ignore phishing warning message (handled elsewhere)
mux.ignoreStream('phishing')
// connect to async provider
const jsonRpcConnection = createJsonRpcStream()
pump(
jsonRpcConnection.stream,
mux.createStream('provider'),
jsonRpcConnection.stream,
logStreamDisconnectWarning.bind(this, 'MetaMask RpcProvider')
)
// handle sendAsync requests via dapp-side rpc engine
const rpcEngine = new RpcEngine()
rpcEngine.push(createIdRemapMiddleware())
rpcEngine.push(createErrorMiddleware())
rpcEngine.push(jsonRpcConnection.middleware)
self.rpcEngine = rpcEngine
// forward json rpc notifications
jsonRpcConnection.events.on('notification', function(payload) {
self.emit('data', null, payload)
})
// Work around for https://github.com/metamask/metamask-extension/issues/5459
// drizzle accidently breaking the `this` reference
self.send = self.send.bind(self)
self.sendAsync = self.sendAsync.bind(self)
}
// Web3 1.0 provider uses `send` with a callback for async queries
MetamaskInpageProvider.prototype.send = function (payload, callback) {
const self = this
if (callback) {
self.sendAsync(payload, callback)
} else {
return self._sendSync(payload)
}
}
// handle sendAsync requests via asyncProvider
// also remap ids inbound and outbound
MetamaskInpageProvider.prototype.sendAsync = function (payload, cb) {
const self = this
if (payload.method === 'eth_signTypedData') {
console.warn('MetaMask: This experimental version of eth_signTypedData will be deprecated in the next release in favor of the standard as defined in EIP-712. See https://git.io/fNzPl for more information on the new standard.')
}
self.rpcEngine.handle(payload, cb)
}
MetamaskInpageProvider.prototype._sendSync = function (payload) {
const self = this
let selectedAddress
let result = null
switch (payload.method) {
case 'eth_accounts':
// read from localStorage
selectedAddress = self.publicConfigStore.getState().selectedAddress
result = selectedAddress ? [selectedAddress] : []
break
case 'eth_coinbase':
// read from localStorage
selectedAddress = self.publicConfigStore.getState().selectedAddress
result = selectedAddress || null
break
case 'eth_uninstallFilter':
self.sendAsync(payload, noop)
result = true
break
case 'net_version':
const networkVersion = self.publicConfigStore.getState().networkVersion
result = networkVersion || null
break
// throw not-supported Error
default:
var link = 'https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#dizzy-all-async---think-of-metamask-as-a-light-client'
var message = `The MetaMask Web3 object does not support synchronous methods like ${payload.method} without a callback parameter. See ${link} for details.`
throw new Error(message)
}
// return the result
return {
id: payload.id,
jsonrpc: payload.jsonrpc,
result: result,
}
}
MetamaskInpageProvider.prototype.isConnected = function () {
return true
}
MetamaskInpageProvider.prototype.isMetaMask = true
// util
function logStreamDisconnectWarning (remoteLabel, err) {
let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}`
if (err) warningMsg += '\n' + err.stack
console.warn(warningMsg)
const listeners = this.listenerCount('error')
if (listeners > 0) {
this.emit('error', warningMsg)
}
}
function noop () {}