MetaMask/metamask-inpage-provider

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

Used to initialize the inpage ethereum provider injected by MetaMask.git

Installation

npm install metamask-inpage-provider -S

 

metamask-inpage-provider/createErrorMiddleware.jsgithub

處理JSON-RPC調用是出現的錯誤,並將錯誤記錄到日誌中web

loglevel是JavaScript的最小輕量級日誌記錄,它沒有控制檯的缺點,替代了console.log()而且對於基於級別的日誌和過濾是十分友好的,本博客loglevel-metamasknpm

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.jsjson

這個庫的做用有設置瀏覽器本地信息存儲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 += '\n' + 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)
本站公眾號
   歡迎關注本站公眾號,獲取更多信息