MetaMask/metamask-inpage-provider

Posted wanghui-garcia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MetaMask/metamask-inpage-provider相关的知识,希望对你有一定的参考价值。

https://github.com/MetaMask/metamask-inpage-provider

Used to initialize the inpage ethereum provider injected by MetaMask.

Installation

npm install metamask-inpage-provider -S

 

metamask-inpage-provider/createErrorMiddleware.js

处理JSON-RPC调用是出现的错误,并将错误记录到日志中

loglevel是javascript的最小轻量级日志记录,它没有控制台的缺点,替代了console.log()并且对于基于级别的日志和过滤是十分友好的,本博客loglevel-metamask

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]//判断错误类型并获得错误message
  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

 

metamask-inpage-provider/index.js

这个库的作用有设置浏览器本地信息存储publicConfig流、构建RpcProvider双向传送json rpc及其结果流,以及对从浏览器中得到的json rpc进行处理,send/sendsync的处理是不同的,然后将他们根据得到的特殊id存储到数组中,等待传送到区块链进行处理

const pump = require(pump)//看本博客pump模块的学习-metamask
const RpcEngine = require(json-rpc-engine)//看本博客MetaMask/json-rpc-engine
const createErrorMiddleware = require(./createErrorMiddleware)//检查json rpc产生的错误
const createIdRemapMiddleware = require(json-rpc-engine/src/idRemapMiddleware)创建一个独特的id来标识json rpc,不然它的id很多都是相同的1,2等,看本博客MetaMask/json-rpc-engine
const createJsonRpcStream = require(json-rpc-middleware-stream)//看本博客的MetaMask/json-rpc-middleware-stream
const LocalStorageStore = require(obs-store)//看本博客MetaMask/obs-store
const asStream = require(obs-store/lib/asStream)//看本博客MetaMask/obs-store
const ObjectMultiplex = require(obj-multiplex)//看博客kumavis/obj-multiplex
const util = require(util)
const SafeEventEmitter = require(safe-event-emitter)//看博客MetaMask/safe-event-emitter

module.exports = MetamaskInpageProvider

util.inherits(MetamaskInpageProvider, SafeEventEmitter)

function MetamaskInpageProvider (connectionStream) {
  const self = this

  // 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 })//定义了一个在浏览器中storageKey为MetaMask-Config的本地存储,用于存储一些区块链的账户信息,网络信息

  pump(//进行浏览器本地信息存储
    mux.createStream(publicConfig),//创建name为publicConfig的子流
    asStream(self.publicConfigStore),
    logStreamDisconnectWarning.bind(this, MetaMask PublicConfigStore)//logStreamDisconnectWarning函数下面有定义,MetaMask PublicConfigStore‘为error说明,
  )

  // ignore phishing warning message (handled elsewhere)
  mux.ignoreStream(phishing)//这里不对name为phishing的流进行处理

  // connect to async provider
  const jsonRpcConnection = createJsonRpcStream()//
  pump(//构建provider,接受浏览器传来的json rpc,并响应
    jsonRpcConnection.stream,
    mux.createStream(provider),
    jsonRpcConnection.stream,
    logStreamDisconnectWarning.bind(this, MetaMask RpcProvider)
  )

  // handle sendAsync requests via dapp-side rpc engine
  const rpcEngine = new RpcEngine() 看了本博客MetaMask/json-rpc-engine后,可以知道这是push进将要对接受到的json rpc调用的处理
  rpcEngine.push(createIdRemapMiddleware())//获得个unique id拿来使用
  rpcEngine.push(createErrorMiddleware()) //进行错误的处理
  rpcEngine.push(jsonRpcConnection.middleware) //将json rpc 请求写入createJsonRpcStream双工流中
  self.rpcEngine = rpcEngine

  // forward json rpc notifications
  jsonRpcConnection.events.on(notification, function(payload) {//设置json-rpc-middleware-stream流相应的notification‘事件
    self.emit(data, null, payload) //触发data事件
  })

  // 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) {//web3调用send()
  const self = this

  if (callback) {//如果是异步带回调的,就调用sendAsync
    self.sendAsync(payload, callback)
  } else {//否则就调用_sendSync
    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)//该json rpc就会被处理
}

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) //设置回调为{}再调用sendAsync
      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 += 
 + err.stack
  console.warn(warningMsg)
  const listeners = this.listenerCount(error)
  if (listeners > 0) {
    this.emit(error, warningMsg)
  }
}

function noop () {}

 

Usage

MetamaskInpageProvider的使用:
// Create a stream to a remote provider:
var metamaskStream = new LocalMessageDuplexStream({
  name: inpage,
  target: contentscript,
})

// compose the inpage provider
var inpageProvider = new MetamaskInpageProvider(metamaskStream)

 

 

以上是关于MetaMask/metamask-inpage-provider的主要内容,如果未能解决你的问题,请参考以下文章