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

如何从Node.js代码启停Hardhat JSON-RPC服务器实现主网同步

从Node.js代码中启动/终止Hardhat分叉节点的最优方案

针对你用hardhat_reset同步主网时出现节点异常的问题,最优方案是直接在Node.js代码中通过子进程管理来启动、终止Hardhat分叉节点,彻底避免重置命令带来的状态残留问题。

核心思路

用Node.js内置的child_process.spawn创建Hardhat节点的子进程,全程在代码中控制节点的生命周期:

  • 需要同步时,先优雅终止旧节点进程
  • 重新启动新节点,指定最新的主网区块号进行分叉
  • 通过监听进程输出确认节点就绪,避免过早发起交互

实现代码示例

封装节点管理工具

const { spawn } = require('child_process');
const Web3 = require('web3');

class HardhatForkManager {
  constructor(alchemyApiKey) {
    this.alchemyUrl = `https://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`;
    this.web3 = new Web3(this.alchemyUrl);
    this.nodeProcess = null;
    this.isReady = false;
  }

  // 启动Hardhat分叉节点,可指定分叉区块号(默认同步到最新)
  async start(forkBlockNumber = null) {
    // 先终止已有进程
    if (this.nodeProcess) await this.stop();

    const args = ['node', '--fork', this.alchemyUrl];
    if (forkBlockNumber) {
      args.push('--fork-block-number', forkBlockNumber.toString());
    }

    // 启动子进程,继承终端输出方便调试
    this.nodeProcess = spawn('npx', args, {
      stdio: ['inherit', 'pipe', 'inherit'],
      env: { ...process.env, HARDHAT_NETWORK: 'hardhat' }
    });

    return new Promise((resolve) => {
      // 监听输出,确认节点启动完成
      this.nodeProcess.stdout.on('data', (data) => {
        const log = data.toString();
        if (log.includes('Started HTTP and WebSocket JSON-RPC server')) {
          this.isReady = true;
          console.log('Hardhat分叉节点已就绪');
          resolve();
        }
      });

      // 监听进程异常退出
      this.nodeProcess.on('exit', (code) => {
        console.log(`Hardhat节点进程退出,代码:${code}`);
        this.isReady = false;
        this.nodeProcess = null;
      });
    });
  }

  // 优雅终止节点进程
  async stop() {
    if (!this.nodeProcess) return;

    return new Promise((resolve) => {
      // 发送SIGINT信号,模拟Ctrl+C终止Hardhat节点
      this.nodeProcess.on('exit', resolve);
      this.nodeProcess.kill('SIGINT');
    });
  }

  // 获取主网最新区块号
  async getLatestMainnetBlock() {
    return parseInt(await this.web3.eth.getBlockNumber());
  }
}

// 使用示例
async function runTestCycle(alchemyApiKey) {
  const manager = new HardhatForkManager(alchemyApiKey);
  
  // 第一次启动,同步到最新区块
  const latestBlock = await manager.getLatestMainnetBlock();
  await manager.start(latestBlock);

  // 执行你的测试逻辑...
  console.log('执行测试任务');

  // 一段时间后需要重新同步
  await new Promise(resolve => setTimeout(resolve, 30000)); // 模拟等待30秒

  // 终止旧节点,启动新节点同步最新区块
  const newLatestBlock = await manager.getLatestMainnetBlock();
  await manager.start(newLatestBlock);

  // 再次执行测试...
  console.log('执行新一轮测试任务');

  // 测试完成后清理
  await manager.stop();
}

// 替换为你的Alchemy API Key
runTestCycle('YOUR_ALCHEMY_API_KEY');

关键注意事项

  • 进程终止方式:必须用SIGINT信号终止Hardhat进程,而不是SIGKILL,否则会导致Hardhat无法清理临时文件,可能残留僵尸进程。
  • 启动就绪判断:必须等待节点输出"Started HTTP and WebSocket JSON-RPC server"日志后再发起交易,避免连接失败。
  • 环境变量传递:启动子进程时继承当前环境变量,确保Hardhat能读取到配置文件中的参数。
  • 异常处理:可以在代码中添加进程崩溃后的自动重启逻辑,提升稳定性。

这种方案完全替代了hardhat_reset的方式,每次同步都是全新的节点实例,彻底解决了重置导致的状态异常问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 14:33:21