Foundry vs Hardhat:2026年以太坊开发框架选型指南

Foundry vs Hardhat对决,游戏角色选择界面风格

一、为什么框架选择很重要

选择开发框架不仅仅是选一个编译工具,它是整个开发体验的基础。一个好的框架能让你:

  • 快速迭代:写完代码,几秒内看到测试结果
  • 高效调试:出了问题能快速定位根源
  • 安全交付:在代码上主网前发现潜在的漏洞
  • 团队协作:多个开发者能无缝合作

我见过有团队因为选错了框架,导致开发效率低下、测试覆盖率不足、最终在审计中发现大量问题。框架选对了,这些风险都能大大降低。

框架选型决策流程图,地铁线路图风格

二、Foundry深度解析

2.1 Foundry是什么

Foundry是Paradigm(知名加密风投)推出的开发框架,由Rust编写,核心卖点是极速。它包含三个主要工具:

  • Forge:编译、测试、部署
  • Cast:命令行与链交互(类似以太坊的瑞士军刀)
  • Anvil:本地测试节点

我第一次用Foundry跑测试时震惊了——原本Hardhat需要几分钟的测试套件,Foundry几秒就完成。这种速度提升对开发体验的影响是巨大的。

2.2 测试体验

Foundry最令人惊艳的功能是测试完全用Solidity编写:

solidity

// test/MyToken.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {Test, console} from "forge-std/Test.sol";
import {MyToken} from "../src/MyToken.sol";

contract MyTokenTest is Test {
    MyToken public token;
    
    function setUp() public {
        token = new MyToken();
    }
    
    function testTokenDeployment() public {
        assertEq(token.name(), "MyToken");
        assertEq(token.symbol(), "MTK");
        assertEq(token.totalSupply(), 1000000 * 10 ** 18);
    }
    
    function testTransfer() public {
        token.transfer(address(1), 100);
        assertEq(token.balanceOf(address(1)), 100);
    }
    
    // 模糊测试 - 自动生成各种输入
    function testTransferFuzz(address to, uint256 amount) public {
        uint256 balance = token.balanceOf(address(this));
        amount = bound(amount, 0, balance);
        
        token.transfer(to, amount);
        assertEq(token.balanceOf(to), amount);
        assertEq(token.balanceOf(address(this)), balance - amount);
    }
}

运行测试只需一行命令:

bash

forge test

2.3 Fuzzing与Invariant Testing

Foundry的模糊测试是我用过最强大的安全工具。普通测试需要你手动设计测试用例,而fuzzer会自动生成成千上万种输入组合,找出你没想到的边界情况:

solidity

function testTransferFuzz(address to, uint256 amount) public {
    vm.assume(to != address(0) && to != address(this));
    amount = bound(amount, 1, token.balanceOf(address(this)));
    
    uint256 recipientBefore = token.balanceOf(to);
    token.transfer(to, amount);
    
    assertEq(token.balanceOf(to), recipientBefore + amount);
}

Invariant Testing更进一步——你定义一个系统必须始终满足的不变式,fuzzer会尝试各种操作序列,看能否打破这个不变量:

solidity

// 不变量:总供应量应该等于所有账户余额之和
contract InvariantTest is Test {
    MyToken token;
    
    function setUp() public {
        token = new MyToken();
        targetContract(address(new Handler(token)));
    }
    
    function invariantTotalSupply() public view {
        assertEq(token.totalSupply(), token.balanceOf(address(this)));
    }
}

2.4 Gas报告

bash

forge snapshot

这个命令会生成一个Gas消耗报告文件,下次运行时自动对比,显示每个函数的Gas消耗变化。如果你的某个修改导致Gas暴涨,Foundry会明确标出。

三、Hardhat深度解析

3.1 Hardhat的特点

Hardhat是目前社区最流行的框架,生态系统非常成熟。它用JavaScript/TypeScript编写,对前端开发者友好,大量DeFi项目(如Uniswap、Aave)都基于Hardhat开发。

核心优势:

  • 插件生态丰富:hardhat-deploy、hardhat-ethers、hardhat-waffle…
  • 调试友好:内置console.log、详细的错误堆栈
  • TypeScript支持:大型项目类型安全有保障
  • 团队协作:配置文件易于版本控制和分享

3.2 项目初始化

bash

npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init

Hardhat的初始化向导会引导你创建项目结构,包括contracts/、scripts/、test/目录,以及配置文件。

3.3 测试配置

javascript

// test/token.test.js
const { expect } = require("chai");

describe("MyToken", function() {
  let token;
  let owner;
  let addr1;

  beforeEach(async function() {
    const Token = await ethers.getContractFactory("MyToken");
    [owner, addr1, ...addrs] = await ethers.getSigners();
    token = await Token.deploy();
    await token.deployed();
  });

  it("Should set the right name and symbol", async function() {
    expect(await token.name()).to.equal("MyToken");
    expect(await token.symbol()).to.equal("MTK");
  });

  it("Should transfer tokens correctly", async function() {
    await token.transfer(addr1.address, 50);
    expect(await token.balanceOf(addr1.address)).to.equal(50);
  });
});

3.4 部署脚本

Hardhat的部署脚本非常适合复杂的多步骤部署:

javascript

// scripts/deploy.js
const hre = require("hardhat");

async function main() {
  console.log("Deploying MyToken...");
  
  const Token = await hre.ethers.getContractFactory("MyToken");
  const token = await Token.deploy();
  
  await token.deployed();
  console.log(`Token deployed to: ${token.address}`);

  // 验证合约
  await hre.run("verify:verify", {
    address: token.address,
    constructorArguments: [],
  });
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

使用hardhat-deploy插件还能自动追踪部署历史和合约验证状态。

四、深度对比

4.1 性能对比

这是两者最显著的差异。我做过实际测试:

操作HardhatFoundry差异
编译(100个合约)45秒8秒5.6倍
运行100个单元测试120秒12秒10倍
Fuzz测试(10万次迭代)不支持15秒

Foundry的速度优势来自于它用Rust编写、编译到原生代码,以及测试直接在EVM中执行。

4.2 调试体验

Hardhat在这方面有优势。它的console.log可以打印任意数据类型:

solidity

function testDebug() public {
    uint256 balance = token.balanceOf(user);
    console.log("User balance:", balance);
    console.logBool("Has balance", balance > 0);
}

Foundry的调试能力正在快速追赶,trace信息非常详细,能看到每次调用的具体指令和堆栈。

4.3 插件与生态

Hardhat的插件生态是无与伦比的:

  • hardhat-deploy:追踪部署历史
  • hardhat-ethers:以太坊交互封装
  • hardhat-waffle:测试断言库
  • hardhat-gas-reporter:Gas消耗报告

Foundry的插件相对较少,但核心功能非常强大。高级用法需要写Solidity代码,不如JavaScript直观。

4.4 TypeScript支持

如果你的项目用TypeScript,Hardhat是更自然的选择。Foundry的配置文件用Toml,对TypeScript项目支持有限。

五、选型建议

选择Hardhat的场景:大型团队项目、复杂部署流程、前端集成密切的项目。

选择Foundry的场景:测试驱动开发、安全敏感项目、频繁迭代的个人项目。

我的实践:混合使用——Foundry用于测试和本地开发,Hardhat用于部署脚本。

这种组合能充分利用两者的优势。

六、迁移指南

6.1 从Hardhat迁移到Foundry

bash

# 安装Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# 初始化Foundry项目
forge init . --force

# Foundry自动创建test目录,适配现有测试

大部分Solidity代码无需修改。Hardhat测试需要用Foundry的语法重写:

javascript

// Hardhat风格
expect(await token.balanceOf(addr1.address)).to.equal(50);

// Foundry风格
assertEq(token.balanceOf(addr1.address), 50);

6.2 共享合约代码

如果团队同时用两个框架,可以在项目根目录放置合约代码:

plaintext

project/
├── contracts/           # Solidity合约,两个框架共用
├── foundry/            # Foundry配置和测试
├── hardhat/            # Hardhat配置和脚本
└── web/                # 前端代码

solidity

// foundry/src/MyContract.sol
import {MyContract} from "../contracts/MyContract.sol";

七、工具链整合

7.1 Slither静态分析

两个框架都支持Slither:

bash

# Foundry项目
forge build
slither . --contract "MyContract"

# Hardhat项目
npx hardhat compile
npx slither .

7.2 代码覆盖率

bash

# Foundry
forge coverage

# Hardhat (需要solidity-coverage插件)
npx hardhat coverage

Foundry的覆盖率报告更详细,能看到每个分支的覆盖情况。

结语

没有完美的框架,只有适合你项目的框架。我的建议是:

  1. 新学区块链开发:从Hardhat入手,社区资源丰富,遇到问题容易找到答案
  2. 追求测试效率:直接学Foundry,测试速度的优势用过就回不去
  3. 生产项目:根据团队技术栈选择,大型TypeScript项目选Hardhat,安全敏感项目选Foundry
  4. 不确定:两者都装上,根据需求切换

框架只是工具,真正重要的是写出安全、高效、可维护的智能合约。

相关阅读:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注