With the creation of the [metamask-extension-provider](https://github.com/MetaMask/metamask-extension-provider) we have our first non-core module that is dependent on the inpage-provider and port-stream. To reduce the size of its dependencies, I have moved the [metamask-inpage-provider](https://github.com/MetaMask/metamask-inpage-provider) into its own module, as well as [extension-port-stream](https://github.com/MetaMask/extension-port-stream). This allows them to be more easily depended & iterated on by external projects.feature/default_network_editable
parent
887cad973f
commit
6ce119d1fb
@ -1,67 +0,0 @@ |
|||||||
const log = require('loglevel') |
|
||||||
|
|
||||||
/** |
|
||||||
* JSON-RPC error object |
|
||||||
* |
|
||||||
* @typedef {Object} RpcError |
|
||||||
* @property {number} code - Indicates the error type that occurred |
|
||||||
* @property {Object} [data] - Contains additional information about the error |
|
||||||
* @property {string} [message] - Short description of the error |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
* Middleware configuration object |
|
||||||
* |
|
||||||
* @typedef {Object} MiddlewareConfig |
|
||||||
* @property {boolean} [override] - Use RPC_ERRORS message in place of provider message |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
* Map of standard and non-standard RPC error codes to messages |
|
||||||
*/ |
|
||||||
const RPC_ERRORS = { |
|
||||||
1: 'An unauthorized action was attempted.', |
|
||||||
2: 'A disallowed action was attempted.', |
|
||||||
3: 'An execution error occurred.', |
|
||||||
[-32600]: 'The JSON sent is not a valid Request object.', |
|
||||||
[-32601]: 'The method does not exist / is not available.', |
|
||||||
[-32602]: 'Invalid method parameter(s).', |
|
||||||
[-32603]: 'Internal JSON-RPC error.', |
|
||||||
[-32700]: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.', |
|
||||||
internal: 'Internal server error.', |
|
||||||
unknown: 'Unknown JSON-RPC error.', |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Modifies a JSON-RPC error object in-place to add a human-readable message, |
|
||||||
* optionally overriding any provider-supplied message |
|
||||||
* |
|
||||||
* @param {RpcError} error - JSON-RPC error object |
|
||||||
* @param {boolean} override - Use RPC_ERRORS message in place of provider message |
|
||||||
*/ |
|
||||||
function sanitizeRPCError (error, override) { |
|
||||||
if (error.message && !override) { return error } |
|
||||||
const message = error.code > -31099 && error.code < -32100 ? RPC_ERRORS.internal : RPC_ERRORS[error.code] |
|
||||||
error.message = message || RPC_ERRORS.unknown |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* json-rpc-engine middleware that both logs standard and non-standard error |
|
||||||
* messages and ends middleware stack traversal if an error is encountered |
|
||||||
* |
|
||||||
* @param {MiddlewareConfig} [config={override:true}] - Middleware configuration |
|
||||||
* @returns {Function} json-rpc-engine middleware function |
|
||||||
*/ |
|
||||||
function createErrorMiddleware ({ override = true } = {}) { |
|
||||||
return (req, res, next) => { |
|
||||||
next(done => { |
|
||||||
const { error } = res |
|
||||||
if (!error) { return done() } |
|
||||||
sanitizeRPCError(error) |
|
||||||
log.error(`MetaMask - RPC Error: ${error.message}`, error) |
|
||||||
done() |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = createErrorMiddleware |
|
@ -1,125 +0,0 @@ |
|||||||
const pump = require('pump') |
|
||||||
const RpcEngine = require('json-rpc-engine') |
|
||||||
const createErrorMiddleware = require('./createErrorMiddleware') |
|
||||||
const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware') |
|
||||||
const createStreamMiddleware = require('json-rpc-middleware-stream') |
|
||||||
const LocalStorageStore = require('obs-store') |
|
||||||
const asStream = require('obs-store/lib/asStream') |
|
||||||
const ObjectMultiplex = require('obj-multiplex') |
|
||||||
|
|
||||||
module.exports = MetamaskInpageProvider |
|
||||||
|
|
||||||
function MetamaskInpageProvider (connectionStream) { |
|
||||||
const self = this |
|
||||||
|
|
||||||
// setup connectionStream multiplexing
|
|
||||||
const mux = self.mux = new ObjectMultiplex() |
|
||||||
pump( |
|
||||||
connectionStream, |
|
||||||
mux, |
|
||||||
connectionStream, |
|
||||||
(err) => logStreamDisconnectWarning('MetaMask', err) |
|
||||||
) |
|
||||||
|
|
||||||
// subscribe to metamask public config (one-way)
|
|
||||||
self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) |
|
||||||
|
|
||||||
pump( |
|
||||||
mux.createStream('publicConfig'), |
|
||||||
asStream(self.publicConfigStore), |
|
||||||
(err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) |
|
||||||
) |
|
||||||
|
|
||||||
// ignore phishing warning message (handled elsewhere)
|
|
||||||
mux.ignoreStream('phishing') |
|
||||||
|
|
||||||
// connect to async provider
|
|
||||||
const streamMiddleware = createStreamMiddleware() |
|
||||||
pump( |
|
||||||
streamMiddleware.stream, |
|
||||||
mux.createStream('provider'), |
|
||||||
streamMiddleware.stream, |
|
||||||
(err) => logStreamDisconnectWarning('MetaMask RpcProvider', err) |
|
||||||
) |
|
||||||
|
|
||||||
// handle sendAsync requests via dapp-side rpc engine
|
|
||||||
const rpcEngine = new RpcEngine() |
|
||||||
rpcEngine.push(createIdRemapMiddleware()) |
|
||||||
rpcEngine.push(createErrorMiddleware()) |
|
||||||
rpcEngine.push(streamMiddleware) |
|
||||||
self.rpcEngine = rpcEngine |
|
||||||
} |
|
||||||
|
|
||||||
// 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.send = 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) |
|
||||||
} |
|
||||||
|
|
||||||
function noop () {} |
|
@ -1,80 +0,0 @@ |
|||||||
const Duplex = require('readable-stream').Duplex |
|
||||||
const inherits = require('util').inherits |
|
||||||
const noop = function () {} |
|
||||||
|
|
||||||
module.exports = PortDuplexStream |
|
||||||
|
|
||||||
inherits(PortDuplexStream, Duplex) |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a stream that's both readable and writable. |
|
||||||
* The stream supports arbitrary objects. |
|
||||||
* |
|
||||||
* @class |
|
||||||
* @param {Object} port Remote Port object |
|
||||||
*/ |
|
||||||
function PortDuplexStream (port) { |
|
||||||
Duplex.call(this, { |
|
||||||
objectMode: true, |
|
||||||
}) |
|
||||||
this._port = port |
|
||||||
port.onMessage.addListener(this._onMessage.bind(this)) |
|
||||||
port.onDisconnect.addListener(this._onDisconnect.bind(this)) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Callback triggered when a message is received from |
|
||||||
* the remote Port associated with this Stream. |
|
||||||
* |
|
||||||
* @private |
|
||||||
* @param {Object} msg - Payload from the onMessage listener of Port |
|
||||||
*/ |
|
||||||
PortDuplexStream.prototype._onMessage = function (msg) { |
|
||||||
if (Buffer.isBuffer(msg)) { |
|
||||||
delete msg._isBuffer |
|
||||||
var data = new Buffer(msg) |
|
||||||
this.push(data) |
|
||||||
} else { |
|
||||||
this.push(msg) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Callback triggered when the remote Port |
|
||||||
* associated with this Stream disconnects. |
|
||||||
* |
|
||||||
* @private |
|
||||||
*/ |
|
||||||
PortDuplexStream.prototype._onDisconnect = function () { |
|
||||||
this.destroy() |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Explicitly sets read operations to a no-op |
|
||||||
*/ |
|
||||||
PortDuplexStream.prototype._read = noop |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Called internally when data should be written to |
|
||||||
* this writable stream. |
|
||||||
* |
|
||||||
* @private |
|
||||||
* @param {*} msg Arbitrary object to write |
|
||||||
* @param {string} encoding Encoding to use when writing payload |
|
||||||
* @param {Function} cb Called when writing is complete or an error occurs |
|
||||||
*/ |
|
||||||
PortDuplexStream.prototype._write = function (msg, encoding, cb) { |
|
||||||
try { |
|
||||||
if (Buffer.isBuffer(msg)) { |
|
||||||
var data = msg.toJSON() |
|
||||||
data._isBuffer = true |
|
||||||
this._port.postMessage(data) |
|
||||||
} else { |
|
||||||
this._port.postMessage(msg) |
|
||||||
} |
|
||||||
} catch (err) { |
|
||||||
return cb(new Error('PortDuplexStream - disconnected')) |
|
||||||
} |
|
||||||
cb() |
|
||||||
} |
|
Loading…
Reference in new issue