Foundry 智能合约开发实战:从环境搭建到部署测试
1. 引言
Foundry 是以 Rust 编写的智能合约开发工具链,以其极快的编译速度、内置的测试框架和强大的命令行工具而闻名。相较于传统的 Hardhat 或 Truffle,Foundry 在开发体验和性能上都有显著提升,尤其适合追求高效开发的 Solidity 开发者。
本文将带您从零开始,完成一个完整的 Foundry 智能合约开发实战,涵盖环境搭建、项目创建、合约编写、测试编写、部署脚本以及常用高级功能。
2. 环境准备与安装
2.1 系统要求
- Rust 工具链:Foundry 基于 Rust 构建,需要先安装 Rust。如果尚未安装,可以使用以下命令:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - Git:用于版本控制和依赖管理。
2.2 安装 Foundry
Foundry 的安装非常简单,通过 foundryup 工具可以一键安装:
curl -L https://foundry.paradigm.xyz | bash
安装完成后,重启终端或运行 source ~/.bashrc(或对应 shell 的配置文件),然后执行:
foundryup
这将安装或更新 forge、cast、anvil 和 chisel 等核心工具。
2.3 验证安装
运行以下命令验证安装是否成功:
forge --version
cast --version
anvil --version
如果都能正确输出版本号,说明 Foundry 已准备就绪。
3. 创建第一个 Foundry 项目
使用 forge init 命令创建一个新项目:
forge init hello-foundry
cd hello-foundry
项目结构如下:
hello-foundry/
├── foundry.toml # 项目配置文件
├── script/ # 部署脚本
├── src/ # 合约源代码
│ └── Counter.sol # 示例合约
├── test/ # 测试文件
│ └── Counter.t.sol # 示例测试
└── lib/ # 依赖库(如 OpenZeppelin)
4. 编写智能合约
让我们修改默认的 Counter 合约,实现一个简单的代币合约 SimpleToken。
src/SimpleToken.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract SimpleToken is ERC20 {
address public owner;
constructor(uint256 initialSupply) ERC20("SimpleToken", "STK") {
owner = msg.sender;
_mint(msg.sender, initialSupply * 10 ** decimals());
}
// 仅所有者可以增发代币
function mint(address to, uint256 amount) external {
require(msg.sender == owner, "Only owner can mint");
_mint(to, amount);
}
// 销毁代币
function burn(uint256 amount) external {
_burn(msg.sender, amount);
}
}
5. 编写测试
Foundry 的测试框架非常强大,支持 Solidity 编写测试,并内置了作弊码(Cheatcodes)用于模拟各种链上场景。
test/SimpleToken.t.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/SimpleToken.sol";
contract SimpleTokenTest is Test {
SimpleToken public token;
address public owner = address(0x1);
address public user = address(0x2);
function setUp() public {
vm.startPrank(owner); // 使用作弊码模拟 owner 地址
token = new SimpleToken(1000); // 初始发行 1000 个代币
vm.stopPrank();
}
function testInitialSupply() public {
assertEq(token.totalSupply(), 1000 * 10 ** token.decimals());
assertEq(token.balanceOf(owner), 1000 * 10 ** token.decimals());
}
function testMintByOwner() public {
vm.startPrank(owner);
token.mint(user, 500 * 10 ** token.decimals());
vm.stopPrank();
assertEq(token.balanceOf(user), 500 * 10 ** token.decimals());
}
function testMintByNonOwnerShouldFail() public {
vm.startPrank(user); // 非 owner 地址尝试 mint
vm.expectRevert("Only owner can mint");
token.mint(user, 100 * 10 ** token.decimals());
vm.stopPrank();
}
function testBurn() public {
uint256 initialBalance = token.balanceOf(owner);
vm.startPrank(owner);
token.burn(100 * 10 ** token.decimals());
vm.stopPrank();
assertEq(token.balanceOf(owner), initialBalance - 100 * 10 ** token.decimals());
}
}
运行测试:
forge test
如果一切正常,您将看到所有测试通过。
6. 编译与部署
6.1 编译合约
forge build
编译后的合约字节码和 ABI 将生成在 out/ 目录下。
6.2 部署到本地测试网
首先启动一个本地 Anvil 节点(模拟以太坊网络):
anvil
Anvil 会启动一个本地节点并输出一些测试账户私钥和 RPC URL(默认为 http://127.0.0.1:8545)。
在新的终端中,使用 forge create 部署合约:
forge create --rpc-url http://127.0.0.1:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
src/SimpleToken.sol:SimpleToken \
--constructor-args 1000
请将 --private-key 替换为 Anvil 输出的一个测试账户私钥。命令执行后会输出合约地址。
6.3 使用 Cast 与合约交互
Cast 是 Foundry 的 CLI 工具,用于与合约交互、查询链上数据等。
查询代币名称:
cast call <合约地址> "name()(string)" --rpc-url http://127.0.0.1:8545
发送交易(mint):
cast send <合约地址> "mint(address,uint256)" <接收地址> 500 \
--rpc-url http://127.0.0.1:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
7. 高级功能与技巧
7.1 Gas 优化报告
Foundry 可以生成 Gas 使用报告,帮助优化合约:
forge test --gas-report
7.2 Fuzz Testing(模糊测试)
Foundry 内置了强大的模糊测试功能,可以自动生成随机输入来测试函数:
function testTransferFuzz(address to, uint256 amount) public {
vm.assume(to != address(0)); // 排除零地址
vm.assume(amount <= token.balanceOf(owner));
vm.startPrank(owner);
token.transfer(to, amount);
vm.stopPrank();
assertEq(token.balanceOf(to), amount);
}
7.3 Invariant Testing(不变性测试)
不变性测试用于验证合约在随机序列操作下某些属性始终成立:
function invariant_totalSupplyNeverDecreases() public {
assertGe(token.totalSupply(), previousTotalSupply);
}
运行不变性测试:
forge test --invariant
7.4 使用 OpenZeppelin 等依赖
在 foundry.toml 中添加依赖:
[dependencies]
openzeppelin-contracts = "5.0.0"
然后安装:
forge install OpenZeppelin/openzeppelin-contracts
8. 部署到测试网
8.1 配置环境变量
创建 .env 文件:
PRIVATE_KEY=你的私钥
ETHERSCAN_API_KEY=你的Etherscan API Key
RPC_URL_SEPOLIA=https://sepolia.infura.io/v3/你的项目ID
8.2 编写部署脚本
script/DeploySimpleToken.s.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../src/SimpleToken.sol";
contract DeploySimpleToken is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
SimpleToken token = new SimpleToken(1000);
vm.stopBroadcast();
console.log("SimpleToken deployed at:", address(token));
}
}
8.3 执行部署
source .env
forge script script/DeploySimpleToken.s.sol:DeploySimpleToken \
--rpc-url $RPC_URL_SEPOLIA \
--broadcast \
--verify \
-vvvv
9. 总结
Foundry 以其卓越的性能和开发者体验,正在成为 Solidity 智能合约开发的首选工具链。通过本文的实战演练,您已经掌握了:
- Foundry 环境的安装与配置
- 项目的创建与结构
- 合约的编写与测试(包括常规测试、模糊测试)
- 本地节点的使用与合约部署
- 与合约的 CLI 交互
- 测试网部署与验证
建议您进一步探索 Foundry 的文档,了解更多高级功能,如分叉测试(Fork Testing)、代码覆盖率(Coverage)和基准测试(Benchmarking),以构建更安全、更高效的智能合约。
下一步学习建议:
- 阅读 Foundry Book 官方文档
- 尝试集成 Chainlink 预言机等更复杂的合约模式
- 使用
forge fmt统一代码风格,forge doc生成文档
更多推荐
所有评论(0)