Web3js如何在自定义区块链中获取pending交易并通过Express接口返回
问题根源
- 订阅逻辑写在HTTP接口处理函数中,每次接口被调用才会发起订阅,而HTTP接口是同步返回的,不等订阅回调获取到交易数据就已经返回了空数组
- 没有全局维护待处理交易的缓存池,订阅拿到的交易没有存储到接口可访问的公共变量中
- 缺少已上链/过期交易的清理逻辑,缓存会无限膨胀
实现方案(基于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
相关产品推荐
相关产品推荐

