You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web3js如何在自定义区块链中获取pending交易并通过Express接口返回

问题根源
  1. 订阅逻辑写在HTTP接口处理函数中,每次接口被调用才会发起订阅,而HTTP接口是同步返回的,不等订阅回调获取到交易数据就已经返回了空数组
  2. 没有全局维护待处理交易的缓存池,订阅拿到的交易没有存储到接口可访问的公共变量中
  3. 缺少已上链/过期交易的清理逻辑,缓存会无限膨胀
实现方案(基于ethers.js)

第一步:服务启动时全局初始化WS订阅,维护pending交易缓存

const ethers = require("ethers");
const url = "ws://192.168.112.82:7001";

// 全局pending交易缓存,用Map做去重、查询和删除
let pendingTxMap = new Map();
// pending交易过期时间:10分钟,超时未上链自动清理
const TX_EXPIRE_MS = 10 * 60 * 1000;

function initWsSubscribe() {
  const customWsProvider = new ethers.providers.WebSocketProvider(url);

  // 监听pending交易事件
  customWsProvider.on("pending", async (txHash) => {
    // 去重,已经在缓存里的交易不重复处理
    if (!txHash || pendingTxMap.has(txHash)) return;
    try {
      const transaction = await customWsProvider.getTransaction(txHash);
      if (transaction) {
        // 存入交易和缓存时间
        pendingTxMap.set(txHash, {
          ...transaction,
          cacheTime: Date.now()
        });
      }
    } catch (err) {
      console.error("获取pending交易详情失败:", err);
    }
  });

  // 监听新区块事件,清理已经上链的交易
  customWsProvider.on("block", async (blockNumber) => {
    try {
      const block = await customWsProvider.getBlock(blockNumber, true);
      if (block?.transactions) {
        block.transactions.forEach(tx => {
          pendingTxMap.delete(tx.hash);
        });
      }
      // 清理过期的pending交易
      const now = Date.now();
      for (const [hash, tx] of pendingTxMap.entries()) {
        if (now - tx.cacheTime > TX_EXPIRE_MS) {
          pendingTxMap.delete(hash);
        }
      }
    } catch (err) {
      console.error("清理已上链交易失败:", err);
    }
  });

  // WS连接异常自动重连
  customWsProvider._websocket.on("error", () => {
    console.log("WS连接异常,3秒后重连...");
    customWsProvider._websocket.terminate();
    setTimeout(initWsSubscribe, 3000);
  });
  customWsProvider._websocket.on("close", (code) => {
    console.log(`WS连接断开,错误码:${code},3秒后重连...`);
    customWsProvider._websocket.terminate();
    setTimeout(initWsSubscribe, 3000);
  });
}

// 服务启动时执行一次初始化订阅
initWsSubscribe();

第二步:接口逻辑直接返回缓存的交易列表

exports.pendingTransactions = (req, res) => {
  // 把Map转成数组,BigNumber类型转成字符串避免前端解析异常
  const txList = Array.from(pendingTxMap.values()).map(tx => ({
    ...tx,
    gasPrice: tx.gasPrice.toString(),
    gasLimit: tx.gasLimit.toString(),
    value: tx.value.toString()
  }));

  res.status(200).json({
    success: true,
    txns: txList
  });
};
节点配置注意事项
  • 你的自定义链节点需要开启WS服务和pending交易订阅权限,以Geth兼容节点为例,启动时需要添加参数:--ws --ws.api=eth,net,web3 --txpool.globalslots=2000
  • 若web3.eth.getPendingTransactions返回空,是因为Geth默认该接口只返回节点自身钱包发起的pending交易,需要额外添加启动参数--rpc.allow-unprotected-txs才能获取全节点的pending交易

内容的提问来源于stack exchange,提问作者kimiro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 22:09:00