Web3:Foundry使用指南
Foundry目录
- 1. 前言
- 2. 什么是Foundry
- 3. 安装与环境配置
- 1. 安装工具
- 2. 重新加载 `.bashrc`
- 3. 检查环境变量 `PATH`
- 4. 手动运行 `foundryup`
- 4. Foundry的基本使用
- 1.创建一个新的Foundry项目
- 2. 编写智能合约
- 3. 编译智能合约
- 4. foundry.toml 主要作用
- 5.部署智能合约
- 5. Cli参考
- 1. forge
- 2. cast
- 3. anvil
- 3. chisel
- 6. 解读 Foundry 配置系统
- 1. foundry.toml 文件
- 2.`foundry.toml` 文件示例
- 3,Profile 选择和优先级
- 3.配置示例说明
- **基本配置**
- **Solidity 编译器设置**
- **以太坊 RPC URL**
- **文件和库配置**
- **EVM 配置**
- **调试和模型检查**
- **优化器和额外输出**
- **权限和文件系统配置**
- **RPC 端点配置**
- **Etherscan API 配置**
- 4. 完整的 foundry.toml 配置文件实例
1. 前言
在区块链开发中,智能合约是构建Web3应用的重要组成部分。为了提升智能合约的开发效率,Foundry应运而生。作为一个新兴的开发框架,Foundry不仅具备高效的编译和测试能力,还拥有强大的调试和性能分析工具。本教程将带你一步步了解Foundry的使用方法,帮助你快速上手Web3开发。
2. 什么是Foundry
Foundry是一个开源的智能合约开发工具包,它为开发者提供了智能合约的编译、部署、测试和调试功能。Foundry更注重性能和灵活性,特别适合进行高频率的合约测试和开发。
-
Foundry的优势:
- 速度: Foundry的编译和部署速度非常快,能够显著提高开发效率。
- 简洁的API: 提供简洁的命令行工具,便于快速启动项目和管理智能合约。
- 原生支持Rust语言: Foundry部分功能基于Rust开发,具有良好的跨平台兼容性。
3. 安装与环境配置
注:执行命令,必须打开 Git Bash
1. 安装工具
- 打开 Git Bash,执行命令:
curl -L https://foundry.paradigm.xyz | bash
2. 重新加载 .bashrc
由于安装过程中系统提示你需要执行 source /c/Users/pc/.bashrc
来刷新环境变量。首先尝试以下操作:
- 打开 Git Bash,执行命令:
source ~/.bashrc
然后尝试再次运行 foundryup
:
foundryup
3. 检查环境变量 PATH
如果 foundryup
仍然无法找到,检查一下 foundryup
是否正确添加到系统的 PATH
环境变量中。
- 执行以下命令,检查
foundryup
是否安装成功:
foundryup --version
- 执行以下命令,检查路径中是否包含
foundryup
安装目录:
echo $PATH
你应该看到 foundryup
所在目录,如果没有,可能需要手动将它添加到你的 PATH
环境变量中。
- 检查 Foundry 工具是否安装成功
forge --version
cast --version
anvil --version
chisel --version
4. 手动运行 foundryup
尝试手动找到 foundryup
可执行文件的位置。你可以使用以下命令来检查 foundryup
是否安装成功,并查看其路径:
which foundryup
4. Foundry的基本使用
一旦Foundry安装完成,你就可以开始创建Web3项目了。
1.创建一个新的Foundry项目
在终端中执行以下命令,生成一个新的项目目录。
forge init my-first-project
2. 编写智能合约
在src
目录中创建一个新的Solidity文件(如MyContract.sol
),并编写简单的智能合约。
示例:
pragma solidity ^0.8.0;contract MyContract {uint256 public count;function increment() public {count += 1;}
}
3. 编译智能合约
使用forge build
命令编译合约。
forge build
4. foundry.toml 主要作用
-
配置编译器
:你可以指定要使用的 Solidity 编译器的版本,以及其他编译选项。 -
优化设置
:用于控制 Solidity 合约的优化选项,例如优化程度、优化的次数等。 -
测试配置
:设置与测试相关的参数,例如超时设置和测试运行时环境。 -
自定义路径
:指定合约、测试、脚本等文件的路径。 -
配置外部库
:如果你在项目中使用了外部库,可以在 foundry.toml 中指定相关的路径和设置。
5.部署智能合约
使用forge deploy
命令进行部署。
forge deploy
5. Cli参考
1. forge
用法: forge <COMMAND>命令:bind 为智能合约生成 Rust 绑定bind-json 通过 JSON cheatcodes 生成项目结构的序列化/反序列化绑定build 构建项目的智能合约 [别名: b, compile]cache 管理 Foundry 缓存clean 移除构建产物和缓存目录 [别名: cl]clone 从 Etherscan 克隆合约compiler 编译器工具completions 生成 shell 完成脚本 [别名: com]config 显示当前配置 [别名: co]coverage 生成覆盖率报告create 部署智能合约 [别名: c]debug 作为脚本调试单个智能合约 [别名: d]doc 为项目生成文档eip712 为给定文件中的结构生成 EIP-712 结构编码flatten 将源文件及其所有导入扁平化为一个文件 [别名: f]fmt 格式化 Solidity 源文件geiger 检测项目及其依赖中不安全 cheat codes 的使用generate 生成脚手架文件generate-fig-spec 生成 Fig 自动补全规范 [别名: fig]help 打印此消息或给定子命令的帮助init 创建一个新的 Forge 项目inspect 获取关于智能合约的专门信息 [别名: in]install 安装一个或多个依赖 [别名: i]remappings 获取项目的自动推断重映射 [别名: re]remove 移除一个或多个依赖 [别名: rm]script 作为脚本运行智能合约,构建可以在链上发送的交易selectors 函数选择器工具 [别名: se]snapshot 创建每个测试的 gas 使用快照 [别名: s]soldeer Soldeer 依赖管理器test 运行项目的测试 [别名: t]tree 显示项目依赖图的树状可视化 [别名: tr]update 更新一个或多个依赖 [别名: u]verify-bytecode 验证已部署的字节码与其在 Etherscan 上的源代码 [别名: vb]verify-check 检查在 Etherscan 上的验证状态 [别名: vc]verify-contract 在 Etherscan 上验证智能合约 [别名: v]
2. cast
用法: cast <COMMAND>命令:4byte 从 https://openchain.xyz 获取给定选择器的函数签名 [别名: 4, 4b]4byte-decode 使用 https://openchain.xyz 解码 ABI 编码的 calldata [别名: 4d, 4bd]4byte-event 从 https://openchain.xyz 获取给定主题 0 的事件签名 [别名: 4e, 4be, topic0-event, t0e]abi-decode 解码 ABI 编码的输入或输出数据 [别名: ad, --abi-decode]abi-encode ABI 编码给定的函数参数,排除选择器 [别名: ae]access-list 为交易创建访问列表 [别名: ac, acl]address-zero 打印零地址 [别名: --address-zero, az]admin 获取 EIP-1967 管理账户 [别名: adm]age 获取区块的时间戳 [别名: a]balance 获取账户的 wei 余额 [别名: b]base-fee 获取区块的 basefee [别名: ba, fee, basefee]bind 从给定 ABI 生成 rust 绑定 [别名: bi]block 获取区块的信息 [别名: bl]block-number 获取最新的区块号 [别名: bn]call 在账户上执行调用而不发布交易 [别名: c]calldata ABI 编码带参数的函数 [别名: cd]calldata-decode 解码 ABI 编码的输入数据 [别名: --calldata-decode, cdd]chain 获取当前链的符号名称chain-id 获取以太坊链 ID [别名: ci, cid]client 获取当前客户端版本 [别名: cl]code 获取合约的运行时字节码 [别名: co]codehash 获取账户的 codehashcodesize 获取合约的运行时字节码大小 [别名: cs]completions 生成 shell 完成脚本 [别名: com]compute-address 从给定的 nonce 和部署者地址计算合约地址 [别名: ca]concat-hex 连接十六进制字符串 [别名: --concat-hex, ch]create2 使用 CREATE2 生成确定性合约地址 [别名: c2]decode-eof 解码 EOF 容器字节decode-transaction 解码原始签名的 EIP 2718 类型交易 [别名: dt, decode-tx]disassemble 将十六进制编码的字节码反汇编为单个/可读的操作码 [别名: da]estimate 估算交易的 gas 成本 [别名: e]etherscan-source 从 Etherscan 获取合约的源代码 [别名: et, src]find-block 获取与提供的时间戳最接近的区块号 [别名: f]format-bytes32-string 将字符串格式化为 bytes32 编码 [别名: --format-bytes32-string]from-bin 将二进制数据转换为十六进制数据 [别名: --from-bin, from-binx, fb]from-fixed-point 将定点数转换为整数 [别名: --from-fix, ff]from-rlp 解码 RLP 十六进制编码的数据 [别名: --from-rlp]from-utf8 将 UTF8 文本转换为十六进制 [别名: --from-ascii, --from-utf8, from-ascii, fu, fa]from-wei 将 wei 转换为 ETH 数量 [别名: --from-wei, fw]gas-price 获取当前 gas 价格 [别名: g]generate-fig-spec 生成 Fig 自动补全规范 [别名: fig]hash-message 根据 EIP-191 哈希消息 [别名: --hash-message, hm]hash-zero 打印零哈希 [别名: --hash-zero, hz]help 打印此消息或给定子命令的帮助implementation 获取 EIP-1967 实现账户 [别名: impl]index 计算映射中条目的存储槽 [别名: in]index-erc7201 按照 `ERC-7201: Namespaced Storage Layout` 指定计算存储槽 [别名: index7201, in7201]interface 从给定 ABI 生成 Solidity 接口 [别名: i]keccak 使用 Keccak-256 哈希任意数据 [别名: k, keccak256]logs 按签名或主题获取日志 [别名: l]lookup-address 执行 ENS 反向查找 [别名: la]max-int 打印给定整数类型的最大值 [别名: --max-int, maxi]max-uint 打印给定整数类型的最大值 [别名: --max-uint, maxu]min-int 打印给定整数类型的最小值 [别名: --min-int, mini]mktx 构建并签名交易 [别名: m]namehash 计算名称的 ENS namehash [别名: na, nh]nonce 获取账户的 nonce [别名: n]parse-bytes32-address 从 bytes32 编码解析校验和地址 [别名: --parse-bytes32-address]parse-bytes32-string 从 bytes32 编码解析字符串 [别名: --parse-bytes32-string]pretty-calldata 美化打印 calldata [别名: pc]proof 为给定存储槽生成存储证明 [别名: pr]publish 将原始交易发布到网络 [别名: p]receipt 获取交易的收据 [别名: re]resolve-name 执行 ENS 查找 [别名: rn]rpc 执行原始 JSON-RPC 请求 [别名: rp]run 在本地环境中运行已发布的交易并打印跟踪 [别名: r]selectors 从字节码中提取函数选择器和参数 [别名: sel]send 签名并发布交易 [别名: s]shl 执行左移操作shr 执行右移操作sig 获取函数的选择器 [别名: si]sig-event 从事件字符串生成事件签名 [别名: se]storage 获取合约存储槽的原始值 [别名: st]storage-root 获取账户的存储根 [别名: sr]to-ascii 将十六进制数据转换为 ASCII 字符串 [别名: --to-ascii, tas, 2as]to-base 将一个基数的数字转换为另一个基数 [别名: --to-base, --to-radix, to-radix, tr, 2r]to-bytes32 将十六进制数据右填充到 32 字节 [别名: --to-bytes32, tb, 2b]to-check-sum-address 将地址转换为校验和格式 (EIP-55) [别名: --to-checksum-address, --to-checksum, to-checksum, ta, 2a]to-dec 将一个基数的数字转换为十进制 [别名: --to-dec, td, 2d]to-fixed-point 将整数转换为定点数 [别名: --to-fix, tf, 2f]to-hex 将一个基数的数字转换为另一个基数 [别名: --to-hex, th, 2h]to-hexdata 将输入标准化为小写,0x 前缀的十六进制 [别名: --to-hexdata, thd, 2hd]to-int256 将数字转换为十六进制编码的 int256 [别名: --to-int256, ti, 2i]to-rlp RLP 编码十六进制数据或十六进制数据数组 [别名: --to-rlp]to-uint256 将数字转换为十六进制编码的 uint256 [别名: --to-uint256, tu, 2u]to-unit 将 ETH 数量转换为另一单位 (ether, gwei 或 wei) [别名: --to-unit, tun, 2un]to-utf8 将十六进制数据转换为 utf-8 字符串 [别名: --to-utf8, tu8, 2u8]to-wei 将 ETH 数量转换为 wei [别名: --to-wei, tw, 2w]tx 获取交易的信息 [别名: t]upload-signature 将给定的签名上传到 https://openchain.xyz [别名: ups]wallet 钱包管理工具 [别名: w]
3. anvil
# 启动本地以太坊节点,生成 10 个账户,余额为 10000 以太币,监听 8545 端口
anvil --accounts 10 --balance 10000 --port 8545# 启动本地节点,连接到远程 RPC 端点,从指定的区块号获取状态
anvil --fork-url http://localhost:8545 --fork-block-number 1400000# 禁用自动挖矿,按需挖矿
anvil --no-mining# 将链的状态转储到指定文件(例如 state.json)
anvil --dump-state ./state.json# 从远程端点加载链的状态
anvil --load-state ./state.json# 设置区块生成的时间间隔为 10 秒
anvil --block-time 10# 设置每个开发账户的初始余额为 10000 以太币
anvil --balance 10000# 设置 RPC URL(例如连接到远程以太坊节点)
anvil --rpc-url http://localhost:8545# 启用硬分叉(例如 'shanghai')
anvil --hardfork shanghai# 禁用 CORS(跨域资源共享)
anvil --no-cors# 启动时不打印任何内容,不输出日志
anvil --silent# 设置链的最大状态持久化数量
anvil --max-persisted-states 100# 设置链的 gas 限制
anvil --gas-limit 10000000# 设置链的 gas 价格
anvil --gas-price 1000000000# 启用 Alphanet 功能
anvil --alphanet# 启用 Optimism 链
anvil --optimism# 打印 Anvil 的版本信息
anvil --version
3. chisel
# 列出所有缓存的会话
chisel list# 加载一个缓存的会话
chisel load <SESSION_NAME># 查看缓存会话的源代码
chisel view <SESSION_NAME># 清除缓存目录中的所有缓存会话
chisel clear-cache# 简单评估一个命令而不进入 REPL
chisel eval "<COMMAND>"# 打印帮助信息
chisel help# 打印 chisel 的版本信息
chisel --version# 禁用默认的 Vm 导入
chisel --no-vm# 指定导入 Solidity 文件的路径,充当 REPL 的前奏
chisel --prelude <PRELUDE_PATH># 强制清除缓存并重新编译
chisel --force# 禁用缓存
chisel --no-cache# 跳过指定文件的构建
chisel --skip <SKIP_PATTERN># 设置预链接的库
chisel --libraries <LIBRARIES_PATH># 设置编译器选项,如指定 solc 版本
chisel --use <SOLC_VERSION># 不自动检测 solc 版本
chisel --no-auto-detect# 使用 Yul 中间表示法编译
chisel --via-ir# 不附加任何元数据到字节码
chisel --no-metadata# 设置目标 EVM 版本
chisel --evm-version <VERSION># 激活 Solidity 优化器
chisel --optimize <true|false># 设置优化运行次数
chisel --optimizer-runs <RUNS># 生成额外的编译输出(例如 assembly、metadata)
chisel --extra-output <SELECTOR>...# 设置输出目录,保存合约工件
chisel -o <PATH># 设置项目根目录路径
chisel --root <PROJECT_ROOT_PATH># 设置合约源目录
chisel -C --contracts <CONTRACTS_PATH># 设置项目的重映射
chisel -R --remappings <REMAPPINGS># 设置编译器缓存路径
chisel --cache-path <CACHE_PATH># 从远程端点获取状态,而不是从空状态开始
chisel -f --fork-url <URL># 从远程端点获取特定区块号的状态
chisel --fork-block-number <BLOCK_NUMBER># 设置交易来源地址
chisel --tx-origin <ADDRESS># 设置区块时间戳
chisel --block-timestamp <TIMESTAMP># 设置区块号
chisel --block-number <BLOCK_NUMBER># 设置每次 EVM 执行的内存限制(以字节为单位)
chisel --memory-limit <MEMORY_LIMIT># 启用隔离模式,在单独的 EVM 上下文中执行顶层调用
chisel --isolate# 启用 Alphanet 功能
chisel --alphanet
6. 解读 Foundry 配置系统
1. foundry.toml 文件
Foundry 的工具会搜索当前工作目录及其父目录中的 foundry.toml
配置文件。如果没有找到,工具会继续搜索直至根目录。此外,全局配置文件的默认位置是 ~/.foundry/foundry.toml
,这也是会被检查的地方。如果你通过环境变量 FOUNDRY_CONFIG
指定了配置文件的路径,则该路径会被直接使用,不再进行搜索。
foundry.toml
文件支持定义多个 profiles,每个 profile 配置一组参数。每个顶级键代表一个 profile,文件是 嵌套 的,意味着每个顶级键下配置的内容都属于该 profile 的设置。
2.foundry.toml
文件示例
## 默认配置 - 对所有 profile 有效
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.10" # 使用本地安装的 solc 编译器,路径可以指定
eth-rpc-url = "https://mainnet.infura.io"## 当选择 `hardhat` profile 时的配置
[profile.hardhat]
src = "contracts"
out = "artifacts"
libs = ["node_modules"]## 当选择 `spells` profile 时的配置
[profile.spells]
# --snip-- 更多设置
3,Profile 选择和优先级
当决定使用哪个 profile 时,Foundry 的配置系统会按以下顺序逐一检查配置:
Config::default()
:提供默认的配置值。foundry.toml
或通过FOUNDRY_CONFIG
环境变量指定的 TOML 文件。- 环境变量:使用
FOUNDRY_
或DAPP_
前缀的环境变量(它们对应的配置参数值)。
- 如果
FOUNDRY_PROFILE
环境变量未设置,则默认使用"default"
profile。 - 环境变量优先于
foundry.toml
中的配置。
3.配置示例说明
foundry.toml
配置文件允许设置非常多的参数,下面是一些重要的选项:
基本配置
src
:指定 Solidity 合约源码的目录,默认是"src"
。test
:指定测试文件的目录,默认是"test"
。script
:指定脚本文件的目录,默认是"script"
。out
:指定编译输出目录,默认是"out"
。libs
:外部库的路径,默认是"lib"
。
Solidity 编译器设置
solc
:指定使用的 Solidity 编译器版本,或者你可以提供本地solc
的路径。auto_detect_solc
:是否自动检测solc
版本,默认为true
。optimizer
:是否启用优化,默认为false
。optimizer_runs
:优化运行的次数,默认为 200。
以太坊 RPC URL
eth_rpc_url
:指定以太坊节点的 RPC 地址(如https://mainnet.infura.io
),用于与以太坊网络进行交互。
文件和库配置
libraries
:可以链接外部 Solidity 库,格式为"<path to lib>:<lib name>:<address>"
。remappings
:定义源代码中的 remappings,以便引用外部合约。cache
:启用缓存,默认为true
。
EVM 配置
gas_limit
:设置区块的 gas 限制。gas_price
:设置 gas 价格。block_timestamp
:设置区块时间戳。
调试和模型检查
model_checker
:启用 Solidity 内置的模型检查器,用于验证合约的安全性。verbosity
:设置 EVM 的详细程度,数值越大,输出信息越多。
优化器和额外输出
extra_output
:设置编译时的额外输出内容,例如metadata
、evm.assembly
。sparse_mode
:启用稀疏模式,仅生成必需的字节码,减少编译时间。
权限和文件系统配置
fs_permissions
:配置文件系统的访问权限,控制是否允许读取或写入某些文件。
RPC 端点配置
rpc_endpoints
配置允许你为多个 RPC 服务设置别名。这样,你可以使用别名来引用不同的端点。
[rpc_endpoints]
optimism = "https://optimism.alchemyapi.io/v2/1234567"
mainnet = "${RPC_MAINNET}"
goerli = "https://eth-goerli.alchemyapi.io/v2/${GOERLI_API_KEY}"
Etherscan API 配置
etherscan
部分配置了用于与 Etherscan 交互的 API 密钥,支持多个链和环境变量。
[etherscan]
mainnet = { key = "${ETHERSCAN_MAINNET_KEY}" }
optimism = { key = "1234576", chain = 42 }
4. 完整的 foundry.toml 配置文件实例
## 默认配置 - 对所有 Profile 有效
[profile.default]
# Solidity 合约源码目录
src = "src"
# 测试文件目录
test = "test"
# 脚本文件目录
script = "script"
# 编译输出目录
out = "out"
# 外部库的路径
libs = ["lib"]
# 是否自动检测 remappings
auto_detect_remappings = true
# 存放 remappings 的列表
remappings = []
# 用于链接的库
libraries = []
# 启用缓存,默认为 true
cache = true
# 缓存路径
cache_path = "cache"
# 广播目录
broadcast = "broadcast"
# solc 额外的允许路径
allow_paths = []
# solc 额外的 include 路径
include_paths = []
# 是否强制重新编译
force = false
# 使用的 EVM 版本
evm_version = "prague"
# 是否启用 gas 报告,默认为 "所有"
gas_reports = ["*"]
gas_reports_ignore = []
# 编译器版本,若为空则自动检测
auto_detect_solc = true
solc = "0.8.10" # 指定编译器版本
offline = false
optimizer = false
optimizer_runs = 200# 模型检查配置
model_checker = { contracts = { 'src/Contract.sol' = ['Contract'] },engine = 'chc',targets = ['assert'],timeout = 10000
}# EVM 相关设置
verbosity = 2
eth_rpc_url = "https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID"# Etherscan API Key 设置
etherscan_api_key = "YOUR_ETHERSCAN_API_KEY"# 忽略的 solc 警告
ignored_error_codes = ["license", "code-size"]
ignored_warnings_from = ["path_to_ignore"]
deny_warnings = false# 线程数,0 表示使用逻辑核心数
threads = 0
# 是否显示测试执行进度
show_progress = true# 文件系统权限配置
fs_permissions = [{ access = "read-write", path = "./out" }]
assertions_revert = true
legacy_assertions = false## 专用于 `hardhat` Profile 配置
[profile.hardhat]
src = "contracts"
out = "artifacts"
libs = ["node_modules"]
eth_rpc_url = "https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID"
auto_detect_solc = true
solc = "0.8.10"
cache = true## 专用于 `spells` Profile 配置
[profile.spells]
src = "spells"
out = "spell_out"
libs = ["lib/spells"]
solc = "0.8.10"
eth_rpc_url = "https://kovan.infura.io/v3/YOUR_INFURA_PROJECT_ID"## RPC 端点配置
[rpc_endpoints]
optimism = "https://optimism.alchemyapi.io/v2/1234567"
mainnet = "${RPC_MAINNET}"
goerli = "https://eth-goerli.alchemyapi.io/v2/${GOERLI_API_KEY}"## 默认的测试合约调用者配置
sender = '0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38'
tx_origin = '0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38'
initial_balance = '0xffffffffffffffffffffffff'
block_number = 0
fork_block_number = 0
chain_id = 1## Gas 限制与费用设置
gas_limit = 1073741824
gas_price = 0
block_base_fee_per_gas = 0
block_coinbase = '0x0000000000000000000000000000000000000000'
block_timestamp = 0
block_difficulty = 0
block_prevrandao = '0x0000000000000000000000000000000000000000'
block_gas_limit = 30000000
memory_limit = 134217728## 额外输出设置
extra_output = ["metadata"]
extra_output_files = []# 强制启用 Create2 工厂
always_use_create_2_factory = false
# 是否启用 FFI cheatcode
ffi = false## 模型检查器设置
[profile.default.model_checker]
contracts = { 'src/Contract.sol' = ['Contract'] }
engine = 'chc'
timeout = 10000
targets = ['assert']## 配置代码格式化
[fmt]
line_length = 100
tab_width = 2
bracket_spacing = true## 构建和构建信息设置
build_info = true
build_info_path = "build-info"
root = "root"