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