Foundry是一个专为以太坊智能合约开发设计的快速、现代化的工具链,集成了编译、测试、部署和调试等功能。它用Rust编写,性能卓越,已成为Solidity开发者的首选工具之一。
Foundry 是一个专为以太坊智能合约开发设计的快速、现代化的工具链,集成了编译、测试、部署和调试等功能。它用 Rust 编写,性能卓越,已成为 Solidity 开发者的首选工具之一。 Foundry 中文文档
Foundry 由四个主要工具组成:
Forge 是 Foundry
附带的命令行工具。 Forge 可用来测试、构建和部署你的智能合约。
日志和跟踪
forge test
的默认行为是只显示通过和失败测试的摘要。 你可以通过增加详细程度(使用-v
标志)来控制此行为。 每个详细级别都会添加更多信息:
-vv
):还会显示测试期间发出的日志。 这包括来自测试的断言错误,显示诸如预期与实际结果等之类的信息。-vvv
):还显示失败测试的堆栈跟踪。-vvvv
):显示所有测试的堆栈跟踪,并显示失败测试的设置(setup)跟踪。-vvvvv
):始终显示堆栈跟踪和设置(setup)跟踪。Watch模式
当你使用forge test --watch
对文件进行更改时,Forge 可以重新运行你的测试。
默认情况下,仅重新运行更改的测试文件。 如果你想重新运行更改的所有测试,你可以使用 forge test --watch --run-all
。
Foundryup 是 Foundry 工具链的官方安装程序。 要安装 Foundryup,打开终端并运行以下命令:
curl -L https\://foundry.paradigm.xyz | bash
运行 foundryup
将自动安装最新的版本的 预编译二进制文件:forge
、cast
、anvil
和 chisel
。
foundryup # 安装或更新 Foundry 工具链
我们可以使用 Forge 命令行工具,并用它来创建、编译、测试合约项目。
forge init
forge init hello_foundry
项目结构:
$ cd hello_foundry
$ tree -L 2
.
├── README.md
├── foundry.toml
├── lib
│ └── forge-std
├── script
│ └── Counter.s.sol
├── src
│ └── Counter.sol
└── test
└── Counter.t.sol
6 directories, 5 files
src:智能合约目录
script :部署脚本文件
lib: 依赖库目录
test:智能合约测试用例文件夹
foundry.toml:配置文件,配置连接的网络URL 及编译选项。
forge build
$ forge build
Compiling 27 files with Solc 0.8.19
Solc 0.8.19 finished in 1.08s
Compiler run successful!
如果 foundry.toml 文件未指定 Solc 编译器版本,则默认使用最新的版本。编译好的文件(合约ABI,bytecode ),会放在out的文件夹中。
forge test
$ forge test
No files changed, compilation skipped
Ran 2 tests for test/Counter.t.sol:CounterTest
[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 30977, ~: 31288)
[PASS] test_Increment() (gas: 31303)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 8.10ms (7.84ms CPU time)
Ran 1 test suite in 9.41ms (8.10ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
out
目录包含你的合约工件(artifact,例如 ABI,而 cache
目录被 forge
使用来(记录),以便仅仅去重新编译那些必要编译的内容。
关于测试的一些约定:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
counter.setNumber(0);
}
function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
}
function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}
其中的 setUp 函数用于测试前的初始化:创建新合约并为状态变量赋初始值;test_xxx 为被 forge 自动调用的测试函数,assertEq 用来断言修改后的状态变量跟预期值相等,当然还有 assertNotEq 函数用来断言两个数不相等。
Forge 使用 forge create 命令可以部署&验证合约到指定网络,部署时可以完全使用命令行来实现,也可以使用 solidity-scripting 的方式实现。
命令行部署
forge create --rpc-url <your_rpc_url> --private-key <your_private_key> --verify src/MyContract.sol:MyContract --constructor-args <constructor_args>
rpc-url: 即区块链节点 RPC,例如:https://eth-sepolia.g.alchemy.com/v2/xxxxxxxxx
private-key: 即钱包私钥,建议创建专门用来开发测试的新钱包。
etherscan-api-key: 即区块链浏览器的 API KEY TOKEN,用于验证合约。
verify: 验证合约,即在浏览器中开源合约的代码
MyContract: 实际部署的合约,由于一个 solidity 中允许存在多个合约,因此这里指定需要部署的合约名称。
constructor-args: 合约的构造参数,如果没有,可以不设置该属性
执行结果中会打印出部署的交易 hash 、部署的合约地址以及验证状态:
> forge create --rpc-url https://eth-sepolia.g.alchemy.com/v2/xxxxxx \
--constructor-args "MyToken" "MT" 18 1000000000000000000000 \
--private-key 0xxxxxxx \
--etherscan-api-key xxxx \
--verify
src/MyToken.sol:MyToken
[⠊] Compiling...
No files changed, compilation skipped
Deployer: 0x5CB8896Db7Bf13DE6A6EA362866288e577e4F6C5
Deployed to: 0x9C7DcF024b94d14FFaA04262139f92F3AA837919
Transaction hash: 0x18ac7e8824b1fe19ab79977ec7672f8e8324f621fef01a7c8d971fca152748b8
Starting contract verification...
Waiting for etherscan to detect contract deployment...
Start verifying contract `0x9C7DcF024b94d14FFaA04262139f92F3AA837919` deployed on sepolia
Contract [src/MyToken.sol:MyToken] "0x9C7DcF024b94d14FFaA04262139f92F3AA837919" is already verified. Skipping verification.
脚本部署
通过 forge create 命令进行合约的部署&验证需要输入的参数较多,更推荐的做法是使用 solidity-scripting,它是一种使用 Solidity 以声明方式部署合约的方法,而不是 Javascript。
1.创建 .env 文件保存隐私信息(如:节点 RPC、私钥等),.env 文件应遵循以下格式:
1// 区块链 RPC 节点地址
2SEPOLIA_RPC_URL=xxxx
3// 钱包私钥
4PRIVATE_KEY=xxxx
5// 区块链浏览器的 API KEY TOKEN
6ETHERSCAN_API_KEY=xxxx
2.编辑 foundry.toml 文件,将以下行添加到文件末尾,这里指定了 .env 中配置的变量。
1[rpc_endpoints]
2sepolia = "${SEPOLIA_RPC_URL}"
3
4[etherscan]
5sepolia = { key = "${ETHERSCAN_API_KEY}" }
3.创建测试脚本。项目根目录中创建一个文件夹并将其命名为 script,并在其中创建一个名为 MyToken.s.sol 的文件,这是我们将创建部署脚本的地方。如下:
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.25;
3
4import {Script} from "forge-std/Script.sol";
5import "../src/MyToken.sol";
6
7contract MyTokenScript is Script {
8
9 function run() external {
10 uint256 deployer = vm.envUint("PRIVATE_KEY");
11
12 vm.startBroadcast(deployer);
13
14 MyToken myToken = new MyToken("MyToken", "MT", 18, 1000000000000000000);
15 vm.stopBroadcast();
16 }
17}
这个部署脚本本身就是一个智能合约,因此它就像任何其他用 Solidity 编写的智能合约一样,必须指定 pragma version。同时,继承 Forge 标准库中的 Script 合约 contract MyTokenScript is Script ,默认情况下,脚本是通过调用名为 run 的函数(我们的入口点)来执行的。
在项目的根目录执行以下命令:
# 加载 .env 文件中的变量
2source .env
3
4# 部署并验证合约
5forge script script/MyToken.s.sol:MyTokenScript --rpc-url $SEPOLIA_RPC_URL --broadcast --verify -vvvv
Forge 将运行我们的脚本并为我们广播交易——这可能需要一些时间,因为 Forge 还将等待交易收据。 大约一分钟后,您应该会看到类似这样的内容,包含了合约的部署地址、gas 消耗、交易 hash 以及验证结果等。
Cast 是 Foundry 用于执行以太坊 RPC 调用的命令行工具。 你可以进行智能合约调用、发送交易或检索任何类型的链数据——所有这些都来自你的命令行!
要使用 Cast,请在命令行工具运行 cast 命令,然后运行子命令:
1$ cast <subcommand>
比如我们可以使用 cast 来获取 DAI 代币的总供应量:
1
2cast call 0x6b175474e89094c44da98b954eedeac495271d0f "totalSupply()(uint256)" --rpc-url <your rpc url> 8603853182003814300330472690
结果会输出:
13214023094757495931935215279 [3.214e27]
我们可以使用 cast 发送任意消息。 下面是在两个帐户之间发送消息的示例。
1cast send --private-key <Your Private Key> 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc $(cast from-utf8 "hello world") --rpc-url http://127.0.0.1:8545/
获取当前链的 ID
cast chain-id
21(0x1)
获取当前链的名称
cast chain
2ethlive
获取当前客户端的版本
cast client
5Geth/v1.13.13-stable-7f131dcb/linux-amd64/go1.21.7
获取当前 gas 价格
cast gas-price
210325245432
查询最新的区块号
cast block-number
219732815
获取指定区块的基础费用
cast basefee
211721855264
获取到指定区块的详细信息,如区块高度、时间戳、交易数等。
cast block 12345678
2{
3 "number": "12345678",
4 "timestamp": "1618304000",
5 "transactions": "210",
6 "miner": "0x..."
7 ......
8}
获取指定区块的时间戳
cast age
2Thu Apr 25 13:59:47 2024
获取特定以太坊账户地址或 ENS 名称的当前余额,单位是 wei。这个命令是日常区块链操作中经常使用的,无论是在开发智能合约、执行交易前的检查,还是简单地监控账户状态。
cast balance 0x123...
使用 ENS 名称查询余额
cast balance vitalik.eth
使用 Cast 工具可以发送交易,并与已部署在以太坊链上的智能合约进行交互。掌握这些技能对于智能合约开发者来说至关重要,它允许你执行合约函数、触发合约的 fallback 和 receive 函数。
调用合约上的任何函数
cast send --private-key <private_key_addr> <contract_addr> "exampleFunc(uint256)" <argument_value_of_the_function>
示例:
cast send --private-key 0x123... 0xabc... "deposit(uint256)" 10
这条命令会向地址为 0xabc... 的合约发送一个调用 deposit 函数的请求,存款金额为 10 wei。
触发 Fallback 函数 如果调用合约中不存在的函数,将自动触发 Fallback 函数。这可以用于测试合约的异常处理或特定的功能。
cast send --private-key <private_key_addr> <contract_addr> "dummy()"
示例:
cast send --private-key 0x123... 0xabc... "dummy()"
触发 Receive 函数
通过向合约发送以太币(Ether),可以触发合约的 Receive 函数。这对于接受捐款或处理支付非常有用。
cast send --private-key <private_key_addr> <contract_addr> --value 10gwei
示例: 以下命令展示了如何发送 10 gwei 的以太币到合约,触发接收函数:
cast send --private-key 0x123... 0xabc... --value 10gwei
获取指定合约的源代码
cast etherscan-source <contract_address>
Anvil 是 Foundry 附带的本地测试网节点,专为提供一个便于本地测试和开发的以太坊节点而设计。 你可以使用它从前端测试你的合约或通过 RPC 进行交互。
它是一个理想的工具,特别适合需要频繁迭代和即时反馈的开发过程。
只需在命令行中输入 anvil,它将自动启动一个本地节点。启动后,你将看到一系列已生成的开发账户和私钥,以及节点侦听的地址和端口信息。
anvil
输出:
Available Accounts
==================
(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH)
.....
Private Keys
==================
(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae........
.....
Chain ID
==================
31337
Base Fee
==================
1000000000
Gas Limit
==================
30000000
Genesis Timestamp
==================
1714205976
Listening on 127.0.0.1:8545
通过在命令行中执行 anvil -h,你可以查看所有 Anvil 提供的配置选项。这些选项涵盖了从账户管理到节点性能等多个方面,使得 Anvil 成为一个高度灵活和可配置的开发工具。
查看所有配置选项
anvil -h
执行此命令后,你将看到一个详细的帮助菜单,列出了所有可用的命令和参数,以及它们的用途和默认值。
生成和配置开发账户数量
默认情况下,Anvil 生成10个开发账户,但你可以通过以下命令指定生成更多或更少的账户:
anvil -a <NUM>
Number of dev accounts to generate and configure
[default: 10]
设置使用的 EVM 硬分叉版本
anvil --hardfork <HARDFORK>
设置节点监听的端口号
anvil -p, --port <PORT>
例如,设置为端口8546:
anvil -p 8546
Chisel 是一个 Solidity REPL("读取-评估-打印 循环 "的缩写),它允许开发人员编写和测试 Solidity 代码片段。它提供了一个交互式环境,用于编写和执行 Solidity 代码,同时还提供了一组内置命令,用于处理和调试您的代码。这种工具特别适合进行快速的代码实验和问题调试。
启动 Chisel 非常简单,只需在命令行中输入 chisel 即可。启动后,你可以直接在命令行中编写和测试 Solidity 代码。
chisel
!help
| !h
显示所有命令。
!quit
| !q
退出 Chisel。
!exec <command> [args]
| !e <command> [args]
执行 shell 命令并打印输出。
在 Chisel 中,你可以通过两种主要方式输入代码:表达式和语句。
表达式:
获取地址余额:
1address(0).balance
编码多个参数:
1abi.encode(256, bytes32(0), "Chisel!")
调用视图函数:
1myViewFunc(128)
语句:
语句是指那些旨在保持或修改会话状态的代码片段。这包括变量定义、不改变状态的函数调用以及合约、函数、事件、错误、映射或结构体的定义。要将表达式作为语句执行,可以在其末尾添加分号(;)。 定义变量:
1uint256 a = 0xa57b;
调用状态修改函数或多个函数:
1myStateMutatingFunc(128) || myViewFunc(128);
定义内部函数:
1function hash64(bytes32 _a, bytes32 _b) internal pure returns (bytes32 _hash) {
2 assembly {
3 // Store the 64 bytes we want to hash in scratch space
4 mstore(0x00, _a)
5 mstore(0x20, _b)
6
7 // Hash the memory in scratch space
8 // and assign the result to `_hash`
9 _hash := keccak256(0x00, 0x40)
10 }
11}
定义事件和结构体:
1event ItHappened(bytes32 indexed hash);
2struct Complex256 { uint256 re; uint256 im; }
brew install tree
Solc(全称 Solidity Compiler)是 Solidity 编程语言的官方编译器,用于将以太坊智能合约的 Solidity 代码编译成 EVM(以太坊虚拟机)字节码,使合约能够在以太坊区块链或兼容的 EVM 链(如 BSC、Polygon 等)上部署和运行。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!