Hardhat 本地链卡住:交易 pending、区块不出块的排查与解决

Hardhat 本地链卡住通常表现为:交易发出后一直 pending、await tx.wait() 不返回、区块号停止增长。

快速诊断:区块是否在出块

# 用 cast(foundry 工具)
cast block-number --rpc-url http://127.0.0.1:8545

# 用 curl
curl -s -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

重复执行,如果区块号不增长说明链已卡死。

原因一:开启了手动挖矿模式

hardhat.config.js 中配置了 auto: false

networks: {
  hardhat: {
    mining: {
      auto: false,
      interval: 0
    }
  }
}

此时交易发出后不会自动打包,需要手动触发出块:

// Hardhat 测试脚本里
await network.provider.send("evm_mine");

// 一次挖多个块
await network.provider.send("hardhat_mine", ["0xa"]); // 10 个块

用 curl 触发:

curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"evm_mine","params":[],"id":1}'

原因二:nonce 冲突导致交易队列堵塞

某笔 gas 过低的交易占住 nonce,后续交易全部等待。查当前 nonce:

cast nonce 0x你的地址 --rpc-url http://127.0.0.1:8545

最简单的解法是直接重置链:

await network.provider.request({
  method: "hardhat_reset",
  params: []
});

或者重置到 fork 某个区块高度:

await network.provider.request({
  method: "hardhat_reset",
  params: [{
    forking: {
      jsonRpcUrl: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
      blockNumber: 20000000
    }
  }]
});

原因三:fork 主网时 RPC 超时

npx hardhat node --fork https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY

如果 Alchemy/Infura 限速或网络超时,Hardhat 会表现得像卡住。开启调试日志:

DEBUG=hardhat* npx hardhat node --fork ...

看是否有 timeoutrate limit 报错。

原因四:缓存/artifacts 脏数据

有时前端或脚本缓存了旧的合约地址/ABI,链本身没问题但调用失败。清理后重新编译部署:

rm -rf cache artifacts
npx hardhat compile
npx hardhat run scripts/deploy.js --network localhost

最直接的解法:重启

如果不需要保留链状态,直接重启即可:

Ctrl+C
npx hardhat node

Hardhat 本地链的状态不持久化,重启后从创世块重新开始。如果需要持久化,使用 hardhat_reset + snapshot(evm_snapshot / evm_revert)来管理状态。