🛠️ 第11课:Hardhat开发框架

DApp开发 ✅ 验证通过

🎯 学习目标:掌握Hardhat项目初始化、配置、编译、测试、部署全流程,理解Hardhat网络与forking。

📖 一、Hardhat是什么?

Hardhat = 以太坊开发的专业框架,提供编译、测试、部署、调试一体化工具链。基于JavaScript/TypeScript,是Truffle的继任者,也是目前最主流的以太坊开发框架。Hardhat Network内置本地EVM,支持mainnet forking,开发者可以在本地模拟真实链上环境进行测试。

# 安装与初始化
npm init -y
npm install --save-dev hardhat
npx hardhat init  # 选择 JavaScript project

# 项目结构
├── contracts/        ← Solidity源码
│   └── Lock.sol
├── scripts/          ← 部署脚本
│   └── deploy.js
├── test/             ← 测试文件
│   └── Lock.test.js
├── hardhat.config.js ← 配置文件
└── artifacts/        ← 编译输出

📖 二、hardhat.config.js配置详解

require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: {
    version: "0.8.19",
    settings: {
      optimizer: { enabled: true, runs: 200 },
      viaIR: true,
    },
  },
  networks: {
    hardhat: {
      forking: {
        url: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
        enabled: false,
      },
    },
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
  etherscan: { apiKey: process.env.ETHERSCAN_API_KEY },
};

📖 三、编写测试(Mocha + Chai)

const { expect } = require("chai");
const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers");

describe("Counter", function () {
  async function deployFixture() {
    const [owner, other] = await ethers.getSigners();
    const Factory = await ethers.getContractFactory("Counter");
    const counter = await Factory.deploy();
    return { counter, owner, other };
  }

  it("should start at 0", async function () {
    const { counter } = await loadFixture(deployFixture);
    expect(await counter.getCount()).to.equal(0);
  });

  it("should increment", async function () {
    const { counter } = await loadFixture(deployFixture);
    await counter.increment();
    expect(await counter.getCount()).to.equal(1);
  });

  it("should emit event", async function () {
    const { counter, owner } = await loadFixture(deployFixture);
    await expect(counter.increment())
      .to.emit(counter, "CountIncremented")
      .withArgs(1, owner.address);
  });

  it("should revert for non-owner", async function () {
    const { counter, other } = await loadFixture(deployFixture);
    await expect(counter.connect(other).reset())
      .to.be.revertedWithCustomError(counter, "Unauthorized");
  });
});

📖 四、部署脚本

const hre = require("hardhat");

async function main() {
  const Factory = await hre.ethers.getContractFactory("Counter");
  console.log("Deploying Counter...");
  const counter = await Factory.deploy();
  await counter.waitForDeployment();
  console.log("Deployed to:", await counter.getAddress());

  if (hre.network.name !== "hardhat") {
    await counter.deploymentTransaction().wait(5);
    await hre.run("verify:verify", {
      address: await counter.getAddress()
    });
  }
}
main().catch(console.error);
命令说明
npx hardhat compile编译合约
npx hardhat test运行测试
npx hardhat coverage测试覆盖率
npx hardhat node启动本地节点
npx hardhat run scripts/deploy.js --network sepolia部署到测试网
npx hardhat verify ADDR --network sepolia验证源码
npx hardhat console交互式控制台
Hardhat测试框架? optimizer.runs? mainnet forking? loadFixture? waitForDeployment?

🔧 动手实验

  1. npm init+安装Hardhat+初始化项目
  2. 编写Counter.sol合约
  3. 编写测试运行npx hardhat test
  4. 编写部署脚本本地部署
  5. 尝试mainnet forking测试

📖 五、Hardhat Network进阶

// hardhat.config.js - 高级网络配置
module.exports = {
  networks: {
    hardhat: {
      // 主网Fork: 本地模拟真实链上状态
      forking: {
        url: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
        blockNumber: 18000000,  // 固定区块(可重复测试)
        enabled: true,
      },
      // 自定义账户余额
      accounts: [{ balance: "10000000000000000000000" }],
      // 挖矿模式
      mining: { auto: true, interval: 5000 },
    },
  },
};

📖 六、Hardhat插件生态

插件用途
hardhat-toolbox一站式工具包(推荐)
hardhat-gas-reporterGas消耗报告
solidity-coverage测试覆盖率
hardhat-watcher文件变化自动测试
hardhat-deploy高级部署管理
hardhat-verifyEtherscan源码验证

📖 七、常见陷阱与最佳实践

  1. ❌ 忘记设置optimizer导致部署Gas过高
  2. ❌ PRIVATE_KEY泄露到Git(用dotenv管理)
  3. ❌ 测试不用loadFixture导致状态污染
  4. ❌ 部署脚本没有错误处理
  5. ❌ 跳过Etherscan源码验证步骤
  6. ✅ 使用dotenv管理敏感配置
  7. ✅ CI/CD自动化测试和部署

📖 八、Hardhat部署进阶

const hre = require("hardhat");

// 多合约部署脚本
async function main() {
  // 1. 部署代币
  const Token = await hre.ethers.getContractFactory("SimpleERC20");
  const token = await Token.deploy("MyToken", "MTK", 1000000);
  await token.waitForDeployment();
  console.log("Token:", await token.getAddress());

  // 2. 部署DEX(依赖代币地址)
  const DEX = await hre.ethers.getContractFactory("SimpleDEX");
  const dex = await DEX.deploy(await token.getAddress(), "0xOtherToken");
  await dex.waitForDeployment();
  console.log("DEX:", await dex.getAddress());

  // 3. 验证源码(仅非本地网络)
  if (hre.network.name !== "hardhat") {
    console.log("Waiting for confirmations...");
    await token.deploymentTransaction().wait(5);
    await hre.run("verify:verify", {
      address: await token.getAddress(),
      constructorArguments: ["MyToken", "MTK", 1000000],
    });
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

📖 九、环境变量管理

// .env文件(不要提交到Git!)
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
PRIVATE_KEY=your_private_key_here
ETHERSCAN_API_KEY=your_api_key_here

// hardhat.config.js
require("dotenv").config();
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: "0.8.19",
  networks: {
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL,
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    },
  },
  etherscan: { apiKey: process.env.ETHERSCAN_API_KEY },
};

📖 十、TypeScript支持

// Hardhat + TypeScript配置
// npm install --save-dev typescript ts-node @types/node @types/chai @types/mocha

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "outDir": "./dist"
  },
  "include": ["./scripts", "./test", "./deploy"],
  "files": ["./hardhat.config.ts"]
}

// test/Token.test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
import { Token } from "../typechain-types";

describe("Token", function () {
  let token: Token;
  beforeEach(async () => {
    const Factory = await ethers.getContractFactory("Token");
    token = await Factory.deploy("Test", "TST", 1000000);
  });

  it("should have correct name", async () => {
    expect(await token.name()).to.equal("Test");
  });
});

📖 十一、Hardhat常见问题

FAQ:
编译错误: 检查pragma版本,清除缓存npx hardhat clean
测试超时: 增加mocha超时this.timeout(60000)
网络问题: 检查RPC URL和API Key
Gas估算失败: 手动指定gasLimit
验证失败: 等待更多确认,检查constructor参数
Forking慢: 缓存fork数据,限制blockNumber
类型错误: 运行npx hardhat compile生成typechain类型

📖 十二、Hardhat性能优化

// hardhat.config.js - 性能优化配置
module.exports = {
  solidity: {
    version: "0.8.19",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,       // 调用次数预估,影响优化方向
        // runs=1: 最小部署大小(部署便宜,调用贵)
        // runs=999999: 最小运行Gas(部署贵,调用便宜)
      },
      viaIR: true,       // ✅ IR pipeline: 更好的优化
      evmVersion: "paris", // Shanghai后包括PUSH0
    },
  },
  mocha: { timeout: 60000 },  // 增加超时
};

📖 十三、Hardhat调试工具

调试工具链:
hardhat-tracer: 追踪内部调用和事件
hardhat-gas-reporter: 每个函数Gas报告
solidity-coverage: 测试覆盖率报告
hardhat-deploy: 部署管理和地址簿
Tenderly: 交易模拟和调试平台
Remix: 在线IDE快速原型验证

📖 十四、Hardhat与Foundry对比

HardhatFoundry
语言JavaScript/TypeScriptSolidity
测试速度较慢(EVM模拟)极快(原生编译)
Fuzz测试手动JS循环原生支持
学习曲线低(前端友好)中等(需学Solidity)
生态成熟(更多插件)快速增长
适合全栈开发者Solidity专家
🛠️

框架驾驭者

你已掌握Hardhat全流程——编译/测试/部署/验证!

✅ 项目初始化 ✅ 配置优化 ✅ Chai测试 ✅ 部署脚本

📋 课程目录