Solidity入门(15)- Foundry框架

文章目录

  • [1. 为什么选择Foundry](#1. 为什么选择Foundry)
    • [1.1 Foundry的诞生背景](#1.1 Foundry的诞生背景)
    • [1.2 Foundry的核心优势](#1.2 Foundry的核心优势)
    • [1.3 适用场景](#1.3 适用场景)
  • [2. Foundry vs Hardhat 对比](#2. Foundry vs Hardhat 对比)
    • [2.1 技术架构对比](#2.1 技术架构对比)
    • [2.2 功能特性对比](#2.2 功能特性对比)
    • [2.4 选择建议](#2.4 选择建议)
  • [3. Foundry安装与配置](#3. Foundry安装与配置)
    • [3.1 系统要求](#3.1 系统要求)
    • [3.2 安装步骤](#3.2 安装步骤)
    • [3.3 更新和卸载](#3.3 更新和卸载)
    • [3.4 常见安装问题](#3.4 常见安装问题)
    • [3.5 环境变量配置](#3.5 环境变量配置)
  • [4. 工具链概览](#4. 工具链概览)
    • [4.1 Forge - 核心开发工具](#4.1 Forge - 核心开发工具)
    • [4.2 Cast - 命令行交互工具](#4.2 Cast - 命令行交互工具)
    • [4.3 Anvil - 本地以太坊节点](#4.3 Anvil - 本地以太坊节点)
  • [5. 项目初始化](#5. 项目初始化)
    • [5.1 创建项目](#5.1 创建项目)
    • [5.2 项目结构](#5.2 项目结构)
    • [5.3 目录说明](#5.3 目录说明)
    • [5.4 示例文件](#5.4 示例文件)
  • [6. foundry.toml配置详解](#6. foundry.toml配置详解)
    • [6.1 配置文件格式](#6.1 配置文件格式)
    • [6.2 基础配置项](#6.2 基础配置项)
    • [6.3 网络配置](#6.3 网络配置)
    • [6.4 多Profile配置](#6.4 多Profile配置)
    • [6.5 测试配置](#6.5 测试配置)
    • [6.6 编译器配置](#6.6 编译器配置)
    • [6.7 完整配置示例](#6.7 完整配置示例)
  • [7. 网络配置详解](#7. 网络配置详解)
    • [7.1 环境变量设置](#7.1 环境变量设置)
    • [7.2 foundry.toml网络配置](#7.2 foundry.toml网络配置)
    • [7.3 网络类型](#7.3 网络类型)
    • [7.4 安全提示](#7.4 安全提示)
    • [7.5 使用网络](#7.5 使用网络)
  • [8. Forge编译合约](#8. Forge编译合约)
    • [8.1 基础编译](#8.1 基础编译)
    • [8.2 编译输出](#8.2 编译输出)
    • [8.3 编译选项](#8.3 编译选项)
    • [8.4 编译优化](#8.4 编译优化)
    • [8.5 编译产物](#8.5 编译产物)
    • [8.6 常见编译问题](#8.6 常见编译问题)
  • [9. 编写部署脚本](#9. 编写部署脚本)
    • [9.1 部署脚本基础](#9.1 部署脚本基础)
    • [9.2 高级部署脚本](#9.2 高级部署脚本)
    • [9.3 脚本执行](#9.3 脚本执行)
    • [9.4 指定私钥](#9.4 指定私钥)
    • [9.5 多网络部署](#9.5 多网络部署)
    • [9.6 Cheatcodes使用](#9.6 Cheatcodes使用)
  • [10. Anvil本地测试网](#10. Anvil本地测试网)
    • [10.1 启动Anvil](#10.1 启动Anvil)
    • [10.2 自定义配置](#10.2 自定义配置)
    • [10.3 分叉主网](#10.3 分叉主网)
    • [10.4 状态管理](#10.4 状态管理)
    • [10.5 与脚本和测试连接](#10.5 与脚本和测试连接)
    • [10.6 Anvil的优势](#10.6 Anvil的优势)
  • [11. 测试网部署](#11. 测试网部署)
    • [11.1 部署前准备](#11.1 部署前准备)
    • [11.2 部署流程](#11.2 部署流程)
    • [11.3 完整部署命令](#11.3 完整部署命令)
    • [11.4 部署后验证](#11.4 部署后验证)
    • [11.5 常见部署问题](#11.5 常见部署问题)
    • [11.6 最佳实践](#11.6 最佳实践)
  • [12. 合约验证和交互](#12. 合约验证和交互)
    • [12.1 合约验证](#12.1 合约验证)
  • 编码构造函数参数
    • [12.2 查询链上数据](#12.2 查询链上数据)
    • [12.3 调用只读函数](#12.3 调用只读函数)
    • [12.4 发送交易](#12.4 发送交易)
    • [12.5 Gas估算](#12.5 Gas估算)
    • [12.6 编码解码](#12.6 编码解码)
    • [12.7 签名和验证](#12.7 签名和验证)
    • [12.8 完整交互流程](#12.8 完整交互流程)
  • [13. Cast命令行工具详解](#13. Cast命令行工具详解)
    • [13.1 链上查询](#13.1 链上查询)
    • [13.2 合约交互](#13.2 合约交互)
    • [13.3 数据编码](#13.3 数据编码)
    • [13.4 地址和私钥操作](#13.4 地址和私钥操作)
    • [13.5 数值转换](#13.5 数值转换)
    • [13.6 签名和验证](#13.6 签名和验证)
    • [13.7 交易相关](#13.7 交易相关)
    • [13.8 日志和事件](#13.8 日志和事件)
    • [13.9 实用技巧](#13.9 实用技巧)
  • [14. 最佳实践](#14. 最佳实践)
    • [14.1 配置管理](#14.1 配置管理)
    • [14.2 部署流程](#14.2 部署流程)
    • [14.3 测试实践](#14.3 测试实践)
    • [14.4 性能优化](#14.4 性能优化)
    • [14.5 安全实践](#14.5 安全实践)
  • [15. 实战演示](#15. 实战演示)
    • [15.1 演示环境准备](#15.1 演示环境准备)
    • [15.2 创建和初始化项目](#15.2 创建和初始化项目)
    • [15.3 查看和编译合约](#15.3 查看和编译合约)
    • [15.4 运行测试](#15.4 运行测试)
    • [15.5 启动Anvil本地节点](#15.5 启动Anvil本地节点)
    • [15.6 编写和运行部署脚本](#15.6 编写和运行部署脚本)
    • [16.7 使用Cast与合约交互](#16.7 使用Cast与合约交互)
    • [15.8 查询链信息](#15.8 查询链信息)
    • [15.9 演示总结](#15.9 演示总结)

1. 为什么选择Foundry

1.1 Foundry的诞生背景

在智能合约开发领域,开发工具的性能一直是开发者关注的焦点。随着DeFi、NFT等应用的快速发展,智能合约项目变得越来越复杂,合约数量不断增加,测试用例也日益庞大。传统的开发工具如Hardhat、Truffle等基于JavaScript/TypeScript,虽然功能完善,但在处理大型项目时,编译和测试速度往往成为瓶颈。

在实际开发中,开发者经常遇到这样的问题:修改一个合约后,需要等待几分钟才能看到编译结果;运行完整的测试套件可能需要十几分钟甚至更长时间。这不仅降低了开发效率,也影响了开发体验。特别是在需要频繁迭代和测试的场景下,这种性能瓶颈更加明显。

Foundry应运而生,它由Paradigm开发,用Rust编写,专注于提供极致的性能体验。Paradigm作为区块链领域的知名投资和研究机构,深刻理解开发者在实际工作中遇到的痛点,因此设计并开发了Foundry这个工具。Foundry的设计理念是:快速、简单、强大。它不仅仅是一个工具,更是一种全新的开发体验,让开发者能够专注于代码本身,而不是等待工具完成工作。

1.2 Foundry的核心优势

  1. 极致的性能:

性能是Foundry最突出的特点。由于使用Rust编写,Foundry能够充分利用系统资源,实现极高的执行效率。在实际测试中,Foundry的编译速度比Hardhat快10到100倍,这意味着一个需要几分钟编译的项目,使用Foundry可能只需要几秒钟。测试执行速度同样快10到100倍,这对于需要频繁运行测试的开发流程来说,节省的时间是巨大的。

除了速度优势,Foundry的内存占用也更低。在处理大型项目时,Hardhat可能需要几GB的内存,而Foundry通常只需要几百MB。这不仅降低了系统资源消耗,也使得在资源受限的环境中运行成为可能。

对于大型项目,这些性能优势尤其明显。当项目包含数百个合约文件、数千个测试用例时,性能差异会变得非常显著。开发者可以更快地获得反馈,更频繁地进行测试,从而提升代码质量和开发效率。

  1. Solidity原生测试:

Foundry的一个革命性特性是支持直接用Solidity编写测试。这意味着你不需要在JavaScript/TypeScript和Solidity之间切换语言上下文,所有的代码都使用同一种语言。这不仅降低了学习成本,也减少了出错的可能性。

使用Solidity编写测试带来了更好的类型安全。在JavaScript中,类型检查是有限的,很多错误只能在运行时发现。而在Solidity中,编译器会在编译时进行严格的类型检查,能够提前发现很多潜在问题。

此外,测试代码与合约代码使用同一语言,使得测试代码更容易理解和维护。开发者可以直接使用合约中的类型、结构和接口,不需要进行类型转换或适配。这种一致性也使得测试代码能够更好地反映合约的实际行为。

  1. 内置强大功能:

Foundry内置了许多在其他工具中需要额外安装插件才能获得的功能。模糊测试(Fuzz Testing)是其中之一,它能够自动生成大量随机输入来测试合约,发现边界情况和潜在漏洞。这在传统测试中需要手动编写大量测试用例才能实现。

Gas报告功能也是内置的,每次运行测试都会自动生成详细的Gas消耗报告,帮助开发者识别高Gas消耗的函数,进行针对性优化。覆盖率分析同样内置,可以清楚地看到哪些代码被测试覆盖,哪些代码需要补充测试。

这些内置功能不仅减少了配置工作,也确保了功能的稳定性和一致性。不需要担心插件版本不兼容、配置冲突等问题。

  1. 完整的工具链:

Foundry提供了三个核心工具,形成了一个完整的开发工具链。Forge是核心开发工具,负责编译、测试和部署。Cast是命令行交互工具,可以方便地与区块链进行交互,无需编写代码。Anvil是本地以太坊节点,提供了高性能的本地测试环境。

这三个工具配合使用,覆盖了从开发到部署的完整流程。开发者可以在本地使用Anvil进行快速测试,使用Forge进行编译和测试,使用Cast进行链上交互和调试。这种一体化的设计使得开发流程更加顺畅。

  1. 活跃的社区:

Foundry由Paradigm维护,这是一个在区块链领域具有重要影响力的机构。Paradigm不仅提供资金支持,也积极参与开发和维护工作,确保Foundry能够持续更新和改进。

社区非常活跃,有大量的开发者在使用和贡献。GitHub上有丰富的讨论和问题解答,Discord上有活跃的交流。文档也非常完善,不仅有详细的API文档,也有大量的教程和示例。

更重要的是,Foundry已经被众多知名项目采用,包括Uniswap V3、Solmate等。这些项目的使用不仅证明了Foundry的可靠性,也为新用户提供了丰富的学习资源。

1.3 适用场景

Foundry特别适合以下场景,了解这些场景有助于你判断是否应该选择Foundry:

  • 纯合约开发:如果你主要专注于智能合约开发,不需要频繁与前端交互,那么Foundry是理想的选择。Foundry专注于合约开发,提供了完整的工具链,能够满足大部分合约开发需求。虽然前端集成需要额外工具,但对于纯合约开发来说,这不是问题。

  • 大型项目:当项目包含大量合约文件时,性能优势会变得非常明显。一个包含数百个合约的项目,使用Hardhat可能需要几十分钟来编译和测试,而使用Foundry可能只需要几分钟。这种时间节省在大型项目中是巨大的,能够显著提升开发效率。

  • 复杂测试场景:如果你需要模糊测试、属性测试等高级测试功能,Foundry是更好的选择。这些功能在Foundry中是内置的,使用起来非常方便。而在其他工具中,可能需要安装和配置多个插件,过程复杂且容易出错。

  • 性能敏感项目:对于需要频繁编译和测试的项目,性能差异会直接影响开发体验。如果你每天需要运行测试数十次甚至上百次,那么使用Foundry节省的时间是巨大的。这种性能优势在CI/CD流程中同样重要,能够加快构建和测试速度。

  • Gas优化:Gas优化是智能合约开发中的重要环节。Foundry提供了详细的Gas报告和分析工具,能够帮助开发者识别高Gas消耗的函数,进行针对性优化。这对于需要严格控制Gas消耗的项目来说非常重要。

当然,Foundry并不是万能的。如果你的项目需要频繁与前端交互,或者需要丰富的插件生态,那么Hardhat可能更适合。但总的来说,对于大多数智能合约开发场景,Foundry都是一个优秀的选择。

2. Foundry vs Hardhat 对比

2.1 技术架构对比

实现语言:

性能对比:

  • 编译速度:Foundry比Hardhat快10到50倍
  • 测试速度:Foundry比Hardhat快10到100倍
  • 内存占用:Foundry更低

2.2 功能特性对比

测试框架:

本地节点:

部署脚本:

2.3 生态系统对比

前端集成:

Hardhat:前端集成更好,插件生态更丰富,更适合全栈开发

Foundry:专注于合约开发,前端集成需要额外工具

插件生态:

Hardhat:插件生态非常丰富,有大量社区插件

Foundry:插件相对较少,但核心功能已内置

学习曲线:

Hardhat:如果熟悉JavaScript/TypeScript,学习曲线较平缓

Foundry:需要学习Solidity测试,但概念更统一

2.4 选择建议

选择Foundry的场景:

  • 主要做纯合约开发
  • 需要极致性能
  • 有复杂的测试场景
  • 需要模糊测试功能
  • 对Gas优化有严格要求

选择Hardhat的场景:

  • 需要频繁与前端交互
  • 需要丰富的插件生态
  • 团队更熟悉JavaScript/TypeScript
  • 需要全栈开发支持

两者并存:

实际上,这两个工具可以并存,根据项目需求灵活选择:

  • 使用Foundry进行合约开发和测试

  • 使用Hardhat进行前端集成和部署

  • 根据具体任务选择最合适的工具

3. Foundry安装与配置

3.1 系统要求

在安装Foundry之前,需要确保系统满足以下要求。虽然Foundry的安装过程相对简单,但了解系统要求有助于避免安装过程中的问题。

必需环境:

  • Rust环境是Foundry运行的基础,因为Foundry本身是用Rust编写的。好消息是,安装脚本会自动处理Rust的安装,你不需要手动安装。安装脚本会检测系统环境,自动下载和安装合适版本的Rust编译器。这个过程可能需要几分钟时间,取决于网络速度。

  • 足够的磁盘空间也是必需的。Foundry本身需要约500MB的磁盘空间,这包括工具本身、编译缓存、依赖库等。如果你计划安装多个版本的Solidity编译器,或者需要存储大量的编译产物,可能需要更多的空间。建议至少预留1GB的磁盘空间,以确保有足够的空间进行开发工作。

推荐环境:

  • 虽然Node.js不是必需的,但如果你需要与前端工具协作,或者使用一些基于Node.js的工具,那么安装Node.js 18或更高版本是推荐的。Node.js 18是一个长期支持版本,稳定性和兼容性都很好。

  • Git是安装依赖库所必需的。Foundry使用Git子模块来管理依赖库,因此需要Git来克隆和更新这些库。如果你还没有安装Git,需要先安装它。大多数系统都预装了Git,如果没有,可以从Git官网下载安装。

3.2 安装步骤

Foundry的安装过程非常简单,只需要两个步骤。整个过程是自动化的,安装脚本会处理大部分工作。

步骤1:下载并运行安装脚本

首先,需要下载并运行Foundry的安装脚本。在终端中执行以下命令:

go 复制代码
curl -L https://foundry.paradigm.xyz | bash

这个命令会执行以下操作:

  • 从Foundry官网下载最新的安装脚本
  • 自动检测你的系统环境(操作系统类型、架构等)
  • 根据系统环境选择合适的方式进行安装
  • 自动安装必要的依赖,包括Rust编译器
  • 安装脚本是智能的,它会检测你的系统是否已经安装了Rust。如果已经安装,它会使用现有的Rust环境;如果没有,它会自动下载并安装Rust。这个过程可能需要几分钟时间,特别是首次安装Rust时,因为需要下载和编译Rust工具链。

安装过程中,脚本会显示详细的进度信息,包括下载进度、编译进度等。如果遇到网络问题或权限问题,脚本会给出相应的错误提示。

windows 如果遇到不能创建path的问题,建议以管理员的方式打开命令行试试

步骤2:安装或更新Foundry工具链

安装脚本执行完成后,需要运行foundryup命令来安装或更新Foundry工具链:

go 复制代码
foundryup

这个命令会执行以下操作:

  • 检查当前安装的Foundry版本
  • 从GitHub下载最新版本的forge、cast和anvil工具
  • 如果已经安装,则更新到最新版本
  • 如果首次安装,则安装最新版本
  • foundryup命令非常智能,它只会下载和安装必要的组件,不会重复下载已经存在的文件。如果网络连接中断,可以重新运行foundryup,它会从中断的地方继续。

步骤3:验证安装

安装完成后,需要验证安装是否成功。在终端中依次运行以下命令:

go 复制代码
forge --version
cast --version
anvil --version

如果安装成功,你会看到类似以下的输出:

go 复制代码
forge 0.2.0 (abc123def456 2024-11-15T10:30:00.000000000Z)
cast 0.2.0 (abc123def456 2024-11-15T10:30:00.000000000Z)
anvil 0.2.0 (abc123def456 2024-11-15T10:30:00.000000000Z)

这些输出显示了每个工具的版本号和编译信息。版本号格式为:主版本号.次版本号.修订版本号,后面跟着的是Git提交哈希和编译时间。

如果命令无法执行或显示"command not found"错误,可能是PATH环境变量没有正确配置。需要检查并配置PATH环境变量,确保~/.foundry/bin目录在PATH中。
也可以试试在git bash 里面执行

3.3 更新和卸载

更新Foundry:

go 复制代码
foundryup

卸载Foundry:

go 复制代码
rm -rf ~/.foundry

3.4 常见安装问题

问题1:curl命令失败

原因:网络问题或代理设置

解决方案:

  • 检查网络连接
  • 配置代理:export https_proxy=http://proxy:port
  • 使用国内镜像(如果有)

问题2:权限错误

原因:PATH配置问题或权限不足

解决方案:

  • 检查PATH环境变量:echo $PATH
  • 确保~/.foundry/bin在PATH中
  • 如果使用sudo安装,检查权限配置

问题3:Rust安装失败

原因:网络问题或系统兼容性

解决方案:

  • 手动安装Rust:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • 配置Rust环境:source $HOME/.cargo/env
  • 重新运行foundryup

问题4:编译错误

原因:系统缺少编译工具

解决方案:

  • macOS:安装Xcode Command Line Tools:xcode-select --install
  • Linux:安装build-essential:sudo apt-get install build-essential
  • Windows:安装Visual Studio Build Tools

3.5 环境变量配置

安装完成后,确保Foundry工具在PATH中:

go 复制代码
# 检查PATH
echo $PATH | grep foundry

# 如果不在PATH中,添加到~/.bashrc或~/.zshrc
export PATH="$HOME/.foundry/bin:$PATH"

官网参考

4. 工具链概览

4.1 Forge - 核心开发工具

Forge是Foundry的核心开发工具,也是整个工具链中最重要的组件。它提供了合约编译、测试执行、脚本运行、部署管理、Gas分析和覆盖率报告等完整功能。可以说,Forge是Foundry的灵魂,大部分开发工作都是通过Forge完成的。

主要命令:

Forge提供了丰富的命令行工具,每个命令都有特定的用途:

go 复制代码
forge init          # 初始化项目,创建基础项目结构
forge build         # 编译合约,生成字节码和ABI
forge test          # 运行测试,执行所有测试用例
forge script        # 运行部署脚本,可以部署合约到网络
forge clean         # 清理构建产物,释放磁盘空间
forge install       # 安装依赖库,从GitHub克隆库到lib目录
forge update        # 更新依赖,获取最新版本的依赖库

这些命令设计得非常直观,命令名称直接反映了功能。例如,forge build用于构建项目,forge test用于运行测试。这种设计降低了学习成本,使得开发者能够快速上手。

核心功能:

  1. 编译系统:
  • Forge的编译系统是其核心功能之一。它支持多版本Solidity,这意味着你可以在同一个项目中使用不同版本的Solidity编译器。这对于需要维护多个版本合约的项目来说非常有用。

  • 增量编译是Forge的一个重要特性。它只编译修改过的文件,而不是每次都重新编译整个项目。这大大提高了编译速度,特别是在大型项目中。当你只修改了一个文件时,Forge会智能地只编译这个文件及其依赖,而不是重新编译所有文件。

  • 自动管理依赖关系是另一个重要特性。当你安装一个依赖库时,Forge会自动处理依赖关系,确保所有依赖都能正确解析。它使用Git子模块来管理依赖,这种方式既简单又可靠。

  • 字节码优化功能允许你控制编译优化级别。你可以选择优化部署成本或执行成本,这取决于你的使用场景。优化器会分析代码,进行各种优化,如常量折叠、死代码消除等。

  1. 测试框架:
  • Forge的测试框架是其最强大的功能之一。它支持Solidity原生测试,这意味着你可以用Solidity编写测- 试代码,而不需要切换到其他语言。这种设计带来了很多好处:类型安全、代码一致性、学习成本低等。

  • 内置模糊测试和属性测试是Forge的独特优势。模糊测试会自动生成大量随机输入来测试合约,发现边界情况和潜在漏洞。属性测试则允许你定义合约应该满足的属性,然后自动验证这些属性是否在所有情况下都成立。这些功能在其他工具中需要额外安装插件才能使用。

  • 覆盖率报告功能可以清楚地显示哪些代码被测试覆盖,哪些代码需要补充测试。这对于确保测试质量非常重要。你可以看到每个函数的覆盖率,识别未被测试的代码路径。

  • Gas快照对比功能允许你比较不同版本的Gas消耗。当你优化代码后,可以运行Gas快照,然后与之前的快照对比,看看优化效果如何。这对于Gas优化工作非常有帮助。

  1. 脚本系统:
  • Forge的脚本系统使用Solidity编写部署脚本,这是一个很大的创新。传统的工具使用JavaScript或TypeScript编写部署脚本,而Forge使用Solidity,这使得脚本代码与合约代码使用同一语言,降低了学习成本。

  • 脚本系统支持发送真实交易,这意味着你可以直接使用脚本部署合约到测试网或主网。脚本会处理所有细节,包括交易签名、Gas估算、nonce管理等。

  • 模拟执行(dry-run)功能允许你在不发送真实交易的情况下测试脚本。这对于调试和验证脚本逻辑非常有用。你可以先进行模拟执行,确认一切正常后再发送真实交易。

  • 多网络部署功能允许你轻松地将同一个合约部署到多个网络。只需要在命令中指定不同的RPC URL,Forge就会自动处理网络切换。这对于需要在多个测试网和主网部署的项目来说非常方便。

性能优势:

  • Forge的性能优势是其最大的卖点。与Hardhat相比,Forge的编译速度要快10到50倍。这意味着一个需要几分钟编译的项目,使用Forge可能只需要几秒钟。这种速度提升在大型项目中尤其明显。

  • 测试速度同样快10到100倍。这是因为Forge使用Rust编写,能够充分利用系统资源,实现并行测试执行。多个测试可以同时运行,大大缩短了总测试时间。

  • 内存占用也更低。Forge使用更高效的内存管理策略,在处理大型项目时,内存占用通常只有Hardhat的几分之一。这不仅降低了系统资源消耗,也使得在资源受限的环境中运行成为可能。

4.2 Cast - 命令行交互工具

Cast是Foundry提供的命令行交互工具,它是一个功能强大且易于使用的工具,可以让你在命令行中直接与区块链进行交互,无需编写代码。这对于快速测试、调试和自动化任务来说非常有用。

核心功能:

  • Cast提供了丰富的功能,涵盖了与区块链交互的各个方面:

  • 与区块链交互:Cast可以连接到任何EVM兼容的区块链,包括主网、测试网和本地节点。它支持标准的JSON-RPC接口,可以与任何兼容的节点进行通信。

  • 编码和解码数据:Cast提供了强大的数据编码和解码功能。你可以编码函数调用、参数、事件等,也可以解码返回的数据。这对于调试和理解交易数据非常有用。

  • 签名和验证:Cast支持消息签名和验证功能。你可以使用私钥签名消息,也可以验证签名的有效性。这对于实现签名验证功能、测试签名逻辑等场景非常有用。

  • 查询链上数据:Cast可以查询各种链上数据,包括账户余额、交易信息、区块信息、合约代码等。这些查询是只读的,不会消耗Gas,可以安全地频繁使用。

  • 发送交易:Cast可以发送交易到区块链,包括简单的ETH转账和合约函数调用。这对于测试合约功能、部署脚本等场景非常有用。

  • Gas估算:Cast可以估算交易的Gas消耗,帮助你了解执行某个操作需要多少Gas。这对于优化Gas消耗、设置合适的Gas限制等场景非常有用。

常用命令示例:

Cast的命令设计得非常直观,命令名称直接反映了功能。以下是一些常用命令的示例:

go 复制代码
# 查询链ID,了解当前连接的链
cast chain-id --rpc-url $RPC_URL

# 查询账户余额,检查账户ETH余额
cast balance $ADDRESS --rpc-url $RPC_URL

# 调用合约只读函数,查询合约状态
cast call $CONTRACT_ADDRESS "functionName()" --rpc-url $RPC_URL

# 发送交易,调用合约函数并修改状态
cast send $CONTRACT_ADDRESS "functionName(uint256)" 100 --private-key $PRIVATE_KEY --rpc-url $RPC_URL

# ABI编码,将函数调用编码为calldata
cast abi-encode "functionName(uint256)" 100

# 签名消息,使用私钥签名消息
cast wallet sign-message "Hello World" --private-key $PRIVATE_KEY

这些命令涵盖了大部分常见的区块链交互场景。通过组合使用这些命令,你可以完成复杂的操作,而无需编写代码。

优势:

Cast的优势在于其简单性和强大功能的结合:

  • 无需编写代码:你可以直接在命令行中使用Cast,无需编写任何代码。这对于快速测试、调试和探索非常有用。当你需要快速检查某个合约的状态或调用某个函数时,Cast是最快的方式。

  • 适合脚本和自动化:由于Cast是命令行工具,它可以很容易地集成到脚本和自动化流程中。你可以编写Shell脚本、Python脚本等,使用Cast来完成各种任务。这对于CI/CD流程、自动化测试等场景非常有用。

  • 轻量级,功能专注:Cast专注于区块链交互,不包含其他不必要的功能。这使得它非常轻量级,启动速度快,资源占用低。同时,功能专注也使得它更容易学习和使用。

  • 学习曲线平缓:Cast的命令设计得非常直观,学习成本低。即使你之前没有使用过类似工具,也能快速上手。文档也非常完善,提供了大量的示例和说明。

4.3 Anvil - 本地以太坊节点

Anvil是Foundry提供的本地以太坊节点,它是一个高性能的本地区块链实现,非常适合本地开发和测试。与真实的区块链节点不同,Anvil运行在本地,不需要同步区块,执行速度极快,可以立即处理交易。

核心特性:

  • Anvil提供了许多强大的特性,使其成为本地开发的理想选择:

  • 高性能的本地以太坊节点实现:Anvil使用Rust编写,性能极高。它可以在本地快速执行交易,不需要等待区块确认。这对于需要频繁测试的场景来说非常重要,可以大大加快开发速度。

  • 预置10个测试账户,每个账户有10000 ETH:Anvil启动时会自动创建10个测试账户,每个账户都有10000 ETH。这些账户的私钥会在启动时显示,你可以直接使用它们进行测试。这避免了每次都需要创建新账户或从水龙头获取测试ETH的麻烦。

-支持主网分叉功能:Anvil可以分叉主网或测试网,在本地创建一个与真实网络状态相同的环境。这对于测试与主网合约的交互、调试主网问题等场景非常有用。你可以指定分叉的区块号,创建一个特定时间点的网络状态快照。

  • 可配置区块时间:Anvil允许你配置区块时间,可以设置为立即出块或固定间隔。这对于测试时间相关的功能(如时间锁、定时任务等)非常有用。你可以快速推进时间,测试各种时间场景。

  • 支持状态快照功能:Anvil支持状态快照,你可以保存当前状态,然后在需要时恢复。这对于测试不同场景非常有用,你可以在同一个状态下测试多个场景,而不需要重新部署合约。

常用命令:

Anvil的命令行选项非常丰富,可以满足各种需求:

go 复制代码
# 启动默认节点,使用默认配置
anvil

# 自定义端口和链ID,适用于需要多个节点或特定链ID的场景
anvil --port 8546 --chain-id 31338

# 分叉主网,在本地创建主网状态的副本
anvil --fork-url $MAINNET_RPC

# 指定分叉区块号,创建特定区块的主网状态快照
anvil --fork-url $MAINNET_RPC --fork-block-number 18000000

# 设置区块时间,控制出块间隔
anvil --block-time 2

这些选项可以组合使用,创建满足特定需求的本地节点。例如,你可以创建一个分叉主网的节点,使用自定义端口,并设置较短的区块时间,用于快速测试。

与Hardhat Network对比:

Anvil和Hardhat Network都是本地以太坊节点,但有一些重要区别:

Anvil的执行速度更快,这是因为它是用Rust编写的,能够充分利用系统资源。分叉功能也更灵活,支持更多的配置选项。两者都预置了10个账户,都支持Gas报告,但在性能上Anvil有明显优势。

使用场景:

Anvil适用于多种场景:

  • 本地开发和测试:在本地开发时,使用Anvil可以快速测试合约功能,不需要连接到远程节点。这避免了网络延迟,也节省了测试ETH。

  • 快速迭代:由于Anvil执行速度极快,你可以快速迭代和测试。修改代码后,可以立即在Anvil上测试,获得即时反馈。

  • 主网状态测试:使用分叉功能,你可以在本地测试与主网合约的交互。这对于调试主网问题、测试集成等场景非常有用。

  • 调试复杂交易:Anvil提供了丰富的调试信息,可以帮助你理解交易的执行过程。这对于调试复杂交易、理解Gas消耗等场景非常有用。

  • 性能测试:Anvil可以用于性能测试,测试合约在不同负载下的表现。由于执行速度快,可以快速完成大量测试。

5. 项目初始化

5.1 创建项目

创建Foundry项目是一个简单的过程,只需要几个命令。了解这个过程有助于你快速开始新的项目。

步骤1:创建项目目录

首先,需要创建一个项目目录。你可以使用任何你喜欢的目录名称,但建议使用描述性的名称,能够反映项目的用途:

go 复制代码
mkdir foundry-project
cd foundry-project

这个步骤很简单,就是创建一个新目录并进入它。如果你已经有了一个项目目录,可以直接进入该目录,跳过创建步骤。

步骤2:初始化Foundry项目

进入项目目录后,运行forge init命令来初始化Foundry项目:

go 复制代码
forge init

这个命令会执行以下操作:

  • 创建标准的项目目录结构(src、test、script、lib等)
  • 生成一个示例合约(Counter.sol)
  • 生成对应的测试文件(Counter.t.sol)
  • 生成部署脚本(Counter.s.sol) 安装forge-std标准库
  • 创建foundry.toml配置文件
  • 初始化Git仓库(如果当前目录不是Git仓库)
  • 创建.gitignore文件
  • 创建README.md文件

整个过程是自动化的,你只需要等待命令完成即可。初始化完成后,你就有了一个完整的、可以立即使用的Foundry项目。

初始化选项:

forge init命令提供了几个有用的选项,可以根据你的需求选择:

go 复制代码
# 不创建Git仓库,适用于已经在Git仓库中的情况
forge init --no-commit

# 不包含示例代码,适用于想要从空白项目开始的情况
forge init --no-template

# 同时使用两个选项,创建最基础的项目结构
forge init --no-commit --no-template

--no-commit选项会跳过Git仓库的初始化和首次提交。如果你已经在Git仓库中,或者想要手动管理Git,可以使用这个选项。

--no-template选项会跳过示例代码的生成。如果你想要一个完全空白的项目,或者已经有了自己的代码结构,可以使用这个选项。

这两个选项可以组合使用,创建最基础的项目结构,只包含必要的目录和配置文件,不包含任何示例代码或Git提交。

5.2 项目结构

初始化完成后,项目结构如下:

go 复制代码
foundry-project/
├── src/              # 智能合约文件
│   └── Counter.sol
├── test/             # 测试文件
│   └── Counter.t.sol
├── script/           # 部署脚本
│   └── Counter.s.sol
├── lib/              # 依赖库
│   └── forge-std/
├── foundry.toml      # 配置文件
├── .gitignore        # Git忽略文件
└── README.md         # 项目说明

5.3 目录说明

理解项目目录结构对于有效使用Foundry非常重要。每个目录都有特定的用途,遵循这些约定可以让项目更加清晰和易于维护。

src目录:

  • src目录是存放所有智能合约源代码的地方。所有以.sol结尾的文件都应该放在这个目录下。Forge会自动扫描这个目录,编译所有找到的合约文件。

  • src目录支持子目录组织,这意味着你可以根据功能或模块来组织合约。例如,你可以创建src/tokens/目录存放代币合约,src/utils/目录存放工具合约等。这种组织方式对于大型项目来说非常重要,能够提高代码的可维护性。

  • Forge会自动编译src目录下的所有合约,包括子目录中的合约。你不需要手动指定要编译哪些文件,Forge会智能地处理依赖关系,按照正确的顺序编译。

test目录:

  • test目录是存放所有测试文件的地方。测试文件有特定的命名规则:必须以.t.sol结尾,例如Counter.t.sol。这个命名规则让Forge能够识别哪些文件是测试文件。

  • 测试文件需要继承forge-std/Test.sol,这个基类提供了丰富的测试功能,包括断言函数、Cheatcodes等。通过继承这个基类,你的测试文件就可以使用所有Foundry提供的测试功能。

  • test目录同样支持子目录组织,你可以根据合约或功能来组织测试文件。例如,test/tokens/目录存放代币相关的测试,test/utils/目录存放工具相关的测试。

  • Foundry支持模糊测试,你可以在测试函数中使用fuzz关键字来定义模糊测试。Forge会自动生成随机输入来测试这些函数,发现边界情况和潜在问题。

script目录:

  • script目录是存放部署脚本的地方。部署脚本也有特定的命名规则:必须以.s.sol结尾,例如DeployCounter.s.sol。这个命名规则让Forge能够识别哪些文件是部署脚本。

  • 部署脚本需要继承forge-std/Script.sol,这个基类提供了部署相关的功能,包括vm.startBroadcast()和- vm.stopBroadcast()等。通过继承这个基类,你的脚本就可以发送真实交易到区块链。

  • 部署脚本可以执行复杂的部署逻辑,包括部署多个合约、设置初始状态、调用初始化函数等。脚本执行时可以选择模拟执行(dry-run)或发送真实交易(broadcast),这为调试和验证提供了灵活性。

lib目录:

  • lib目录是存放依赖库的地方。依赖库通常是从GitHub克隆的Solidity库,如OpenZeppelin、Solmate等。这些库会被作为Git子模块管理,确保版本控制和依赖一致性。

  • 使用forge install命令可以安装依赖库。例如,forge install OpenZeppelin/openzeppelin-contracts会从GitHub克隆OpenZeppelin合约库到lib目录。Forge会自动处理依赖关系,确保所有依赖都能正确解析。

  • lib目录中的库会被自动包含在编译过程中。你可以在合约中使用import语句导入这些库,Forge会自动解析导入路径。这种设计使得使用第三方库变得非常简单,不需要手动配置路径或管理依赖。

5.4 示例文件

Counter.sol(示例合约):

go 复制代码
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}

Counter.t.sol(示例测试):

go 复制代码
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import "../src/Counter.sol";

contract CounterTest is Test {
    Counter public counter;

    function setUp() public {
        counter = new Counter();
    }

    function testSetNumber() public {
        counter.setNumber(100);
        assertEq(counter.number(), 100);
    }

    function testIncrement() public {
        counter.increment();
        assertEq(counter.number(), 1);
    }
}

Counter.s.sol(示例部署脚本):

go 复制代码
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../src/Counter.sol";

contract DeployCounter is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);
        
        Counter counter = new Counter();
        
        vm.stopBroadcast();
        console.log("Counter deployed at:", address(counter));
    }
}

6. foundry.toml配置详解

6.1 配置文件格式

Foundry的配置通过foundry.toml文件进行管理,这个文件使用TOML格式。

基础配置示例:

go 复制代码
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.24"
evm_version = "paris"
optimizer = true
optimizer_runs = 200
bytecode_size_limit = 24576

6.2 基础配置项

目录配置:

go 复制代码
src = "src"              # 源代码目录
out = "out"              # 输出目录
libs = ["lib"]           # 库目录(数组)

编译器配置:

go 复制代码
solc = "0.8.24"          # Solidity版本
evm_version = "paris"     # EVM版本
optimizer = true          # 启用优化器
optimizer_runs = 200      # 优化器运行次数
bytecode_size_limit = 24576  # 字节码大小限制

优化器运行次数说明:

1:优化部署成本,适合一次性部署的合约

200:默认推荐值,平衡部署和执行成本

10000:优化执行成本,适合频繁调用的函数

6.3 网络配置

RPC端点配置:

go 复制代码
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC}"
mainnet = "${MAINNET_RPC}"

Etherscan配置:

go 复制代码
[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }
mainnet = { key = "${ETHERSCAN_API_KEY}" }

6.4 多Profile配置

Foundry支持多Profile配置,可以为不同的环境设置不同的配置。

默认配置:

go 复制代码
[profile.default]
optimizer_runs = 200

CI配置:

go 复制代码
[profile.ci]
optimizer_runs = 1

生产配置:

go 复制代码
[profile.production]
optimizer_runs = 10000

开发配置:

go 复制代码
[profile.development]
optimizer = false

使用不同Profile:

go 复制代码
# 使用CI配置编译
forge build --profile ci

# 使用生产配置测试
forge test --profile production

6.5 测试配置

go 复制代码
[profile.default]
# 模糊测试运行次数
fuzz = { runs = 256 }

# 属性测试运行次数
invariant = { runs = 256 }

# 测试超时时间(秒)
test_timeout = 300

# Gas报告
gas_reports = ["*"]

6.6 编译器配置

多版本编译器:

go 复制代码
[profile.default]
solc_version = "0.8.24"

# 或者启用自动检测
auto_detect_solc = true

指定多个版本:

go 复制代码
[profile.default]
solc_version = "0.8.24"

[profile.legacy]
solc_version = "0.7.6"

6.7 完整配置示例

go 复制代码
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.24"
evm_version = "paris"
optimizer = true
optimizer_runs = 200
bytecode_size_limit = 24576

[rpc_endpoints]
sepolia = "${SEPOLIA_RPC}"
mainnet = "${MAINNET_RPC}"

[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }
mainnet = { key = "${ETHERSCAN_API_KEY}" }

[profile.ci]
optimizer_runs = 1

[profile.production]
optimizer_runs = 10000

7. 网络配置详解

7.1 环境变量设置

首先,需要设置环境变量。创建一个.env文件:

go 复制代码
# .env文件
SEPOLIA_RPC=https://sepolia.infura.io/v3/YOUR_API_KEY
MAINNET_RPC=https://mainnet.infura.io/v3/YOUR_API_KEY
PRIVATE_KEY=your_private_key_here
ETHERSCAN_API_KEY=your_etherscan_api_key_here

重要提示:

  • .env文件不应该提交到Git仓库
  • 在.gitignore中添加.env
  • 使用测试网私钥进行测试
  • 主网私钥要特别保护

7.2 foundry.toml网络配置

在foundry.toml中引用环境变量:

go 复制代码
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC}"
mainnet = "${MAINNET_RPC}"

[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }
mainnet = { key = "${ETHERSCAN_API_KEY}" }

7.3 网络类型

  1. Anvil本地网络:
go 复制代码
# 启动默认配置
anvil

# 默认配置
# URL: http://127.0.0.1:8545
# Chain ID: 31337
# 预置账户: 10个,每个10000 ETH
  1. 分叉主网:
go 复制代码
# 分叉主网
anvil --fork-url $MAINNET_RPC

# 指定分叉区块号
anvil --fork-url $MAINNET_RPC --fork-block-number 18000000

# 设置区块时间
anvil --fork-url $MAINNET_RPC --block-time 2
  1. 测试网络:

在foundry.toml中配置:

go 复制代码
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC}"
goerli = "${GOERLI_RPC}"
  1. 主网:
go 复制代码
[rpc_endpoints]
mainnet = "${MAINNET_RPC}"

7.4 安全提示

私钥安全:

  • 使用环境变量存储私钥
  • 不要提交.env文件到Git
  • 使用测试网私钥进行测试
  • 主网私钥要特别保护
  • 不要在代码中硬编码私钥
  • 不要将私钥提交到版本控制

RPC端点:

  • 使用可靠的RPC提供商(Infura、Alchemy、QuickNode)
  • 注意API限制
  • 考虑使用多个RPC端点作为备份

7.5 使用网络

直接指定RPC URL:

go 复制代码
forge script script/Counter.s.sol --rpc-url $SEPOLIA_RPC

使用配置的网络名称:

go 复制代码
forge script script/Counter.s.sol --rpc-url sepolia

8. Forge编译合约

8.1 基础编译

编译是智能合约开发的基础步骤,Forge提供了简单而强大的编译功能。理解编译过程对于有效使用Forge非常重要。

编译所有合约:

最基础的编译命令是forge build,它会编译项目中的所有合约:

go 复制代码
forge build

这个命令会执行以下操作:

  • 扫描src目录,找到所有.sol文件
  • 解析导入语句,处理依赖关系
  • 使用配置的Solidity版本编译所有合约
  • 生成字节码、ABI和元数据文件
  • 将编译产物写入out目录

编译过程是增量式的,Forge会智能地只编译修改过的文件。如果你只修改了一个文件,Forge只会重新编译这个文件及其依赖,而不是重新编译整个项目。这大大提高了编译速度,特别是在大型项目中。

如果编译成功,你会看到类似"Compiler run successful"的消息。如果编译失败,Forge会显示详细的错误信息,包括错误位置、错误类型和建议的修复方法。

清理后重新编译:

有时候,你可能想要清理所有编译产物,然后重新编译。这在你修改了配置、怀疑缓存有问题、或者想要确保完全重新编译时很有用:

go 复制代码
forge clean && forge build

forge clean命令会删除out目录中的所有编译产物,包括字节码、ABI、元数据等。然后forge build会重新编译所有合约。这个过程可能需要一些时间,特别是对于大型项目,但可以确保所有文件都是最新编译的。

查看编译详情:

如果你想查看编译的详细信息,可以使用--sizes选项:

go 复制代码
forge build --sizes

这个选项会显示每个合约的字节码大小,这对于优化合约大小非常有用。以太坊对合约大小有限制(通常是24KB),如果合约太大,需要优化代码或使用库来减少大小。查看合约大小可以帮助你识别哪些合约需要优化。

8.2 编译输出

编译成功后的输出示例:

go 复制代码
Compiling 2 files with 0.8.24
Compiler run successful
Artifacts written to /Users/username/foundry-project/out
Size of Counter.sol:Counter: 0.234 KB

8.3 编译选项

查看合约大小:

go 复制代码
forge build --sizes

输出:

go 复制代码
Contract          Size (KB)
Counter           0.234

查看所有编译的合约:

go 复制代码
forge build --names

强制重新编译:

go 复制代码
forge build --force

编译特定合约:

go 复制代码
# 指定文件路径
forge build src/Counter.sol

# 使用匹配模式
forge build --match-path "src/**/*.sol"

8.4 编译优化

在foundry.toml中配置优化器:

go 复制代码
[profile.default]
optimizer = true
optimizer_runs = 200
optimizer_runs值的选择:
  • 1:优化部署成本,适合一次性部署的合约
  • 200:默认推荐值,平衡部署和执行成本
  • 10000:优化执行成本,适合频繁调用的函数

8.5 编译产物

编译产物存放在out目录下:

out/

├── Counter.sol/

│ ├── Counter.json # ABI和字节码

│ └── metadata.json # 元数据

8.6 常见编译问题

问题1:版本不匹配

错误信息:

Error: Solidity version mismatch

解决方案:

  • 检查foundry.toml中的solc版本
  • 确保版本与合约中的pragma声明一致

问题2:依赖缺失

错误信息:

Error: Unable to import "forge-std/Test.sol"

解决方案:

go 复制代码
# 安装依赖
forge install foundry-rs/forge-std

问题3:导入路径错误

错误信息:

Error: File not found

解决方案:

  • 检查remappings配置
  • 确保导入路径正确
  • 使用forge remappings查看当前映射

9. 编写部署脚本

9.1 部署脚本基础

Foundry的部署脚本使用Solidity编写,这是一个很大的创新和优势。传统的工具如Hardhat使用JavaScript或TypeScript编写部署脚本,而Foundry使用Solidity,这使得脚本代码与合约代码使用同一语言,降低了学习成本,也提高了类型安全性。

使用Solidity编写部署脚本的好处是多方面的。首先,你不需要在两种语言之间切换,所有的代码都使用Solidity,这降低了认知负担。其次,Solidity的类型系统更加严格,能够在编译时发现更多错误。最后,脚本可以直接使用合约中的类型和接口,不需要进行类型转换或适配。

脚本文件位置:script/目录

所有部署脚本都应该放在script/目录下。这个目录是Foundry约定的部署脚本存放位置,Forge会自动识别这个目录中的脚本文件。

文件命名规则:ScriptName.s.sol

部署脚本有特定的命名规则:文件名必须以.s.sol结尾。这个命名规则让Forge能够识别哪些文件是部署脚本。例如,DeployCounter.s.sol是一个有效的部署脚本文件名。

基础结构:

go 复制代码
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../src/Counter.sol";

contract DeployCounter is Script {
    function run() external {
        // 从环境变量读取私钥
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        
        // 开始广播交易
        vm.startBroadcast(deployerPrivateKey);
        
        // 部署合约
        Counter counter = new Counter();
        
        // 停止广播
        vm.stopBroadcast();
        
        // 输出部署地址
        console.log("Counter deployed at:", address(counter));
    }
}

9.2 高级部署脚本

包含更多信息:

go 复制代码
contract DeployCounter is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        address deployer = vm.addr(deployerPrivateKey);
        
        console.log("Deploying from:", deployer);
        console.log("Balance:", deployer.balance);
        
        vm.startBroadcast(deployerPrivateKey);
        
        Counter counter = new Counter();
        
        // 部署后调用初始化函数
        counter.setNumber(100);
        
        vm.stopBroadcast();
        
        console.log("Counter deployed at:", address(counter));
        console.log("Initial number:", counter.number());
    }
}

9.3 脚本执行

模拟执行(Dry-run):

go 复制代码
# 不发送真实交易,只模拟
forge script script/Counter.s.sol --rpc-url $SEPOLIA_RPC

在指定网络上模拟:

go 复制代码
forge script script/Counter.s.sol --fork-url $MAINNET_RPC

发送真实交易:

go 复制代码
# 添加--broadcast选项
forge script script/Counter.s.sol --rpc-url $SEPOLIA_RPC --broadcast

自动验证合约:

go 复制代码
forge script script/Counter.s.sol \
  --rpc-url $SEPOLIA_RPC \
  --broadcast \
  --verify \
  --etherscan-api-key $ETHERSCAN_API_KEY

9.4 指定私钥

使用环境变量(推荐):

go 复制代码
export PRIVATE_KEY=your_private_key
forge script script/Counter.s.sol --rpc-url $SEPOLIA_RPC --broadcast

通过命令行参数(不推荐,不安全):

go 复制代码
forge script script/Counter.s.sol \
  --rpc-url $SEPOLIA_RPC \
  --broadcast \
  --private-key $PRIVATE_KEY

9.5 多网络部署

分别部署到不同网络:

go 复制代码
# 部署到Sepolia
forge script script/Counter.s.sol --rpc-url sepolia --broadcast

# 部署到主网
forge script script/Counter.s.sol --rpc-url mainnet --broadcast

9.6 Cheatcodes使用

部署脚本中可以使用很多Cheatcodes:

go 复制代码
contract DeployCounter is Script {
    function run() external {
        // 读取环境变量
        uint256 privateKey = vm.envUint("PRIVATE_KEY");
        string memory rpcUrl = vm.envString("RPC_URL");
        
        // 将私钥转换为地址
        address deployer = vm.addr(privateKey);
        
        // 标记地址(用于调试)
        vm.label(deployer, "Deployer");
        
        // 设置区块号
        vm.roll(100);
        
        // 设置时间戳
        vm.warp(block.timestamp + 86400);
        
        // 给地址ETH
        vm.deal(address(0x123), 1 ether);
        
        // 模拟调用者
        vm.prank(address(0x123));
        
        vm.startBroadcast(privateKey);
        // 部署逻辑
        vm.stopBroadcast();
    }
}

10. Anvil本地测试网

10.1 启动Anvil

启动Anvil非常简单,只需要一个命令。但理解Anvil的工作原理和配置选项对于有效使用它非常重要。

基础启动:

最基础的启动方式就是直接运行anvil命令:

go 复制代码
anvil

这个命令会启动一个使用默认配置的本地以太坊节点。节点会立即启动,不需要等待同步或初始化。启动后,Anvil会显示预置账户的信息,包括地址和私钥,你可以直接使用这些账户进行测试。

默认配置:

Anvil的默认配置经过精心设计,适合大多数开发场景:

  • HTTP服务器:http://127.0.0.1:8545,这是标准的以太坊RPC端口。你可以通过这个地址连接到Anvil节点,就像连接真实的以太坊节点一样。

  • WebSocket服务器:ws://127.0.0.1:8545,支持WebSocket连接。这对于需要实时监听事件的场景很有用。

  • Chain ID:31337,这是一个特殊的链ID,专门用于本地开发。它不会与任何真实的网络冲突。

  • 预置账户:10个账户,每个账户有10000 ETH。这些账户的私钥会在启动时显示,你可以直接使用它们进行测试,不需要从水龙头获取测试ETH。

这些默认配置对于大多数开发场景来说已经足够。如果你需要不同的配置,可以使用命令行选项来自定义。

启动输出:

go 复制代码
Available Accounts
==================
(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
(1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
...

Private Keys
==================
(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
...

Listening on 127.0.0.1:8545

10.2 自定义配置

自定义端口和链ID:

go 复制代码
anvil --port 8546 --chain-id 31338

自定义账户数量:

go 复制代码
anvil --accounts 20

自定义账户余额:

go 复制代码
anvil --balance 5000

设置区块时间:

go 复制代码
anvil --block-time 2

组合使用:

go 复制代码
anvil --port 8546 --chain-id 31338 --accounts 20 --balance 5000 --block-time 2

10.3 分叉主网

基础分叉:

go 复制代码
anvil --fork-url $MAINNET_RPC

指定分叉区块号:

go 复制代码
anvil --fork-url $MAINNET_RPC --fork-block-number 18000000

设置区块时间:

go 复制代码
anvil --fork-url $MAINNET_RPC --block-time 2

分叉测试网:

go 复制代码
anvil --fork-url $SEPOLIA_RPC

10.4 状态管理

Anvil支持状态快照功能,可以在控制台输入命令:

保存快照:

go 复制代码
anvil_snapshot

返回快照ID。

恢复快照:

go 复制代码
anvil_revert <snapshot_id>

重置到创世区块:

go 复制代码
anvil_reset

10.5 与脚本和测试连接

在脚本中使用:

go 复制代码
forge script script/Counter.s.sol --rpc-url http://127.0.0.1:8545

在测试中使用:

go 复制代码
forge test --fork-url http://127.0.0.1:8545

10.6 Anvil的优势

性能优势:

  • 极快的执行速度
  • 低延迟
  • 适合快速迭代

功能优势:

  • 支持主网分叉
  • 灵活的状态管理
  • 丰富的调试信息
  • 可配置性强

使用场景:

  • 本地开发和测试
  • 快速迭代
  • 主网状态测试
  • 调试复杂交易
  • 性能测试

11. 测试网部署

11.1 部署前准备

部署到测试网是一个重要的步骤,需要做好充分的准备。在开始部署之前,确保所有准备工作都已完成,可以避免部署过程中的问题。

  1. 获取测试ETH:

部署合约需要支付Gas费用,因此你需要有足够的测试ETH。可以从以下水龙头获取Sepolia测试ETH:

  • sepoliafaucet.com:这是一个专门为Sepolia测试网提供测试ETH的水龙头。通常每天可以获取一定数量的测试ETH。

  • QuickNode Faucet:QuickNode提供的测试网水龙头,需要注册账户。

  • Alchemy Faucet:Alchemy提供的测试网水龙头,需要注册账户并创建应用。

  • Infura Faucet:Infura提供的测试网水龙头,需要注册账户。

不同的水龙头有不同的限制和要求,有些需要注册账户,有些有频率限制。建议注册多个账户,以便在需要时能够获取足够的测试ETH。

  1. 配置环境变量:

环境变量是存储敏感信息(如私钥、API密钥)的最佳方式。创建一个.env文件来存储这些信息:

go 复制代码
SEPOLIA_RPC=https://sepolia.infura.io/v3/YOUR_API_KEY
PRIVATE_KEY=your_test_private_key
ETHERSCAN_API_KEY=your_etherscan_api_key

重要提示:

  • .env文件不应该提交到Git仓库
  • 在.gitignore中添加.env
  • 使用测试网私钥,不要使用主网私钥
  • 私钥格式应该是0x开头的十六进制字符串
  1. 配置foundry.toml:

在foundry.toml中配置网络和Etherscan设置:

go 复制代码
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC}"

[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }

这样配置后,你可以在命令中使用sepolia作为网络名称,而不需要每次都输入完整的RPC URL。这使命令更简洁,也更容易管理。

11.2 部署流程

步骤1:模拟执行

go 复制代码
forge script script/Counter.s.sol --rpc-url sepolia

这会:

  • 检查部署是否正常
  • 估算Gas消耗
  • 不发送真实交易

步骤2:检查Gas估算

确保账户有足够的ETH支付Gas费用。

步骤3:发送交易

go 复制代码
forge script script/Counter.s.sol \
  --rpc-url sepolia \
  --broadcast \
  --verify \
  --etherscan-api-key $ETHERSCAN_API_KEY \
  --slow

11.3 完整部署命令

go 复制代码
forge script script/Counter.s.sol \
  --rpc-url sepolia \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --verify \
  --etherscan-api-key $ETHERSCAN_API_KEY \
  --slow

选项说明:

go 复制代码
--rpc-url:指定RPC端点
--broadcast:发送真实交易
--verify:自动验证合约
--slow:降低发送速度,避免nonce冲突
--resume:恢复失败的部署
--skip-simulation:跳过模拟(不推荐)

11.4 部署后验证

查看部署的合约代码:

go 复制代码
cast code $CONTRACT_ADDRESS --rpc-url sepolia

调用合约函数:

go 复制代码
cast call $CONTRACT_ADDRESS "number()" --rpc-url sepolia

在Etherscan上查看:

访问 https://sepolia.etherscan.io/address/$CONTRACT_ADDRESS

11.5 常见部署问题

问题1:Nonce too high

原因:账户nonce不匹配

解决方案:

  • 使用--slow选项
  • 等待一段时间后重试
  • 检查账户nonce:cast nonce $ADDRESS --rpc-url sepolia

问题2:Insufficient funds

原因:账户余额不足

解决方案:

  • 检查账户余额:cast balance $ADDRESS --rpc-url sepolia
  • 从水龙头获取更多测试ETH
    问题3:Gas price too low

原因:Gas价格设置过低

解决方案:

  • 增加gas price
  • 使用--legacy选项

问题4:验证失败

原因:构造函数参数不正确

解决方案:

  • 检查构造函数参数
  • 确保优化次数正确
  • 手动验证:forge verify-contract

11.6 最佳实践

推荐做法:

  • 先dry-run测试

  • 检查Gas估算

  • 使用--slow避免nonce问题

  • 保存部署地址和交易哈希

  • 及时验证合约

  • 测试所有功能

    避免做法:

  • 不要跳过dry-run直接部署

  • 不要忽视Gas优化

  • 不要在主网直接测试

  • 不要使用主网私钥测试

12. 合约验证和交互

12.1 合约验证

合约验证是部署后的重要步骤。验证后的合约可以在Etherscan上查看源代码,这对于用户信任和代码审计非常重要。理解验证过程有助于你正确完成验证。

自动验证:

最简单的方式是在部署时使用--verify选项,合约会在部署后自动验证:

go 复制代码
forge script script/Counter.s.sol \
  --rpc-url sepolia \
  --broadcast \
  --verify \
  --etherscan-api-key $ETHERSCAN_API_KEY

这种方式最方便,但需要确保所有配置都正确,包括优化次数、构造函数参数等。如果自动验证失败,可以稍后手动验证。

手动验证:

如果部署时没有验证,或者自动验证失败,可以手动验证:

go 复制代码
forge verify-contract \
  $CONTRACT_ADDRESS \
  src/Counter.sol:Counter \
  --chain sepolia \
  --etherscan-api-key $ETHERSCAN_API_KEY

这个命令需要指定:

  • 合约地址:部署后的合约地址
  • 完全限定名称:源文件路径:合约名称
  • 链名称:要验证的链(sepolia、mainnet等)
  • Etherscan API密钥:用于提交验证请求
  • 验证过程可能需要几分钟时间,Etherscan需要编译源代码并比较字节码。验证成功后,你可以在Etherscan上看到合约的源代码。

带构造函数参数:

  • 如果合约有构造函数参数,需要在验证时指定这些参数。首先需要编码构造函数参数:

编码构造函数参数

go 复制代码
cast abi-encode "constructor(uint256)" 100

然后使用编码后的参数进行验证:

go 复制代码
forge verify-contract \
  $CONTRACT_ADDRESS \
  src/Counter.sol:Counter \
  --constructor-args $(cast abi-encode "constructor(uint256)" 100) \
  --chain sepolia \
  --etherscan-api-key $ETHERSCAN_API_KEY

构造函数参数必须与部署时使用的参数完全一致,否则验证会失败。如果构造函数有多个参数,需要按照顺序编码所有参数。

验证多个合约:

如果部署了多个合约,需要分别对每个合约执行验证命令。每个合约都需要:

  • 正确的合约地址
  • 正确的源文件路径和合约名称
  • 正确的构造函数参数(如果有)
  • 正确的优化次数

确保所有合约都正确验证,这样用户才能查看所有合约的源代码。

12.2 查询链上数据

查询余额:

go 复制代码
cast balance $ADDRESS --rpc-url sepolia

查询链ID:

go 复制代码
cast chain-id --rpc-url sepolia

查询区块号:

go 复制代码
cast block-number --rpc-url sepolia

查询Gas价格:

go 复制代码
cast gas-price --rpc-url sepolia

12.3 调用只读函数

无参数函数:

go 复制代码
cast call $CONTRACT_ADDRESS "number()" --rpc-url sepolia

带参数函数:

go 复制代码
cast call $CONTRACT_ADDRESS "balanceOf(address)" $ADDRESS --rpc-url sepolia

格式化输出:

go 复制代码
# 转换为ASCII
cast call $CONTRACT_ADDRESS "name()" --rpc-url sepolia --to-ascii

# 转换为十进制
cast call $CONTRACT_ADDRESS "number()" --rpc-url sepolia --to-dec

12.4 发送交易

简单交易:

go 复制代码
cast send $CONTRACT_ADDRESS "increment()" \
  --private-key $PRIVATE_KEY \
  --rpc-url sepolia

带参数交易:

go 复制代码
cast send $CONTRACT_ADDRESS "setNumber(uint256)" 100 \
  --private-key $PRIVATE_KEY \
  --rpc-url sepolia

指定Gas限制:

go 复制代码
cast send $CONTRACT_ADDRESS "increment()" \
  --private-key $PRIVATE_KEY \
  --rpc-url sepolia \
  --gas-limit 100000

12.5 Gas估算

估算无参数函数:

go 复制代码
cast estimate $CONTRACT_ADDRESS "increment()" --rpc-url sepolia

估算带参数函数:

go 复制代码
cast estimate $CONTRACT_ADDRESS "setNumber(uint256)" 100 --rpc-url sepolia

12.6 编码解码

ABI编码:

go 复制代码
cast abi-encode "setNumber(uint256)" 100

ABI解码:

go 复制代码
cast abi-decode "setNumber(uint256)" 0x0000000000000000000000000000000000000000000000000000000000000064

编码函数选择器:

go 复制代码
cast sig "setNumber(uint256)"

解码calldata:

go 复制代码
cast 4byte-decode 0x3fb5c1cb0000000000000000000000000000000000000000000000000000000000000064

12.7 签名和验证

签名消息:

go 复制代码
cast wallet sign-message "Hello World" --private-key $PRIVATE_KEY

验证签名:

go 复制代码
cast wallet verify $SIGNATURE "Hello World" $ADDRESS

12.8 完整交互流程

  1. 部署合约:
go 复制代码
forge script script/Counter.s.sol --rpc-url sepolia --broadcast
  1. 验证合约:
go 复制代码
forge verify-contract $CONTRACT_ADDRESS src/Counter.sol:Counter --chain sepolia
  1. 查询初始值:
go 复制代码
cast call $CONTRACT_ADDRESS "number()" --rpc-url sepolia
  1. 调用函数修改状态:
go 复制代码
cast send $CONTRACT_ADDRESS "increment()" --private-key $PRIVATE_KEY --rpc-url sepolia
  1. 再次查询确认修改:
go 复制代码
cast call $CONTRACT_ADDRESS "number()" --rpc-url sepolia

13. Cast命令行工具详解

13.1 链上查询

基础查询:

go 复制代码
# 查询链ID
cast chain-id --rpc-url $RPC_URL

# 查询区块号
cast block-number --rpc-url $RPC_URL

# 查询Gas价格
cast gas-price --rpc-url $RPC_URL

# 查询基础费用
cast base-fee --rpc-url $RPC_URL
账户查询:

# 查询余额
cast balance $ADDRESS --rpc-url $RPC_URL

# 查询nonce
cast nonce $ADDRESS --rpc-url $RPC_URL

# 查询合约代码
cast code $ADDRESS --rpc-url $RPC_URL

# 查询存储
cast storage $ADDRESS $SLOT --rpc-url $RPC_URL
区块查询:

# 查询区块信息
cast block latest --rpc-url $RPC_URL

# 查询特定区块
cast block 1000000 --rpc-url $RPC_URL

# 查询区块哈希
cast block-hash 1000000 --rpc-url $RPC_URL

13.2 合约交互

只读调用:

go 复制代码
cast call $CONTRACT_ADDRESS "functionName()" --rpc-url $RPC_URL

发送交易:

go 复制代码
cast send $CONTRACT_ADDRESS "functionName(uint256)" 100 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL

Gas估算:

go 复制代码
cast estimate $CONTRACT_ADDRESS "functionName()" --rpc-url $RPC_URL

13.3 数据编码

ABI编码:

go 复制代码
cast abi-encode "functionName(uint256,address)" 100 $ADDRESS

编码函数选择器:

go 复制代码
cast sig "functionName(uint256)"

编码参数:

go 复制代码
cast abi-encode-params "uint256,address" 100 $ADDRESS

解码calldata:

go 复制代码
cast 4byte-decode $CALLDATA
cast calldata-decode "functionName(uint256)" $CALLDATA

13.4 地址和私钥操作

私钥转地址:

go 复制代码
cast wallet address --private-key $PRIVATE_KEY

生成随机私钥:

go 复制代码
cast wallet new

验证地址格式:

go 复制代码
cast --to-checksum-address $ADDRESS

地址格式转换:

go 复制代码
cast --to-address $ADDRESS

13.5 数值转换

Wei转ETH:

go 复制代码
cast --to-unit 1000000000000000000 ether

ETH转Wei:

go 复制代码
cast --to-wei 1 ether

十六进制转十进制:

go 复制代码
cast --to-dec 0x64

十进制转十六进制:

go 复制代码
cast --to-hex 100

13.6 签名和验证

签名消息:

go 复制代码
cast wallet sign-message "Hello World" --private-key $PRIVATE_KEY

验证签名:

go 复制代码
cast wallet verify $SIGNATURE "Hello World" $ADDRESS

签名交易:

go 复制代码
cast wallet sign $TX_DATA --private-key $PRIVATE_KEY

13.7 交易相关

发送ETH:

go 复制代码
cast send $ADDRESS --value 1ether --private-key $PRIVATE_KEY --rpc-url $RPC_URL

发送原始交易:

go 复制代码
cast publish $RAW_TX --rpc-url $RPC_URL

查询交易:

go 复制代码
cast tx $TX_HASH --rpc-url $RPC_URL
cast receipt $TX_HASH --rpc-url $RPC_URL

13.8 日志和事件

查询事件日志:

go 复制代码
cast logs --from-block 1000000 --to-block latest --address $CONTRACT_ADDRESS --rpc-url $RPC_URL

解码日志:

go 复制代码
cast logs-decode "Transfer(address,address,uint256)" $LOG_DATA

13.9 实用技巧

使用环境变量:

go 复制代码
export RPC_URL=$SEPOLIA_RPC
cast chain-id --rpc-url $RPC_URL

设置Gas倍数:

go 复制代码
export CAST_GAS_MULTIPLIER=1.2
cast send $CONTRACT_ADDRESS "functionName()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL

切换费用模式:

go 复制代码
# Legacy模式
cast send ... --legacy

# EIP-1559模式
cast send ... --1559

查看详细输出:

go 复制代码
cast send ... --verbose

处理JSON输出:

go 复制代码
cast tx $TX_HASH --rpc-url $RPC_URL | jq '.gasUsed'

13.10 与其他工具对比

vs web3.js:

  • Cast可以直接在命令行使用,无需编写代码

  • 适合脚本和自动化

  • 更轻量级

    vs ethers.js:

  • Cast更轻量级,功能更专注

  • 学习曲线更平缓

  • 适合快速交互和测试

14. 最佳实践

14.1 配置管理

推荐做法:

  • 使用环境变量管理私钥和API密钥

  • 使用.env文件存储敏感信息

  • 在.gitignore中添加.env

  • 使用多Profile配置不同环境

  • 使用环境变量引用RPC URL

    避免做法:

  • 不要在代码中硬编码私钥

  • 不要提交.env文件到Git

  • 不要在主网配置中使用测试私钥

  • 不要将私钥提交到版本控制

14.2 部署流程

推荐做法:

  • 先dry-run测试
  • 检查Gas估算
  • 使用--slow避免nonce冲突
  • 保存部署地址和交易哈希
  • 及时验证合约
  • 测试所有功能

避免做法:

  • 不要跳过dry-run直接部署
  • 不要忽视Gas优化
  • 不要在主网直接测试
  • 不要使用主网私钥测试

14.3 测试实践

推荐做法:

  • 编写完整的测试用例
  • 使用模糊测试发现边界问题
  • 利用Anvil分叉主网测试
  • 追求高测试覆盖率
  • 测试所有错误路径

避免做法:

  • 不要忽略测试覆盖率
  • 不要只测试正常流程
  • 不要跳过边界情况测试

14.4 性能优化

编译优化:

  • 使用增量编译
  • 合理设置optimizer_runs
  • 清理不必要的依赖
  • 使用多Profile配置

测试优化:

  • 使用并行测试
  • 合理设置fuzz runs
  • 使用Anvil加速测试
  • 避免不必要的网络调用

部署优化:

  • 批量部署减少交易
  • 使用CREATE2确定性地址
  • 优化构造函数参数
  • 合理设置Gas价格

14.5 安全实践

私钥安全:

  • 使用环境变量存储私钥
  • 不要提交.env文件
  • 使用测试网私钥测试
  • 主网私钥要特别保护

合约安全:

  • 进行充分测试
  • 使用模糊测试
  • 进行安全审计
  • 遵循最佳实践

15. 实战演示

15.1 演示环境准备

检查Foundry安装:

go 复制代码
forge --version
cast --version
anvil --version

预期输出:

go 复制代码
forge 0.2.0 (abc123def456 2024-11-15T10:30:00.000000000Z)
cast 0.2.0 (abc123def456 2024-11-15T10:30:00.000000000Z)
anvil 0.2.0 (abc123def456 2024-11-15T10:30:00.000000000Z)

15.2 创建和初始化项目

创建项目:

go 复制代码
mkdir foundry-demo
cd foundry-demo
forge init --no-commit

查看项目结构:

go 复制代码
tree -L 2

预期输出:

go 复制代码
foundry-demo/
├── src/
│   └── Counter.sol
├── test/
│   └── Counter.t.sol
├── script/
│   └── Counter.s.sol
├── lib/
│   └── forge-std/
├── foundry.toml
├── .gitignore
└── README.md

15.3 查看和编译合约

查看示例合约:

go 复制代码
cat src/Counter.sol

编译合约:

go 复制代码
forge build

预期输出:

go 复制代码
Compiling 2 files with 0.8.24
Compiler run successful
Artifacts written to /Users/username/foundry-demo/out
Size of Counter.sol:Counter: 0.234 KB

查看编译详情:

go 复制代码
forge build --sizes

15.4 运行测试

查看测试文件:

go 复制代码
cat test/Counter.t.sol

运行测试:

go 复制代码
forge test

预期输出:

go 复制代码
Running 2 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrement() (gas: 28347)
[PASS] testSetNumber() (gas: 28347)
Test result: ok. 2 passed; 0 failed; finished in 2.34ms

15.5 启动Anvil本地节点

启动Anvil:

go 复制代码
anvil

预期输出:

go 复制代码
Available Accounts
==================
(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
(1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
...

Private Keys
==================
(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
...


Listening on 127.0.0.1:8545

15.6 编写和运行部署脚本

查看部署脚本:

go 复制代码
cat script/Counter.s.sol

设置环境变量:

go 复制代码
export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

模拟执行:

go 复制代码
forge script script/Counter.s.sol --rpc-url http://127.0.0.1:8545

发送真实交易:

go 复制代码
forge script script/Counter.s.sol --rpc-url http://127.0.0.1:8545 --broadcast

16.7 使用Cast与合约交互

查询合约状态:

go 复制代码
cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 "number()" --rpc-url http://127.0.0.1:8545

调用函数:

go 复制代码
cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 "increment()" \
  --private-key $PRIVATE_KEY \
  --rpc-url http://127.0.0.1:8545

Gas估算:

go 复制代码
cast estimate 0x5FbDB2315678afecb367f032d93F642f64180aa3 "increment()" \
  --rpc-url http://127.0.0.1:8545

15.8 查询链信息

查询链ID:

go 复制代码
cast chain-id --rpc-url http://127.0.0.1:8545

查询区块号:

go 复制代码
cast block-number --rpc-url http://127.0.0.1:8545

查询Gas价格:

go 复制代码
cast gas-price --rpc-url http://127.0.0.1:8545

15.9 演示总结

通过这个完整的演示,我们完成了:

  • 创建和初始化Foundry项目
  • 查看和编译合约
  • 运行测试并查看结果
  • 启动Anvil本地节点
  • 编写和运行部署脚本
  • 在本地节点上部署合约
  • 使用Cast与合约交互
  • 进行Gas估算
  • 查询账户余额和链信息

整个流程展示了Foundry工具链的强大功能。在实际开发中,你可以使用Anvil进行本地测试,使用Forge进行编译和测试,使用Cast进行链上交互,最后部署到测试网或主网。

相关推荐
lsrsyx20 小时前
TEBBIT 交易所:在数字资产时代构筑信任与性能的新标杆
区块链
小明的小名叫小明1 天前
Aave协议(3)
区块链·defi
Zoey的笔记本1 天前
构建去中心化协作引擎:基于开源框架的Web3团队项目管理实践
开源·web3·区块链
Sui_Network1 天前
Sui 2025 年终回顾:Sui 技术栈篇
大数据·人工智能·科技·去中心化·区块链
Dream Algorithm1 天前
RWA : Real World Asset
大数据·区块链
Yyyyy123jsjs1 天前
外汇Tick数据交易时段详解与Python实战分析
人工智能·python·区块链
Wnq100722 天前
去中心化控制系统搭建与实施方案
去中心化·区块链
DICOM医学影像2 天前
13. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - sepolia测试网
区块链·solidity·以太坊·web3.0·sepolia·hardhat
小明的小名叫小明2 天前
Aave协议(2)
架构·区块链·defi