区块链钱包开发(五)—— 构建json RPC框架

一、框架概览

MetaMask JSON-RPC 框架是处理请求的基础组件。它主要由以下几个核心仓库(repo)组成:

其中,json-rpc-engine 是整个体系的核心,其他库大多围绕它进行扩展和集成。

总体架构图:

flowchart TB %% 定义子图 subgraph AppLayer["应用层"] DApp["DApp/网页应用"] end subgraph ProviderLayer["Provider层"] Provider["SafeEventEmitterProvider"] ProviderMiddleware["providerFromMiddleware"] end subgraph EngineLayer["引擎层"] Engine["JsonRpcEngine"] ScaffoldMiddleware["createScaffoldMiddleware"] end subgraph CommLayer["通信层"] StreamMiddleware["StreamMiddleware"] EngineStream["EngineStream"] end subgraph MiddlewareLayer["中间件层"] PermissionMiddleware["权限中间件"] QueueMiddleware["队列中间件"] WalletMiddleware["钱包中间件"] CacheMiddleware["缓存中间件"] FilterMiddleware["过滤器中间件"] SubscriptionMiddleware["订阅中间件"] FetchMiddleware["Fetch中间件"] end subgraph RPCLayer["RPC服务层"] RpcServiceChain["RpcServiceChain"] PrimaryService["RPC Service (主要)"] BackupService["RPC Service (备用)"] end subgraph NetworkLayer["网络层"] HttpRequest["HTTP请求"] end %% 连接线 DApp <--> Provider Provider <--> PermissionMiddleware Provider <--> ProviderMiddleware ProviderMiddleware <--> Engine %% 中间件层内部连接 PermissionMiddleware --> QueueMiddleware QueueMiddleware --> WalletMiddleware WalletMiddleware --> CacheMiddleware CacheMiddleware --> FilterMiddleware FilterMiddleware --> SubscriptionMiddleware SubscriptionMiddleware --> FetchMiddleware %% 通信层连接 Engine <--> StreamMiddleware StreamMiddleware <--> EngineStream %% 引擎与中间件关系 Engine <--> MiddlewareLayer Engine <--> ScaffoldMiddleware %% RPC服务层连接 FetchMiddleware <--> RpcServiceChain RpcServiceChain --> PrimaryService RpcServiceChain --> BackupService %% 网络层连接 PrimaryService <--> HttpRequest BackupService <--> HttpRequest %% 风格设置 classDef appStyle fill:#e1bee7,stroke:#9c27b0,stroke-width:2px; classDef providerStyle fill:#bbdefb,stroke:#1976d2,stroke-width:2px; classDef engineStyle fill:#c8e6c9,stroke:#388e3c,stroke-width:2px; classDef middlewareStyle fill:#ffe0b2,stroke:#f57c00,stroke-width:2px; classDef commStyle fill:#b2dfdb,stroke:#00796b,stroke-width:2px; classDef rpcStyle fill:#ffcdd2,stroke:#d32f2f,stroke-width:2px; classDef networkStyle fill:#d1c4e9,stroke:#512da8,stroke-width:2px; class AppLayer appStyle; class ProviderLayer providerStyle; class EngineLayer engineStyle; class MiddlewareLayer middlewareStyle; class CommLayer commStyle; class RPCLayer rpcStyle; class NetworkLayer networkStyle; %% 添加子图说明 AppLayer --> |"DApp通过window.ethereum API调用"| ProviderLayer ProviderLayer --> |"格式化JSON-RPC请求"| EngineLayer EngineLayer --> |"通过中间件链处理"| MiddlewareLayer EngineLayer <--> |"跨上下文通信"| CommLayer MiddlewareLayer --> |"远程请求"| RPCLayer RPCLayer --> |"HTTP调用"| NetworkLayer

二、json-rpc-engine 详解

源码地址:github.com/MetaMask/co...

1. 设计理念

json-rpc-engine 借鉴了 Koa/Express 等 Web 框架的中间件(middleware)机制,将 JSON-RPC 请求的处理流程拆解为一系列可插拔的中间件,每个中间件只关注自己的职责,极大提升了可维护性和可扩展性。

2. 基本用法

创建引擎

javascript 复制代码
const { JsonRpcEngine } = require('@metamask/json-rpc-engine');
const engine = new JsonRpcEngine();

添加中间件

javascript 复制代码
engine.push((req, res, next, end) => {
  // 处理请求
  if (req.method === 'hello') {
    res.result = 'world';
    return end();
  }
  next();
});

发送请求

javascript 复制代码
engine.handle({ id: 1, jsonrpc: '2.0', method: 'hello' }, (err, res) => {
  console.log(res.result); // 输出 'world'
});

3. 中间件机制

每个中间件函数签名如下:

javascript 复制代码
function middleware(req, res, next, end) {
  // req: 请求对象
  // res: 响应对象
  // next: 进入下一个中间件
  // end: 终止链式调用(可传递错误)
}
  • next():继续传递到下一个中间件。
  • end():终止处理流程,返回响应。
  • 异步支持 :支持 async/await,允许中间件异步处理。

示例:日志中间件

javascript 复制代码
engine.push((req, res, next, end) => {
  console.log('请求方法:', req.method);
  next();
});

4. 典型应用场景

  • 权限控制:如只允许白名单方法通过。
  • 缓存:如对某些请求结果做缓存。
  • 签名/账户管理 :如拦截 eth_signeth_accounts 等方法。
  • 网络代理:如将请求转发到远程节点。

5. 核心源码解读

核心方法是 #runMiddleware,用于执行中间件方法。

方法签名

typescript 复制代码
static async #runMiddleware(
  request: JsonRpcRequest,
  response: PendingJsonRpcResponse<Json>,
  middleware: JsonRpcMiddleware<JsonRpcParams, Json>,
  returnHandlers: JsonRpcEngineReturnHandler[],
): Promise<[unknown, boolean]>
  • request:当前的 JSON-RPC 请求对象。
  • response:当前的响应对象。
  • middleware:要执行的中间件函数。
  • returnHandlers:用于收集 return handler 的数组。
  • 返回值 :Promise,resolve 时返回 [error, isComplete],即本中间件是否终结了请求。

主要流程

Promise 封装

整个方法用 Promise 封装,便于异步链式调用和错误捕获。


end 回调

typescript 复制代码
const end: JsonRpcEngineEndCallback = (error) => {
  const parsedError = error || response.error;
  if (parsedError) {
    response.error = serializeError(parsedError);
  }
  // True indicates that the request should end
  resolve([parsedError, true]);
};
  • 作用 :中间件调用 end() 时触发,表示本次请求处理终止。
  • 行为
    • 如果有错误,序列化并写入 response.error
    • resolve Promise,返回 [error, true]true 表示请求已终结。

next 回调

typescript 复制代码
const next: JsonRpcEngineNextCallback = (
  returnHandler?: JsonRpcEngineReturnHandler,
) => {
  if (response.error) {
    end(response.error);
  } else {
    if (returnHandler) {
      if (typeof returnHandler !== 'function') {
        end(
          new JsonRpcError(
            errorCodes.rpc.internal,
            `JsonRpcEngine: "next" return handlers must be functions. ` +
              `Received "${typeof returnHandler}" for request:\n${jsonify(
                request,
              )}`,
            { request },
          ),
        );
      }
      returnHandlers.push(returnHandler);
    }
    // False indicates that the request should not end
    resolve([null, false]);
  }
};
  • 作用 :中间件调用 next() 时触发,表示继续传递到下一个中间件。
  • 行为
    • 如果 response 已有 error,直接调用 end
    • 如果传入了 returnHandler,类型检查后加入 returnHandlers 数组。
    • resolve Promise,返回 [null, false]false 表示请求未终结,还需继续后续中间件。

执行中间件

typescript 复制代码
try {
  middleware(request, response, next, end);
} catch (error) {
  end(error);
}
  • 作用:实际调用传入的中间件函数。
  • 行为
    • 传入 requestresponsenextend 四个参数。
    • 如果中间件抛出异常,捕获并调用 end

例子

javascript 复制代码
engine.push((req, res, next, end) => {
  // 前置逻辑
  next(async () => {
    // 后置逻辑
  });
});
  • 当中间件调用 next(returnHandler) 时,returnHandler 被收集到 returnHandlers
  • 当所有中间件正向执行完毕后,engine 会逆序执行 returnHandlers

6. 其他工具方法

createAsyncMiddleware

用于将基于 async/await 的异步中间件包装成 engine 能识别的"回调风格"中间件。

javascript 复制代码
engine.push(createAsyncMiddleware(async (req, res, next) => {
  // 前置逻辑
  await next();
  // 后置逻辑
}));

等价于 callback 风格:

javascript 复制代码
engine.push((req, res, next, end) => {
  // 前置逻辑
  next(() => {
    // 后置逻辑
  });
});

mergeMiddleware

将多个中间件合并成一个中间件,便于批量插入或动态组合。

javascript 复制代码
const { mergeMiddleware } = require('@metamask/json-rpc-engine');

const middlewareA = (req, res, next, end) => {
  if (req.method === 'foo') {
    res.result = 'bar';
    return end();
  }
  next();
};

const middlewareB = (req, res, next, end) => {
  if (req.method === 'hello') {
    res.result = 'world';
    return end();
  }
  next();
};

// 合并为一个中间件
const combined = mergeMiddleware([middlewareA, middlewareB]);

engine.push(combined);

foo 方法会被 middlewareA 处理,hello 方法会被 middlewareB 处理,其他请求继续传递。


asMiddleware

将一个 JsonRpcEngine 实例包装成一个中间件,可以嵌套引擎,实现模块化拆分。

javascript 复制代码
const { JsonRpcEngine } = require('@metamask/json-rpc-engine');

// 子引擎
const subEngine = new JsonRpcEngine();
subEngine.push((req, res, next, end) => {
  if (req.method === 'sub_method') {
    res.result = 'from subEngine';
    return end();
  }
  next();
});

// 主引擎
const mainEngine = new JsonRpcEngine();
mainEngine.push(subEngine.asMiddleware()); // 嵌套子引擎
mainEngine.push((req, res, next, end) => {
  res.result = 'from mainEngine';
  end();
});

// 测试
mainEngine.handle({ id: 1, jsonrpc: '2.0', method: 'sub_method' }, (err, res) => {
  console.log(res.result); // 输出 'from subEngine'
});

sub_methodsubEngine 处理,其他方法由 mainEngine 处理。


createScaffoldMiddleware

用于根据传入的 method-handler 映射对象,快速生成一个支持静态返回或自定义处理函数的 JSON-RPC 中间件。

源码实现

javascript 复制代码
export function createScaffoldMiddleware(handlers: {
  [methodName: string]: ScaffoldMiddlewareHandler<JsonRpcParams, Json>;
}): JsonRpcMiddleware<JsonRpcParams, Json> {
  return (req, res, next, end) => {
    const handler = handlers[req.method];
    // if no handler, return
    if (handler === undefined) {
      return next();
    }

    // if handler is fn, call as middleware
    if (typeof handler === 'function') {
      return handler(req, res, next, end);
    }
    // if handler is some other value, use as result
    (res as JsonRpcSuccess<Json>).result = handler;
    return end();
  };
}

用法

javascript 复制代码
const { createScaffoldMiddleware } = require('@metamask/json-rpc-engine');

const scaffold = createScaffoldMiddleware({
  eth_chainId: '0x1',
  net_version: '1',
  web3_clientVersion: async () => 'MyClient/v1.0.0',
});

engine.push(scaffold);

// 测试
engine.handle({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }, (err, res) => {
  console.log(res.result); // 输出 '0x1'
});
engine.handle({ id: 2, jsonrpc: '2.0', method: 'web3_clientVersion' }, (err, res) => {
  console.log(res.result); // 输出 'MyClient/v1.0.0'
});

三、其他 RPC 组件

1. eth-json-rpc-provider

源码:github.com/MetaMask/co...

eth-json-rpc-provider 提供了将 enginemiddleware 封装为标准以太坊 Provider 的能力。JsonRpcEngine 里最后一个 middleware 通常就是把 eth-json-rpc-provider(链上请求执行者)包装成中间件,最终通过 request 方法把请求发到链上。

request 实现

javascript 复制代码
async request<Params extends JsonRpcParams, Result extends Json>(
  eip1193Request: Eip1193Request<Params>,
): Promise<Result> {
  const jsonRpcRequest =
    convertEip1193RequestToJsonRpcRequest(eip1193Request);
  const response = await this.#engine.handle<
    Params | Record<never, never>,
    Result
  >(jsonRpcRequest);

  if ('result' in response) {
    return response.result;
  }

  const error = new JsonRpcError(
    response.error.code,
    response.error.message,
    response.error.data,
  );
  if ('stack' in response.error) {
    error.stack = response.error.stack;
  }
  throw error;
}

2. json-rpc-middleware-stream

源码:github.com/MetaMask/co...

主要有两个核心方法:

createEngineStream

JsonRpcEngine 包装成 stream 形式,兼容流式通信架构。

javascript 复制代码
export default function createEngineStream(opts: JsonRpcEngine): Duplex {
  if (!opts?.engine) {
    throw new Error('Missing engine parameter!');
  }

  const { engine } = opts;
  const stream = new Duplex({ objectMode: true, read: () => undefined, write });
  // 转发 engine 的 notification 事件
  if (engine.on) {
    engine.on('notification', (message) => {
      stream.push(message);
    });
  }
  return stream;

  function write(
    req: JsonRpcRequest,
    _encoding: unknown,
    streamWriteCallback: (error?: Error | null) => void,
  ) {
    // 处理请求并将请求传递给下游 stream
    engine.handle(req, (_err, res) => {
      stream.push(res);
    });
    streamWriteCallback();
  }
}

createStreamMiddleware

createStreamMiddleware 的作用是在 JSON-RPC Engine 和底层流式通信通道(如 MessagePort、WebSocket、内容脚本与后台脚本之间的 stream)之间,充当桥接中间件,实现请求和通知的双向流转。

详细说明

  • 当网页中注入的 provider(如 window.ethereum)发起 JSON-RPC 请求时,这些请求会通过 engine 传递到 createStreamMiddleware 生成的 middleware。
  • createStreamMiddleware 会将收到的请求对象写入下游的 stream(如内容脚本与后台的连接流),实现请求的跨上下文转发。
  • 当下游 stream(如后台脚本)处理完请求并返回响应时,createStreamMiddleware 会自动将响应回填到原始请求的响应对象,并调用 end(),完成一次完整的请求-响应闭环。
  • 此外,当下游 stream 推送通知(如账户变更、链变更、订阅事件等)时,createStreamMiddleware 会自动触发自身的 notification 事件。开发者可以监听该事件,及时处理链上状态变更或推送消息。
  • 该中间件还支持重试机制(如 retryOnMessage),在特定消息下自动重发未完成的请求,提升健壮性。

典型流程

  • provider.requestenginecreateStreamMiddleware.middleware → 下游 stream → 下游 stream 响应 → createStreamMiddleware 识别响应 → 回填到原始请求。
  • 下游 stream 推送通知 → createStreamMiddleware 触发 notification 事件 → provider 监听并处理。

代码场景举例

javascript 复制代码
// 1. 创建中间件和 stream
const { middleware, stream, events } = createStreamMiddleware({ retryOnMessage: 'SOME_RETRY_SIGNAL' });

// 2. 连接上下游 stream
pipeline(connectionStream, stream, connectionStream, ...);

// 3. 将中间件添加到 engine
engine.push(middleware);

// 4. 监听 notification 事件
events.on('notification', (payload) => {
  // 处理链变更、账户变更等通知
});

源码

javascript 复制代码
export default function createStreamMiddleware(options: Options = {}) {
  const idMap: IdMap = {}; // 请求 id 到上下文的映射
  const stream = new Duplex({
    objectMode: true,
    read: () => undefined,
    write: processMessage,
  });

  const events = new SafeEventEmitter();

  // 核心中间件:将请求写入流,等待响应
  const middleware: JsonRpcMiddleware<JsonRpcParams, JsonRpcParams> = (
    req,
    res,
    next,
    end,
  ) => {
    // 先注册请求上下文,防止响应先到
    idMap[req.id as unknown as string] = { req, res, next, end };
    sendToStream(req);
  };

  return { events, middleware, stream };

  // 发送请求到下游流
  function sendToStream(req: JsonRpcRequest) {
    stream.push(req);
  }

  // 处理流上收到的消息(响应或通知)
  function processMessage(
    res: PendingJsonRpcResponse<JsonRpcParams>,
    _encoding: unknown,
    streamWriteCallback: (error?: Error | null) => void,
  ) {
    let errorObj: Error | null = null;
    try {
      const isNotification = !hasProperty(res, 'id');
      if (isNotification) {
        processNotification(res as unknown as JsonRpcNotification);
      } else {
        processResponse(res);
      }
    } catch (_err) {
      errorObj = _err as Error;
    }
    streamWriteCallback(errorObj);
  }

  // 处理 JSON-RPC 响应,回填到原始请求上下文
  function processResponse(res: PendingJsonRpcResponse<JsonRpcParams>) {
    const { id: responseId } = res;
    if (responseId === null) {
      return;
    }
    const context = idMap[responseId];
    if (!context) {
      console.warn(`StreamMiddleware - Unknown response id "${responseId}"`);
      return;
    }
    delete idMap[responseId];
    Object.assign(context.res, res); // 响应内容写回原始 res
    setTimeout(context.end); // 异步调用 end,完成请求
  }

  // 处理 JSON-RPC 通知,转发为事件
  function processNotification(notif: JsonRpcNotification) {
    if (options?.retryOnMessage && notif.method === options.retryOnMessage) {
      retryStuckRequests();
    }
    events.emit('notification', notif); // 通知事件转发
  }

  // 重试所有未完成的请求
  function retryStuckRequests() {
    Object.values(idMap).forEach(({ req, retryCount = 0 }) => {
      if (!req.id) {
        return;
      }
      if (retryCount >= 3) {
        throw new Error(
          `StreamMiddleware - Retry limit exceeded for request id "${req.id}"`,
        );
      }
      const idMapObject = idMap[req.id];
      if (idMapObject) {
        idMapObject.retryCount = retryCount + 1;
      }
      sendToStream(req); // 重新发送请求
    });
  }
}

3. eth-json-rpc-middleware

源码:github.com/MetaMask/et...

JSON-RPC 框架中的中间件集合,为 JsonRpcEngine 提供丰富的功能扩展。这些中间件可以像乐高积木一样自由组合,拦截、处理、缓存、转发、增强 JSON-RPC 请求。

主要文件与功能

  • wallet.ts

    • 功能:实现与钱包相关的核心中间件,如账户管理、签名、交易发送等。
    • 用途 :拦截如 eth_accountseth_sendTransactioneth_sign 等请求,调用本地钱包逻辑处理。
  • fetch.ts

    • 功能:实现将 JSON-RPC 请求通过 HTTP(s) 发送到远程节点(如 Infura、自建节点)。
    • 用途:通常作为 engine 的最后一个中间件,兜底处理所有未被前面中间件处理的请求。
  • providerAsMiddleware.ts

    • 功能:将标准 provider(如 ethers.js/web3.js provider)适配为 engine 可用的 middleware。
    • 用途:便于集成第三方 provider 作为 engine 的一环。
  • retryOnEmpty.ts

    • 功能:对返回空结果的请求自动重试,提升健壮性。
    • 用途:常用于区块、交易等可能因节点同步延迟而暂时查不到结果的场景。
  • inflight-cache.ts

    • 功能:对相同参数的并发请求做缓存,避免重复请求,提升性能。
    • 用途:如多个 DApp 同时请求同一个区块或账户信息时,只发一次请求。
  • block-cache.ts / block-ref.ts / block-ref-rewrite.ts / block-tracker-inspector.ts

    • 功能:与区块相关的缓存、引用重写、区块追踪等功能。
    • 用途:提升区块数据的获取效率,支持区块快照、缓存、重写等高级用法。

典型用法

javascript 复制代码
import { JsonRpcEngine } from '@metamask/json-rpc-engine';
import { createWalletMiddleware, createFetchMiddleware, createInflightCacheMiddleware } from './eth-json-rpc-middleware';

const engine = new JsonRpcEngine();
engine.push(createInflightCacheMiddleware());
engine.push(createWalletMiddleware({ getAccounts, signTransaction, ... }));
engine.push(createFetchMiddleware({ rpcUrl: 'https://mainnet.infura.io/v3/xxx' }));

这样,engine 会先查缓存,再走本地钱包逻辑,最后兜底发到远程节点。

由于中间件过多,不可能全部讲解,这里我们着重介绍 walletfetch 中间件:

wallet

wallet.ts 实现了与本地钱包相关的核心 JSON-RPC 方法拦截和处理。

拦截的 JSON-RPC 方法:

注意 :下列方法大部分不是以太坊的标准 RPC 命令,而是 MetaMask 定制化的命令如 wallet_xxx,有些和以太坊标准命令同名但实现也不仅仅是直接把命令转发到链上,中间有复杂的处理可参考源码或文档:docs.metamask.io/wallet/how-...

  • eth_accounts:返回当前钱包管理的所有账户地址数组。
  • eth_coinbase:返回当前钱包的主账户地址(通常是第一个账户)。
  • eth_sendTransaction:发起链上交易,由钱包弹窗确认并签名后广播。
  • eth_signTransaction:对交易数据进行签名但不广播,仅返回签名结果。
  • eth_signTypedData / eth_signTypedData_v3 / eth_signTypedData_v4:对结构化数据(EIP-712)进行签名,常用于合约交互和链下授权。
  • personal_sign:对任意消息进行签名,常用于链下认证和登录。
  • eth_getEncryptionPublicKey:获取账户的加密公钥,用于加密消息。
  • eth_decrypt:用账户私钥解密加密消息。
  • personal_ecRecover:从签名和消息中恢复出签名者的地址。
  • wallet_getCapabilities:查询钱包支持的扩展能力列表。
  • wallet_sendCalls:批量发送多条链上调用,实现高效批量操作。
  • wallet_getCallsStatus:查询批量调用的执行状态和结果。
javascript 复制代码
// `getAccounts`、`getCallsStatus` 等函数作为依赖通过参数传递进 `createWalletMiddleware`,中间件内 
// 部会先进行一些前置处理(如参数校验、权限检查、格式转换等),然后再调用这些实际的业务实现函数。真正的 
//业务逻辑函数(如 `getAccounts`、`getCallsStatus` 的具体实现)位于 `metamask-controller.js` 中,由 
//控制器负责与底层钱包数据和链上交互,确保中间件与核心逻辑解耦
export function createWalletMiddleware({
  getAccounts,
  getCallsStatus,
  getCapabilities,
  processDecryptMessage,
  processEncryptionPublicKey,
  processPersonalMessage,
  processTransaction,
  processSignTransaction,
  processTypedMessage,
  processTypedMessageV3,
  processTypedMessageV4,
  processSendCalls,
}:
WalletMiddlewareOptions): JsonRpcMiddleware<any, Block> {
  if (!getAccounts) {
    throw new Error('opts.getAccounts is required');
  }
  
  return createScaffoldMiddleware({
    // account lookups
    eth_accounts: createAsyncMiddleware(lookupAccounts),
    eth_coinbase: createAsyncMiddleware(lookupDefaultAccount),

    // tx signatures
    eth_sendTransaction: createAsyncMiddleware(sendTransaction),
    eth_signTransaction: createAsyncMiddleware(signTransaction),

    // message signatures
    eth_signTypedData: createAsyncMiddleware(signTypedData),
    eth_signTypedData_v3: createAsyncMiddleware(signTypedDataV3),
    eth_signTypedData_v4: createAsyncMiddleware(signTypedDataV4),
    personal_sign: createAsyncMiddleware(personalSign),
    eth_getEncryptionPublicKey: createAsyncMiddleware(encryptionPublicKey),
    eth_decrypt: createAsyncMiddleware(decryptMessage),
    personal_ecRecover: createAsyncMiddleware(personalRecover),

    // EIP-5792
    wallet_getCapabilities: createAsyncMiddleware(async (params, req) =>
      walletGetCapabilities(params, req, { getAccounts, getCapabilities }),
    ),
    wallet_sendCalls: createAsyncMiddleware(async (params, req) =>
      walletSendCalls(params, req, { getAccounts, processSendCalls }),
    ),
    wallet_getCallsStatus: createAsyncMiddleware(async (params, req) =>
      walletGetCallsStatus(params, req, {
        getCallsStatus,
      }),
    ),
  });

fetch

fetch.ts 实现了将 JSON-RPC 请求通过 HTTP(s) 发送到远程以太坊节点(如 Infura、自建节点),是 engine 的"兜底"中间件。所谓"兜底"中间件,就是只有当前面的中间件都没有终结请求时 fetchMiddleware 才会作为兜底中间件处理该请求,把它发到远程节点。我们前面说 eth-json-rpc-provider 通常作为最后一个中间件,和这里的描述冲突,实际上 eth-json-rpc-provider 更为通用,内部仍然是封装的 fetchMiddleware

核心代码:

javascript 复制代码
function createFetchMiddlewareWithRpcService({
  rpcService,
  options = {},
}: {
  rpcService: AbstractRpcService;
  options?: {
    originHttpHeaderKey?: string;
  };
}): JsonRpcMiddleware<JsonRpcParams, Json> {
  return createAsyncMiddleware(
    async (req: JsonRpcRequestWithOrigin<JsonRpcParams>, res) => {
      const headers =
        'originHttpHeaderKey' in options &&
        options.originHttpHeaderKey !== undefined &&
        req.origin !== undefined
          ? { [options.originHttpHeaderKey]: req.origin }
          : {};
      // 发送请求到远程节点,进行链上处理
      const jsonRpcResponse = await rpcService.request(
        {
          id: req.id,
          jsonrpc: req.jsonrpc,
          method: req.method,
          params: req.params,
        },
        {
          headers,
        },
      );

      if ('error' in jsonRpcResponse) {
        throw rpcErrors.internal({
          data: jsonRpcResponse.error,
        });
      }

      res.result = jsonRpcResponse.result;
    },
  );
}

4. eth-json-rpc-filters

源码:github.com/MetaMask/et...

createEthFilterMiddleware

实现了以太坊 JSON-RPC filter 相关方法的中间件,统一管理日志、区块、pending 交易等过滤器的创建、更新、查询和销毁,为 DApp 提供链上事件订阅与筛选能力。

使用方法 :DApp 需定期轮询调用 getFilterChanges 获取新事件。

javascript 复制代码
window.ethereum.request({ method: 'eth_newFilter', ... }) + 轮询
javascript 复制代码
// index.js
function createEthFilterMiddleware({ blockTracker, provider }) {
  return createScaffoldMiddleware({
    // install filters
    eth_newFilter:                   waitForFree(toFilterCreationMiddleware(newLogFilter)),
    eth_newBlockFilter:              waitForFree(toFilterCreationMiddleware(newBlockFilter)),
    eth_newPendingTransactionFilter: waitForFree(toFilterCreationMiddleware(newPendingTransactionFilter)),
    // uninstall filters
    eth_uninstallFilter:             waitForFree(toAsyncRpcMiddleware(uninstallFilterHandler)),
    // checking filter changes
    eth_getFilterChanges:            waitForFree(toAsyncRpcMiddleware(getFilterChanges)),
    eth_getFilterLogs:               waitForFree(toAsyncRpcMiddleware(getFilterLogs)),
  });
}

createSubscriptionMiddleware

通过用事件封装 EthFilterMiddleware 实现发布订阅机制。

使用方法:DApp 订阅后自动收到事件通知。

javascript 复制代码
window.ethereum.request({ method: 'eth_subscribe', ... }) + 监听事件
javascript 复制代码
// subscriptionManager.js
function createSubscriptionMiddleware({ blockTracker, provider }) {
  // state and utilities for handling subscriptions
  const subscriptions = {};
  const filterManager = createFilterMiddleware({ blockTracker, provider });

  // internal flag
  let isDestroyed = false;

  // create subscriptionManager api object
  const events = new SafeEventEmitter();
  const middleware = createScaffoldMiddleware({
    eth_subscribe: createAsyncMiddleware(subscribe),
    eth_unsubscribe: createAsyncMiddleware(unsubscribe),
  });
  middleware.destroy = destroy;
  return { events, middleware };
}

5. RpcService

源码:github.com/MetaMask/co...

这部分严格来说不属于JSON PRC框架本身,它是用来管理JSON-RPC服务,提供故障恢复与转移、重试、熔断、降级等机制,确保网络请求的高可用性。 详细介绍参考:区块链钱包开发------构建网络控制器(network-controller)

四、总结

在 MetaMask 的实现中,JSON RPC 框架通过中间件机制来处理请求和响应,支持多种以太坊(后面也将支持 Solana 和 BTC)相关的操作,如账户管理、交易发送和事件订阅等。中间件可以在请求处理的不同阶段插入自定义逻辑,从而实现功能扩展和错误处理。通过这种方式,开发者可以灵活地构建和管理与以太坊区块链的交互。

学习交流请添加vx: gh313061

本教程配套视频教程:space.bilibili.com/382494787/l...

下期预告:构建各个控制器间的通信框架

相关推荐
陀螺财经4 小时前
以太坊十年,一场造梦之旅
区块链
余_弦1 天前
区块链钱包开发(四.2)—— stream通信框架的使用
区块链
链上罗主任1 天前
以太坊十年:智能合约与去中心化的崛起
web3·区块链·智能合约·以太坊
不可描述的两脚兽1 天前
学习笔记《区块链技术与应用》第4天 比特币脚本语言
笔记·学习·区块链
余_弦1 天前
区块链钱包开发(四.1)—— 搭建stream风格的通信框架
区块链
技术路上的探险家1 天前
Web3:在 VSCode 中使用 Vue 前端与已部署的 Solidity 智能合约进行交互
vscode·web3·区块链·交互·react·solidity·ethers.js
阿祥~1 天前
FISCO BCOS Gin调用WeBASE-Front接口发请求
区块链·gin·fisocbocs
技术路上的探险家2 天前
Web3:以太坊虚拟机
web3·区块链·智能合约·solidity·foundry
AWS官方合作商2 天前
AWS Blockchain Templates:快速部署企业级区块链网络的终极解决方案
区块链·aws