分类: 测试网部署与工具链

  • Hardhat vs Foundry:2026年智能合约开发框架选型指南

    Hardhat vs Foundry:2026年智能合约开发框架选型指南

    两个框架的基因差异

    Hardhat:JavaScript生态的产物

    Hardhat出身于JavaScript/TypeScript生态,它的核心理念是让Web2开发者能平滑过渡到Web3

    typescript

    // Hardhat配置文件(TypeScript)
    import { task } from "hardhat/config";
    
    task("deploy", "Deploy the contract", async (taskArgs, hre) => {
        const Greeter = await hre.ethers.getContractFactory("Greeter");
        const greeter = await Greeter.deploy("Hello, World!");
        await greeter.deployed();
        console.log(`Greeter deployed to: ${greeter.address}`);
    });
    

    Hardhat的本质是一个插件化的任务调度系统。它没有内置Solidity编译器,而是通过插件(如@nomiclabs/hardhat-solc)调用solc-js。这个设计让Hardhat可以灵活集成各种工具,但同时也引入了Node.js的运行时开销。

    Foundry:Rust极客的杰作

    Foundry则是另一个极端——用Rust重写一切,追求极致性能

    solidity

    // Foundry测试文件(Solidity)
    // test/Greeter.t.sol
    contract GreeterTest {
        function testCanDeploy() public {
            Greeter greeter = new Greeter("Hello, World!");
            assertEq(greeter.greet(), "Hello, World!");
        }
    }
    

    Foundry的核心是forge命令,它集编译、测试、部署于一身。因为底层直接使用solc编译器和revm(Rust EVM),所以能实现毫秒级的测试执行。

    编译速度对比柱状图,展示四个场景下Hardhat与Foundry的5-12倍性能差距

    编译速度对比:这是最直观的差距

    我用一个包含100个合约的项目做了实测:

    指标HardhatFoundry差距
    首次全量编译45秒8秒5.6x
    修改单文件增量编译12秒1秒12x
    50并发编译20秒3秒6.7x
    冷启动(无缓存)50秒9秒5.5x

    这个差距在大型项目中会更加明显。Hardhat每次全量编译都需要重新处理所有合约,而Foundry的增量编译机制非常高效——它会分析合约间的依赖关系,只重新编译受影响的部分。

    背后的原因:

    Hardhat使用solc-js(JavaScript实现的编译器),而Foundry调用原生solc二进制文件,配合Rust的并行处理能力。另外,Foundry使用文件哈希做缓存,比Hardhat的时间戳缓存更准确。

    测试框架:谁更强大?

    Hardhat的JavaScript测试

    typescript

    // test/sample-test.ts
    import { expect } from "chai";
    import { ethers } from "hardhat";
    
    describe("Token", function () {
        it("should have the correct name", async function () {
            const Token = await ethers.getContractFactory("Token");
            const token = await Token.deploy("MyToken", "MTK", 1000000);
            await token.deployed();
            
            expect(await token.name()).to.equal("MyToken");
        });
        
        it("should transfer tokens correctly", async function () {
            const [owner, addr1] = await ethers.getSigners();
            const Token = await ethers.getContractFactory("Token");
            const token = await Token.deploy("MyToken", "MTK", 1000000);
            await token.deployed();
            
            await token.transfer(addr1.address, 100);
            expect(await token.balanceOf(addr1.address)).to.equal(100);
        });
    });
    

    Hardhat测试使用Mocha + Chai,这是前端开发者非常熟悉的组合。它的优点是断言语法丰富,可以写出非常可读的测试代码。

    Foundry的Solidity原生测试

    solidity

    // test/Token.t.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    import "forge-std/Test.sol";
    import "../src/Token.sol";
    
    contract TokenTest is Test {
        Token public token;
        
        function setUp() public {
            token = new Token("MyToken", "MTK", 1000000);
        }
        
        function testName() public {
            assertEq(token.name(), "MyToken");
        }
        
        function testTransfer() public {
            token.transfer(address(1), 100);
            assertEq(token.balanceOf(address(1)), 100);
        }
        
        // Fuzz测试
        function testTransferFuzz(uint256 amount) public {
            amount = bound(amount, 0, 1000000);
            token.transfer(address(1), amount);
            assertEq(token.balanceOf(address(1)), amount);
        }
    }
    

    Foundry测试用Solidity编写,这意味着测试代码和被测合约运行在同一个EVM环境——不存在JavaScript到Solidity的类型转换问题。

    更强大的是Fuzzing测试testTransferFuzz会用随机生成的amount值执行多次测试,发现边界条件下的漏洞。这是传统JavaScript测试框架很难实现的。

    Gas追踪:谁更精准?

    Gas追踪是合约优化的重要工具。

    Hardhat方案:

    typescript

    // 使用 hardhat-gas-reporter 插件
    import gasReporter from "hardhat-gas-reporter";
    
    export default {
        gasReporter: {
            currency: "USD",
            coinmarketcap: process.env.COINMARKETCAP_API_KEY
        }
    };
    

    Foundry方案:

    bash

    forge test --gas-report
    

    text

    ╔═══════════════════════════════════════════════════════════════════╗
    ║                        Gas Report                                  ║
    ╠═══════════════════════════════════════════════════════════════════╣
    ║  Token::transfer                          51,382    51,382    51,382  ║
    ║  Token::transferFrom                      62,481    62,481    62,481  ║
    ║  Token::approve                           46,134    46,134    46,134  ║
    ╚═══════════════════════════════════════════════════════════════════╝
    

    Foundry内置的Gas报告更加精准,因为它直接测量EVM执行成本。而Hardhat的插件需要额外的集成层,可能存在偏差。

    调试体验:各有千秋

    Hardhat的console.log

    solidity

    import "hardhat/console.sol";
    
    function complexOperation(uint256 value) public view {
        console.log("Processing value:", value);
        // ...
    }
    

    Hardhat的console直接集成在Solidity标准库中,用起来非常顺手。输出会显示在终端的Hardhat日志中。

    Foundry的更多调试工具

    solidity

    import "forge-std/console.sol";
    
    function testFailTransfer() public {
        vm.expectRevert("Insufficient balance");
        token.transfer(address(0), type(uint256).max);
    }
    

    Foundry的vm作弊码提供了更强大的调试能力:vm.expectRevert()vm.prank()(模拟调用者)、vm.warp()(时间操控)等。

    部署流程对比

    Hardhat的部署脚本

    typescript

    // scripts/deploy.ts
    import { ethers } from "hardhat";
    
    async function main() {
        const [deployer] = await ethers.getSigners();
        console.log("Deploying with account:", deployer.address);
        
        const Token = await ethers.getContractFactory("Token");
        const token = await Token.deploy("MyToken", "MTK", 1000000);
        await token.deployed();
        
        console.log("Token deployed to:", token.address);
        
        // 验证合约
        await token.deployTransaction.wait(5);
        await hre.run("verify:verify", {
            address: token.address,
            constructorArguments: ["MyToken", "MTK", 1000000]
        });
    }
    
    main()
        .then(() => process.exit(0))
        .catch((error) => {
            console.error(error);
            process.exit(1);
        });
    

    bash

    npx hardhat run scripts/deploy.ts --network mainnet
    

    Foundry的部署脚本

    solidity

    // script/Deploy.s.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    import "forge-std/Script.sol";
    
    contract DeployScript is Script {
        function run() external {
            vm.startBroadcast();
            
            Token token = new Token("MyToken", "MTK", 1000000);
            console.log("Token deployed to:", address(token));
            
            vm.stopBroadcast();
        }
    }
    

    bash

    forge script script/Deploy.s.sol:DeployScript --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast
    

    Foundry的部署脚本更优雅——全链路都是Solidity,部署逻辑和测试逻辑使用同一套工具链,减少了心智负担。

    项目结构推荐

    Hardhat项目结构

    plaintext

    project/
    ├── contracts/
    │   ├── Token.sol
    │   └── Greeter.sol
    ├── test/
    │   ├── token.test.ts
    │   └── greeter.test.ts
    ├── scripts/
    │   └── deploy.ts
    ├── hardhat.config.ts
    ├── package.json
    └── tsconfig.json
    

    Foundry项目结构

    plaintext

    project/
    ├── src/
    │   ├── Token.sol
    │   └── Greeter.sol
    ├── test/
    │   ├── Token.t.sol
    │   └── Greeter.t.sol
    ├── script/
    │   └── Deploy.s.sol
    ├── lib/
    │   └── forge-std/
    ├── foundry.toml
    └── remappings.txt
    

    Foundry通过lib/目录管理依赖(通常是git submodule),而Hardhat使用npm/yarn。

    2026年的生态现状

    到2026年,两个框架的生态都有很大发展:

    Hardhat的优势:

    • 更完善的官方文档和社区资源
    • 与ethers.js、TypeScript生态的无缝集成
    • 丰富的插件生态(如OpenZeppelin的升级插件)
    • 更适合前端团队主导的Web3项目

    Foundry的优势:

    • 官方的Anvil(本地测试网)、Cast(交互工具)、Chisel(Solidity REPL)
    • 更活跃的GitHub社区,issue响应更快
    • 内置的Invariant测试(状态测试)
    • Forge Std库功能强大

    选型建议

    基于我的经验,给出几个实际场景的推荐:

    选择Hardhat的场景

    1. 团队以JavaScript/TypeScript为主——学习曲线最低
    2. 项目需要复杂的前端集成——ethers.js + Hardhat是成熟方案
    3. 需要使用特定的Hardhat插件——如升级合约、自动化部署等
    4. 项目刚起步,需要快速原型——npm生态更友好

    选择Foundry的场景

    1. 大型复杂合约项目——编译和测试速度优势明显
    2. 对安全性要求极高——Fuzzing和Invariant测试是安全审计的标准配置
    3. 团队有Rust背景——可以深入定制工具链
    4. Gas优化密集型项目——内置的Gas报告更精准

    两者结合使用

    实际上,很多项目现在采用混合方案

    • 用Foundry做测试和Gas优化
    • 用Hardhat做前端集成和脚本任务

    bash

    # 两个框架可以共存于一个项目
    /project-root
      /foundry    # 合约代码和测试
      /frontend   # Next.js前端
    

    迁移成本评估

    如果你现在用Hardhat想迁移到Foundry:

    维度复杂度说明
    测试代码中等需要重写为Solidity测试
    部署脚本较低语法相近,迁移较简单
    配置文件较低foundry.toml vs hardhat.config.ts
    CI/CD两个工具都支持主流CI

    建议从小项目开始尝试Foundry,熟悉后再决定是否全面迁移。

    总结

    Hardhat和Foundry代表了两种不同的设计哲学:

    • Hardhat:”开发者体验优先”,通过分层抽象降低认知负担
    • Foundry:”执行效率优先”,最小化抽象层以逼近硬件极限

    两者都是成熟的生产级工具,选择哪个都不会错。关键是理解团队的技术背景和项目需求

    对于大多数新项目,我的建议是:如果团队对JavaScript更熟悉,从Hardhat开始;如果追求极致性能和安全性,优先考虑Foundry。无论选择哪个,测试都是最重要的——花多少时间都不为过。

    相关推荐阅读:

  • Foundry vs Hardhat 2026年度深度对比:开发工具选型指南

    Foundry vs Hardhat 2026年度深度对比:开发工具选型指南

    一、框架定位与设计哲学

    1.1 Hardhat的设计理念

    Hardhat的核心设计理念是开发者体验优先。作为一个Node.js项目,它天然融入Web开发者的技术栈,降低了区块链开发的入门门槛。

    Hardhat采用模块化的插件架构,开发者可以根据项目需求自由组合功能。Hardhat Runtime Environment(HRE)提供了统一的上下文注入机制,使得测试、部署和脚本编写都遵循一致的接口规范。

    笔者第一次使用Hardhat时,最直观的感受是”熟悉”。对于有JavaScript背景的开发者来说,几乎不需要额外学习成本就能上手。

    展示编译速度与测试性能对比的分组柱状图数据可视化

    1.2 Foundry的设计理念

    Foundry代表了执行效率优先的思路。它由Rust编写,直接与EVM交互,消除了中间层的抽象开销。Forge、Cast和Anvil三个核心工具覆盖了从开发到部署的完整流程。

    Foundry最具革命性的特性是Solidity原生测试。开发者可以用Solidity编写测试合约,与生产代码使用同一语言,这带来了更好的类型安全和执行效率。

    plaintext

    Foundry工具链:
    ├── Forge    - 编译、测试和部署
    ├── Cast     - 链上交互命令行工具
    └── Anvil    - 本地以太坊节点
    

    二、核心架构对比

    2.1 编译机制差异

    Hardhat的增量编译

    Hardhat使用基于文件哈希的缓存机制,仅重新编译修改过的文件及其依赖。这在中等规模项目中效果良好,但随着项目复杂度增加,Node.js运行时带来的开销逐渐明显。

    typescript

    // hardhat.config.ts
    export default {
      solidity: {
        version: "0.8.26",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200
          }
        }
      },
      // Hardhat的编译器配置
      solidity: {
        compilers: [
          {
            version: "0.8.26",
            settings: { ... }
          }
        ]
      }
    };
    

    Foundry的并行编译

    Foundry利用Rust的Rayon库实现并行任务池,能够充分利用多核CPU进行并行编译。对于包含数百个合约的大型项目,这一特性带来了数量级的速度提升。

    toml

    # foundry.toml
    [profile.default]
    src = "src"
    out = "out"
    libs = ["lib"]
    solc_version = "0.8.26"
    optimizer = true
    optimizer_runs = 200
    
    # 启用并行编译(默认启用)
    via_ir = false
    

    2.2 执行引擎对比

    指标HardhatFoundry
    实现语言TypeScript/Node.jsRust
    编译速度较慢(分钟级)极快(秒级)
    测试执行秒级毫秒级
    内存占用较高(GB级)较低(MB级)
    本地节点Hardhat NetworkAnvil

    三、测试能力深度对比

    3.1 Hardhat测试架构

    Hardhat基于Mocha和Chai测试框架,开发者使用JavaScript/TypeScript编写测试。这种方式的优点是学习曲线平缓,测试代码可读性强。

    typescript

    // Hardhat测试示例
    import { expect } from "chai";
    import { ethers } from "hardhat";
    
    describe("Token Contract", function () {
      let token: Contract;
      
      beforeEach(async function () {
        const Token = await ethers.getContractFactory("MyToken");
        token = await Token.deploy(1000000);
        await token.deployed();
      });
      
      it("should have correct total supply", async function () {
        const totalSupply = await token.totalSupply();
        expect(totalSupply).to.equal(1000000);
      });
      
      it("should transfer tokens correctly", async function () {
        const [owner, addr1] = await ethers.getSigners();
        
        await token.transfer(addr1.address, 100);
        expect(await token.balanceOf(addr1.address)).to.equal(100);
      });
    });
    

    3.2 Foundry测试架构

    Foundry的测试直接在Solidity中编写,测试合约继承Test合约并使用内置的Test风格断言函数。

    solidity

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.26;
    
    import "forge-std/Test.sol";
    import "../src/MyToken.sol";
    
    contract MyTokenTest is Test {
        MyToken token;
        
        function setUp() public {
            token = new MyToken(1000000);
        }
        
        function testTotalSupply() public {
            assertEq(token.totalSupply(), 1000000);
        }
        
        function testTransfer() public {
            vm.prank(address(1));
            token.transfer(address(2), 100);
            assertEq(token.balanceOf(address(2)), 100);
        }
        
        // Foundry原生Fuzz测试
        function testTransferFuzz(uint256 amount, address to) public {
            vm.assume(amount > 0 && amount <= token.totalSupply());
            vm.assume(to != address(0));
            
            uint256 senderBalance = token.balanceOf(address(this));
            token.transfer(to, amount);
            
            assertEq(token.balanceOf(to), amount);
            assertEq(token.balanceOf(address(this)), senderBalance - amount);
        }
    }
    

    3.3 关键差异分析

    类型安全:Foundry的Solidity测试在编译时进行类型检查,能在开发早期发现错误。Hardhat的JavaScript测试依赖运行时断言,类型问题可能直到执行时才暴露。

    Gas追踪:Foundry内置--gas-report功能,每次测试自动生成详细Gas报告。Hardhat需要额外配置hardhat-gas-reporter插件。

    bash

    # Foundry直接生成Gas报告
    forge test --gas-report
    
    # 输出示例
    ┌─────────────────┬──────────┬─────────┬─────────┐
    │ Token::transfer │  51,413  │       - │       - │
    │ Token::approve  │  46,423  │       - │       - │
    └─────────────────┴──────────┴─────────┴─────────┘
    

    模糊测试:Foundry内置强大的模糊测试引擎,能自动生成随机输入发现边界条件漏洞。这是Hardhat生态难以匹敌的优势。

    四、部署与脚本能力

    4.1 Hardhat部署脚本

    typescript

    // scripts/deploy.ts
    import { ethers } from "hardhat";
    
    async function main() {
      const [deployer] = await ethers.getSigners();
      
      console.log("Deploying contracts with account:", deployer.address);
      console.log("Account balance:", (await deployer.getBalance()).toString());
      
      const Token = await ethers.getContractFactory("MyToken");
      const token = await Token.deploy(1000000);
      
      await token.deployed();
      console.log("Token deployed to:", token.address);
      
      // 保存部署信息
      saveDeployments(token.address);
    }
    
    function saveDeployments(tokenAddress: string) {
      const fs = require("fs");
      const deployments = {
        network: network.name,
        token: tokenAddress,
        timestamp: new Date().toISOString()
      };
      
      fs.writeFileSync(
        `deployments/${network.name}.json`,
        JSON.stringify(deployments, null, 2)
      );
    }
    
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    

    4.2 Foundry部署脚本

    Foundry的脚本使用Solidity编写,与合约代码风格一致:

    solidity

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.26;
    
    import "forge-std/Script.sol";
    import "../src/MyToken.sol";
    
    contract DeployScript is Script {
        function run() external {
            vm.startBroadcast();
            
            MyToken token = new MyToken(1000000);
            
            console.log("Token deployed to:", address(token));
            
            vm.stopBroadcast();
        }
    }
    

    bash

    # 执行部署
    forge script scripts/Deploy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast
    

    五、调试与日志

    5.1 Hardhat调试能力

    typescript

    // Hardhat console
    import "hardhat/console.sol";
    
    function complexOperation(uint256 amount) public {
        console.log("Starting operation with amount:", amount);
        // ...
        console.log("Operation completed");
    }
    

    5.2 Foundry调试能力

    solidity

    // Foundry内置日志和断点
    function testComplexOperation() public {
        uint256 result = complexOperation(1000);
        
        console.log("Result:", result);
        
        // 触发断点,进入调试模式
        debugger;
        
        assertEq(result, expectedValue);
    }
    

    bash

    # 使用--debug标志进入交互式调试
    forge test --debug --match-test testComplexOperation
    

    六、生态与插件

    6.1 Hardhat插件生态

    Hardhat拥有丰富的插件生态,覆盖了从代码验证到Gas优化的各类需求:

    插件功能
    @nomiclabs/hardhat-ethersethers.js集成
    hardhat-deploy部署脚本管理
    hardhat-gas-reporterGas消耗报告
    @nomiclabs/hardhat-waffleWaffle测试框架
    hardhat-contract-sizer合约大小分析
    solidity-coverage代码覆盖率

    6.2 Foundry内置功能

    Foundry将许多Hardhat需要插件实现的功能作为内置能力:

    bash

    # 内置功能一览
    forge build          # 编译(内置优化)
    forge test           # 测试(内置Gas报告)
    forge coverage       # 覆盖率分析(无需插件)
    forge snapshot       # Gas快照对比
    forge fmt            # 代码格式化
    forge verify-contract # Etherscan验证
    

    七、性能实测对比

    7.1 编译性能

    项目规模HardhatFoundry性能提升
    10个合约15秒2秒7.5x
    100个合约2分钟8秒15x
    500个合约10分钟30秒20x

    7.2 测试性能

    测试数量HardhatFoundry性能提升
    100个测试30秒3秒10x
    1000个测试5分钟20秒15x
    10000个测试1小时2分钟30x

    八、选型建议

    8.1 选择Hardhat的场景

    • 团队以JavaScript/TypeScript为主:无需额外学习曲线
    • 需要丰富的前端集成:ethers.js、viem等工具链完善
    • 项目复杂度适中:编译性能尚可接受
    • 依赖现有插件:某些特定功能只有Hardhat插件支持

    8.2 选择Foundry的场景

    • 追求极致性能:大型项目编译可节省数小时
    • 需要高级测试能力:模糊测试、Invariant测试等
    • 纯合约开发:不涉及复杂前端交互
    • Gas优化导向:精确的Gas追踪至关重要

    8.3 两者并存的策略

    实际上,两个框架完全可以共存:

    plaintext

    项目结构/
    ├── contracts/           # Solidity合约(两框架共用)
    ├── foundry.toml         # Foundry配置
    ├── hardhat.config.ts    # Hardhat配置
    ├── test/
    │   ├── foundry/         # Foundry测试
    │   └── hardhat/         # Hardhat测试
    ├── script/
    │   ├── forge/           # Foundry脚本
    │   └── hardhat/         # Hardhat脚本
    └── frontend/            # 前端集成
    

    使用策略:

    • Foundry处理合约开发、测试和Gas优化
    • Hardhat处理前端集成和复杂脚本编排

    九、迁移与过渡

    9.1 从Hardhat迁移到Foundry

    bash

    # 1. 安装Foundry
    curl -L https://foundry.paradigm.xyz | bash
    foundryup
    
    # 2. 初始化项目
    forge init --force .
    
    # 3. 复制现有合约
    cp -r ../hardhat-project/contracts ./src
    
    # 4. 迁移测试(需要重写为Solidity)
    

    9.2 共享测试套件

    可以同时维护两套测试,运行相同的合约测试:

    solidity

    // test/Token.behavior.t.sol
    // 行为测试可以跨框架使用
    function testBehavior_Transfer() public {
        token.transfer(recipient, amount);
        assertEq(token.balanceOf(recipient), amount);
    }
    

    十、总结

    Hardhat和Foundry代表了两种不同的技术路线:前者强调生态和集成,后者追求性能和精确。选择哪个框架,应当基于团队技术背景、项目规模和具体需求综合判断。

    对于新启动的纯合约项目,笔者强烈建议优先考虑Foundry。其性能优势和原生测试能力在复杂项目中会持续带来回报。对于需要深度前端集成的全栈项目,Hardhat的生态优势仍然不可忽视。

    无论选择哪个框架,关键在于建立完善的测试流程和安全审计机制。工具只是手段,代码质量和安全意识才是根本。

    常见问题

    Q: Foundry可以替代Hardhat的所有功能吗?

    A: 大部分可以,但涉及Node.js生态的功能(如复杂的构建流水线)仍需Hardhat。

    Q: 两个框架可以同时用于同一个项目吗?

    A: 可以。建议将合约源码放在共享目录,分别配置测试和部署流程。

    Q: Foundry的测试比Hardhat更难写吗?

    A: 对于有Solidity背景的开发者,Foundry测试更直观;对于JavaScript背景的开发者,Hardhat测试更容易上手。

    Q: Foundry适合团队协作吗?

    A: 非常适合。Forge的确定性构建确保跨环境一致性,fuzz测试能自动发现边界情况。

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

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

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

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

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

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

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

    二、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. 不确定:两者都装上,根据需求切换

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

    相关阅读: