Foundry是一个Solidity框架,用于构建、测试、模糊、调试和部署Solidity智能合约
Foundry是一个Solidity框架,用于构建、测试、模糊、调试和部署Solidity智能合约。在这个Foundry教程中,我们将介绍以下内容。
1. Foundry简介 [视频]
2. Foundry入门
3. 使用Foundry的Hardhat
4. 使用 Solidity 测试
5. Foundry作弊代码
6. 部署和使用合约
## Foundry简介
我制作了一个[视频],你可以在YouTube上观看:https://youtu.be/VhaP9kYvlOA
智能合约通常是用Solidity编写的,但使用Javascript框架(如Truffle或Hardhat)进行测试和部署。Foundry提供了一套在Rust中构建的工具,允许区块链开发者在Solidity中编写测试,并通过命令行部署和与合约交互。
### 为什么用Foundry?
– 在solidity中编写单元测试,而不是Javascript
– 更快的编译和测试
– 内置的模糊测试
– Gas优化工具
– 支持主网分叉
– Etherscan 代码验证
– 硬件钱包兼容
– Solidity脚本
– 通过作弊代码操纵区块链状态
## 开始使用Foundry
### 安装说明
为了开始使用,我们需要安装foundry包,它需要rust。以下是linux、mac、windows和docker的命令。
**Linux/Mac**:
“`
curl -L https://foundry.paradigm.xyz | bash;
foundryup
“`
**Windows**: (需要 Rust,从 https://rustup.rs/ 安装)
“`
cargo install –git https://github.com/foundry-rs/foundry –bins –locked
“`
**Docker**:
“`
docker pull ghcr.io/foundry-rs/foundry:latest
“`
### 使用foundry的第一步
Foundry软件包带有两个主要的命令行功能:
– **forge** – 建立编译测试本地智能合约
– **cast** – 使用已部署的智能合约执行链上交易
如果想从Github上克隆一个repo,我们可以使用forge命令。
“`
forge install jamesbachini/myVault -hh
“`
这里我们使用的是Github用户名和版本库名称,加上-hh 参数,用于迁移Hardhat版本库。
我们也可以用 “myrepo” 初始化一个新的版本库。
“`
forge init myrepo
“`
然后就可以继续编译和测试智能合约了
“`
forge build
forge test
“`

注意测试是如何通过的,还得到了测试交易的Gas成本
——
## 如何使用 Hardhat 设置 Foundry
假设我们已经按照上面的说明安装了 Foundry,我们就可以与 [Hardhat](https://hardhat.org/) 一起使用它。
注意,我们需要为当前工作目录设置一个 repo,所以如果还没有的话,请使用 git init。
然后复制并粘贴以下内容到版本库根目录下的一个新的*foundry.toml*文件中:
“`toml
[default]
src = ‘contracts’
test = ‘test’
out = ‘artifacts/contracts’
libs = [‘lib’]
“`
这将设置目录结构以便与 Hardhat 保持一致。Foundry 测试可以使用正常的 MyContract.t.sol 命名放在标准测试文件夹中。
> 从这里我们可以在Hardhat 中使用Foundry进行测试和部署。就我个人而言,我喜欢 Hardhat 的脚本环境(特别是对于复杂的部署),但也认识到使用 Foundry 进行测试和模糊处理的好处。在同一个代码库中使用这两个应用程序,可以提供两个最佳选择。
## 使用 Solidity 测试
在我们开始编写单元测试之前,需要安装标准库
“`
forge install foundry-rs/forge-std
“`
然后我们可以将其导入测试文件中,该文件的名称将与我们的合约相同,后缀是.t.sol。
“`solidity
pragma solidity ^0.8.13;
import “forge-std/Test.sol”;
contract MyContractTest is Test {
testWhatever(uint256 var1) public {
uint256 var2 = 1;
assertEq(var1,var2);
}
}
“`
请注意,在上面的例子中,我们给函数名加上了一个前缀test -> testWhatever(),此时出现任何revert都将失败。我们也可以把testFail -> testFailWhatever()作为前缀,这样就需要revert才能通过。
另外请注意,在上面的例子中,我们在var1中传入了一个uint256变量。这个值将被**fuzzed(模糊)**,这意味着它将用各种不常用的值在函数上循环,试图创造一个边缘情况,导致回退(revert)。在这种情况下,任何不是1的值都会由于assertEq()函数而导致测试失败。
也可以创建自定义断言,例如:
“`solidity
function myAssertion(uint a, uint b) {
if (a != b) {
emit log_string(“a != b”);
fail();
}
}
“`
然后,我们可以在整个合约中使用这个断言,或者建立一个自定义断言库,并类似于我们先前导入标准库的方式来导入它们。
如果代码库包含许多不同的智能合约,可以使用` –match-contract`将单个合约和它的依赖关系分离出来,甚至可以使用`–match-test`命令行选项进行特定测试。
“`
forge test –match-test optionalSpecificTest –match-contract optionalSpecificContract
“`
另外,我们可以使用forge run来执行一个单一的solidity *”脚本 “*。
“`
forge run src/Contract.sol // 执行单个脚本
forge run src/Contract.sol –debug // open script in debugger
forge run src/Contract.sol –sig “foo(string)” “hi” // 执行单一函数
“`
一旦我们发现了一个bug,我们可以使用-v命令来提高提示程度并获得更多细节。
“`
debug with logs -vv
debug with traces for failing tests -vvv
debug with traces for all tests -vvvv
“`
追踪(Traces)是一种非常强大的方式,可以仔细观察智能合约执行过程中发生的事情。这有点像在命令行上有[Tenderly.co](https://tenderly.co/)。
还有一个交互式调试器(debugger),这让我想起了1990年的Linux调试器。我没有经常使用,但这里有更多的说明: https://book.getfoundry.sh/forge/debugger.html
“`
forge test –debug “testSomething”
“`
可以在本地分叉(fork)一个区块链,然后关联外部智能合约一起测试我们的合约。如果你想与其他defi协议交互,例如Uniswap,或者使用真实的市场数据执行压力测试,这很有用。
“`bash
forge test –fork-url https://eth-mainnet.alchemyapi.io/v2/abc123alchmeyApiKey
“`
### Gas优化
编译时的合约Gas报告可以通过foundry.toml配置来设置
“`
gas_reports = [“MyContract”, “MyContractFactory”]
“`
然后用*forge test -gas-report*选项执行命令。
优化函数的一个方法是使用测试合约,并在修改前后进行快照对比:
“`
forge snapshot –snap gas1.txt
// make some changes
forge snapshot –diff gas1.txt
“`
这将提供之前的Gas报告和当前快照之间的差异。
### 用Slither进行安全分析
当涉及到智能合约安全时,Slither绝不是一个简单的解决方案,但它是有用的,并提供了一些自动检查,如检查重入错误。为了使用slither,我一般会切换到WSL(linux的Windows子系统),它可以用以下命令安装(注意0.8.13是目前foundry演示合约中使用的solc版本。你可以把它修改为Solidity文件中设置的任何版本)。
“`
apt install python3-pip
pip3 install slither-analyzer
pip3 install solc-select
solc-select install 0.8.13
solc-select use 0.8.13
“`
然后把以下复制到我们工作的主目录下一个叫slither.config.json的文件里
“`
{
“filter_paths”: “lib”,
“solc_remaps”: [
“ds-test/=lib/ds-test/src/”,
“forge-std/=lib/forge-std/src/”
]
}
“`
然后运行一个测试,使用
“`
slither src/Contract.sol
“`
在官方readme中有更多关于slither的信息:https://github.com/crytic/slither
## Foundry作弊代码
Foundry有一套作弊代码,它可以对区块链的状态进行修改,以方便在测试时使用。这些代码可以直接执行合约:0x7109709ECfa91a80626fF3989D68f67F5b1DD12D 进行调用,但更多时候是通过标准库和vm对象执行。
**重要的作弊代码有:**
– **vm.warp(uint256) external;** 设置 block.timestamp
– **vm.roll(uint256) external;** 设置 block.height.
– **vm.prank(address) external;** 设置地址作为下一次调用的msg.sender
– **vm.startPrank(address) external;** 设置地址作为所有后续调用的msg.sender
– **vm.stopPrank() external;** 重置后续调用msg.sender为`address(this)`。
– **vm.deal(address, uint256) external;** 设置一个地址的余额,参数:(who,newBalance)。
– **vm.expectRevert(bytes calldata) external;** 期待下次调用时出现错误。
– **vm.record() external;** 记录所有存储的读和写。
– **vm.expectEmit(true, false, false, false); emit Transfer(address(this)); transfer();** 检查事件主题1在两个事件中是否相等
– **vm.load(address,bytes32)外部返回(bytes32);** 从一个地址加载一个存储槽
– **vm.store(address,bytes32,bytes32) external;** 将一个值存储到一个地址的存储槽中,参数(who, slot, value)。
这些可以用来改变测试的过程,如在这个例子中,告诉测试套件在调用时期望一个标准的算术错误。
“`
vm.expectRevert(stdError.arithmeticError);
“`
完整的列表在这里:https://github.com/foundry-rs/forge-std/blob/master/src/Vm.sol
## 部署和使用合约
Foundry也可以用来部署并与智能合约交互。
要部署一个合约,我们可以使用下面的命令:
“`
forge create –rpc-url https://mainnet.infura.io –private-key abc123456789 src/MyContract.sol:MyContract –constructor-args “Hello Foundry” “Arg2”
“`
注意我们在生产中部署时不应该使用硬编码的私钥。一个选择是使用-ledger或-trezor来通过硬件钱包执行。另外,也可以使用环境变量来存储私钥。
另一种环境变量来存储私钥:
**Linux/Mac**:
–priva*teKey $privateKey*.
“`
export privateKey=abc123
“`
**Windows Powershell**:
–privateKey *$env:privateKey*
“`
$env:privateKey = “0x123abc”
“`
私钥不应该包含0x前缀,否则你会得到一个错误 “Invalid character ‘x’ at position 1(无效字符’x’在位置1)”
我们还可以使用forge命令**在etherscan上验证合约**,以便我们能够使用Etherscan的UI和Metamask与之交互。
“`
forge verify-contract –chain-id 1 –num-of-optimizations 200 –constructor-args (cast abi-encode “constructor(string)” “Hello Foundry” –compiler-version v0.8.10+commit.fc410830 0xContractAddressHere src/MyContract.sol:MyContract ABCetherscanApiKey123
“`
也可以使用forge来**扁平化合约**,其中包括外部合约的依赖关系合并到一个文件中:
“`
forge flatten –output src/MyContract.flattened.sol src/MyContract.sol
“`
要生成ABI,可以使用以下命令:
“`
forge inspect src/MyContract.sol abi
“`
注意任何ABI都可以转换为接口并直接在solidity中使用: https://gnidan.github.io/abi-to-sol/
如果合约已经被验证,我们也可以使用以下命令来生成一个接口。
“`
cast interface 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
“`
### 用 Cast 交互
我们可以call 方式调用合约请求链上数据。我们也可以提供凭证(私钥)来发送一个交易,就像我们在metamask中签署一个交易一样。
“`
cast call 0xabc123 “totalSupply()(uint256)” –rpc-url https://eth-mainnet.alchemyapi.io
cast send 0xabc123 “mint(uint256)” 3 –rpc-url https://eth-mainnet.alchemyapi.io –private-key=abc123
“`
一旦区块被确认,也可以获取交易本身的信息:
“`
cast tx 0xa1588a7c58a0ac632a9c7389b205f3999b7caee67ecb918d07b80f859aa605fd
“`
也可以通过flashbots保护执行交易,cast使用`–flashbots` 参数发送交易。
最后,你可能想估算一个Gas成本,这也可以用cast来完成:
“`
cast estimate 0xabc123 “mint(uint256)” 3 –rpc-url https://eth-mainnet.alchemyapi.io –private-key=abc123
“`
Foundry为测试和审计智能合约提供了一个快速、高效的框架。我希望这个教程是有用的,如果想进一步深入了解,别忘了查看官方文档。[https://book.getfoundry.sh](https://book.getfoundry.sh/)
——
本翻译由 [Duet Protocol](https://duet.finance/?utm_souce=learnblockchain) 赞助支持。
- 原文:https://jamesbachini.com/foundry-tutorial/ 作者:james
- 译文出自:区块链开发网翻译计划
- 译者:翻译小组
- 校对:Tiny 熊
- 本文永久链接:learnblockchain.cn/article…
Foundry是一个Solidity框架,用于构建、测试、模糊、调试和部署Solidity智能合约。在这个Foundry教程中,我们将介绍以下内容。
- Foundry简介 [视频]
- Foundry入门
- 使用Foundry的Hardhat
- 使用 Solidity 测试
- Foundry作弊代码
- 部署和使用合约
Foundry简介
我制作了一个[视频],你可以在YouTube上观看:https://youtu.be/VhaP9kYvlOA
智能合约通常是用Solidity编写的,但使用Javascript框架(如Truffle或Hardhat)进行测试和部署。Foundry提供了一套在Rust中构建的工具,允许区块链开发者在Solidity中编写测试,并通过命令行部署和与合约交互。
为什么用Foundry?
- 在solidity中编写单元测试,而不是Javascript
- 更快的编译和测试
- 内置的模糊测试
- Gas优化工具
- 支持主网分叉
- Etherscan 代码验证
- 硬件钱包兼容
- Solidity脚本
- 通过作弊代码操纵区块链状态
开始使用Foundry
安装说明
为了开始使用,我们需要安装foundry包,它需要rust。以下是linux、mac、windows和docker的命令。
Linux/Mac:
curl -L https://foundry.paradigm.xyz | bash;
foundryup
Windows: (需要 Rust,从 https://rustup.rs/ 安装)
cargo install --git https://github.com/foundry-rs/foundry --bins --locked
Docker:
docker pull ghcr.io/foundry-rs/foundry:latest
使用foundry的第一步
Foundry软件包带有两个主要的命令行功能:
- forge – 建立编译测试本地智能合约
- cast – 使用已部署的智能合约执行链上交易
如果想从Github上克隆一个repo,我们可以使用forge命令。
forge install jamesbachini/myVault -hh
这里我们使用的是Github用户名和版本库名称,加上-hh 参数,用于迁移Hardhat版本库。
我们也可以用 “myrepo” 初始化一个新的版本库。
forge init myrepo
然后就可以继续编译和测试智能合约了
forge build
forge test
注意测试是如何通过的,还得到了测试交易的Gas成本
如何使用 Hardhat 设置 Foundry
假设我们已经按照上面的说明安装了 Foundry,我们就可以与 Hardhat 一起使用它。
注意,我们需要为当前工作目录设置一个 repo,所以如果还没有的话,请使用 git init。
然后复制并粘贴以下内容到版本库根目录下的一个新的foundry.toml文件中:
[default]
src = 'contracts'
test = 'test'
out = 'artifacts/contracts'
libs = ['lib']
这将设置目录结构以便与 Hardhat 保持一致。Foundry 测试可以使用正常的 MyContract.t.sol 命名放在标准测试文件夹中。
从这里我们可以在Hardhat 中使用Foundry进行测试和部署。就我个人而言,我喜欢 Hardhat 的脚本环境(特别是对于复杂的部署),但也认识到使用 Foundry 进行测试和模糊处理的好处。在同一个代码库中使用这两个应用程序,可以提供两个最佳选择。
使用 Solidity 测试
在我们开始编写单元测试之前,需要安装标准库
forge install foundry-rs/forge-std
然后我们可以将其导入测试文件中,该文件的名称将与我们的合约相同,后缀是.t.sol。
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
contract MyContractTest is Test {
testWhatever(uint256 var1) public {
uint256 var2 = 1;
assertEq(var1,var2);
}
}
请注意,在上面的例子中,我们给函数名加上了一个前缀test -> testWhatever(),此时出现任何revert都将失败。我们也可以把testFail -> testFailWhatever()作为前缀,这样就需要revert才能通过。
另外请注意,在上面的例子中,我们在var1中传入了一个uint256变量。这个值将被fuzzed(模糊),这意味着它将用各种不常用的值在函数上循环,试图创造一个边缘情况,导致回退(revert)。在这种情况下,任何不是1的值都会由于assertEq()函数而导致测试失败。
也可以创建自定义断言,例如:
function myAssertion(uint a, uint b) {
if (a != b) {
emit log_string("a != b");
fail();
}
}
然后,我们可以在整个合约中使用这个断言,或者建立一个自定义断言库,并类似于我们先前导入标准库的方式来导入它们。
如果代码库包含许多不同的智能合约,可以使用--match-contract
将单个合约和它的依赖关系分离出来,甚至可以使用--match-test
命令行选项进行特定测试。
forge test --match-test optionalSpecificTest --match-contract optionalSpecificContract
另外,我们可以使用forge run来执行一个单一的solidity “脚本 “。
forge run src/Contract.sol // 执行单个脚本
forge run src/Contract.sol --debug // open script in debugger
forge run src/Contract.sol --sig "foo(string)" "hi" // 执行单一函数
一旦我们发现了一个bug,我们可以使用-v命令来提高提示程度并获得更多细节。
debug with logs -vv
debug with traces for failing tests -vvv
debug with traces for all tests -vvvv
追踪(Traces)是一种非常强大的方式,可以仔细观察智能合约执行过程中发生的事情。这有点像在命令行上有Tenderly.co。
还有一个交互式调试器(debugger),这让我想起了1990年的Linux调试器。我没有经常使用,但这里有更多的说明: https://book.getfoundry.sh/forge/debugger.html
forge test --debug "testSomething"
可以在本地分叉(fork)一个区块链,然后关联外部智能合约一起测试我们的合约。如果你想与其他defi协议交互,例如Uniswap,或者使用真实的市场数据执行压力测试,这很有用。
forge test --fork-url https://eth-mainnet.alchemyapi.io/v2/abc123alchmeyApiKey
Gas优化
编译时的合约Gas报告可以通过foundry.toml配置来设置
gas_reports = ["MyContract", "MyContractFactory"]
然后用forge test -gas-report选项执行命令。
优化函数的一个方法是使用测试合约,并在修改前后进行快照对比:
forge snapshot --snap gas1.txt
// make some changes
forge snapshot --diff gas1.txt
这将提供之前的Gas报告和当前快照之间的差异。
用Slither进行安全分析
当涉及到智能合约安全时,Slither绝不是一个简单的解决方案,但它是有用的,并提供了一些自动检查,如检查重入错误。为了使用slither,我一般会切换到WSL(linux的Windows子系统),它可以用以下命令安装(注意0.8.13是目前foundry演示合约中使用的solc版本。你可以把它修改为Solidity文件中设置的任何版本)。
apt install python3-pip
pip3 install slither-analyzer
pip3 install solc-select
solc-select install 0.8.13
solc-select use 0.8.13
然后把以下复制到我们工作的主目录下一个叫slither.config.json的文件里
{
"filter_paths": "lib",
"solc_remaps": [
"ds-test/=lib/ds-test/src/",
"forge-std/=lib/forge-std/src/"
]
}
然后运行一个测试,使用
slither src/Contract.sol
在官方readme中有更多关于slither的信息:https://github.com/crytic/slither
Foundry作弊代码
Foundry有一套作弊代码,它可以对区块链的状态进行修改,以方便在测试时使用。这些代码可以直接执行合约:0x7109709ECfa91a80626fF3989D68f67F5b1DD12D 进行调用,但更多时候是通过标准库和vm对象执行。
重要的作弊代码有:
- vm.warp(uint256) external; 设置 block.timestamp
- vm.roll(uint256) external; 设置 block.height.
- vm.prank(address) external; 设置地址作为下一次调用的msg.sender
- vm.startPrank(address) external; 设置地址作为所有后续调用的msg.sender
- vm.stopPrank() external; 重置后续调用msg.sender为
address(this)
。 - vm.deal(address, uint256) external; 设置一个地址的余额,参数:(who,newBalance)。
- vm.expectRevert(bytes calldata) external; 期待下次调用时出现错误。
- vm.record() external; 记录所有存储的读和写。
- vm.expectEmit(true, false, false, false); emit Transfer(address(this)); transfer(); 检查事件主题1在两个事件中是否相等
- vm.load(address,bytes32)外部返回(bytes32); 从一个地址加载一个存储槽
- vm.store(address,bytes32,bytes32) external; 将一个值存储到一个地址的存储槽中,参数(who, slot, value)。
这些可以用来改变测试的过程,如在这个例子中,告诉测试套件在调用时期望一个标准的算术错误。
vm.expectRevert(stdError.arithmeticError);
完整的列表在这里:https://github.com/foundry-rs/forge-std/blob/master/src/Vm.sol
部署和使用合约
Foundry也可以用来部署并与智能合约交互。
要部署一个合约,我们可以使用下面的命令:
forge create --rpc-url https://mainnet.infura.io --private-key abc123456789 src/MyContract.sol:MyContract --constructor-args "Hello Foundry" "Arg2"
注意我们在生产中部署时不应该使用硬编码的私钥。一个选择是使用-ledger或-trezor来通过硬件钱包执行。另外,也可以使用环境变量来存储私钥。
另一种环境变量来存储私钥: Linux/Mac: –privateKey $privateKey.
export privateKey=abc123
Windows Powershell:
–privateKey $env:privateKey
$env:privateKey = "0x123abc"
私钥不应该包含0x前缀,否则你会得到一个错误 “Invalid character ‘x’ at position 1(无效字符’x’在位置1)”
我们还可以使用forge命令在etherscan上验证合约,以便我们能够使用Etherscan的UI和Metamask与之交互。
forge verify-contract --chain-id 1 --num-of-optimizations 200 --constructor-args (cast abi-encode "constructor(string)" "Hello Foundry" --compiler-version v0.8.10+commit.fc410830 0xContractAddressHere src/MyContract.sol:MyContract ABCetherscanApiKey123
也可以使用forge来扁平化合约,其中包括外部合约的依赖关系合并到一个文件中:
forge flatten --output src/MyContract.flattened.sol src/MyContract.sol
要生成ABI,可以使用以下命令:
forge inspect src/MyContract.sol abi
注意任何ABI都可以转换为接口并直接在solidity中使用: https://gnidan.github.io/abi-to-sol/
如果合约已经被验证,我们也可以使用以下命令来生成一个接口。
cast interface 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
用 Cast 交互
我们可以call 方式调用合约请求链上数据。我们也可以提供凭证(私钥)来发送一个交易,就像我们在metamask中签署一个交易一样。
cast call 0xabc123 "totalSupply()(uint256)" --rpc-url https://eth-mainnet.alchemyapi.io
cast send 0xabc123 "mint(uint256)" 3 --rpc-url https://eth-mainnet.alchemyapi.io --private-key=abc123
一旦区块被确认,也可以获取交易本身的信息:
cast tx 0xa1588a7c58a0ac632a9c7389b205f3999b7caee67ecb918d07b80f859aa605fd
也可以通过flashbots保护执行交易,cast使用--flashbots
参数发送交易。
最后,你可能想估算一个Gas成本,这也可以用cast来完成:
cast estimate 0xabc123 "mint(uint256)" 3 --rpc-url https://eth-mainnet.alchemyapi.io --private-key=abc123
Foundry为测试和审计智能合约提供了一个快速、高效的框架。我希望这个教程是有用的,如果想进一步深入了解,别忘了查看官方文档。https://book.getfoundry.sh
本翻译由 Duet Protocol 赞助支持。
本文参与区块链开发网写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2022-08-11 11:28
- 阅读 ( 520 )
- 学分 ( 67 )
- 分类:智能合约