{"content":{"title":"Foundry 常用命令和作弊码速查表","body":">- 原文链接：https://milotruck.github.io/blog/Foundry-Cheatsheet/ \r\n>- 译文出自：[登链翻译计划](https://github.com/lbc-team/Pioneer)\r\n>- 译者：[翻译小组](https://learnblockchain.cn/people/412) ，校对：[Tiny 熊](https://learnblockchain.cn/people/15)\r\n>- 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/7344)\r\n\r\n\r\n\r\n[Foundry](https://learnblockchain.cn/docs/foundry/i18n/zh/index.html)中常用命令和作弊码的速查表。\r\n\r\n还可以在 [gist](https://gist.github.com/MiloTruck/30cd9b051176da8b76fe7dc81996eaf9) 中找到此速查表。\r\n\r\n## 初始化\r\n\r\n### 创建新项目\r\n\r\n创建新项目：\r\n\r\n```\r\nforge init <project_name>\r\n```\r\n\r\n使用模板创建新项目：\r\n\r\n```\r\nforge init --template <template> <project_name>\r\n\r\n# 例如\r\nforge init --template https://github.com/zobront/paradigm-ctf paradigm_ctf\r\n```\r\n\r\n### 在现有项目中使用\r\n\r\n初始化 Foundry：\r\n\r\n```\r\n# Create required directories\r\nmkdir out lib\r\n\r\n# 添加 `forge-std` 模块到 `lib`\r\ngit submodule add https://github.com/foundry-rs/forge-std lib/forge-std\r\n\r\n# Create foundry.toml\r\ntouch foundry.toml\r\n```\r\n\r\n在`foundry.toml`中指定目录：\r\n\r\n```\r\n[profile.default]\r\nsrc = \"contracts\"\r\nout = \"out\"\r\nlib = \"lib\"\r\n```\r\n\r\n## 依赖项\r\n\r\n### 添加依赖项\r\n\r\n在现有项目中安装依赖项：\r\n\r\n```\r\nforge install\r\n```\r\n\r\n添加新依赖项：\r\n\r\n```\r\nforge install <dependency>\r\n\r\n# 例如\r\nforge install openzeppelin/openzeppelin-contracts\r\n```\r\n\r\n将依赖项添加到现有 git 存储库：\r\n\r\n```\r\n# 不生成 git commit\r\nforge install --no-commit <dependency>\r\n\r\n# 不生成 git 库\r\nforge install --no-git <dependency>\r\n```\r\n\r\n### 重映射\r\n\r\nForge 可以自动推断重映射：\r\n\r\n```\r\nforge remappings > remappings.txt\r\n```\r\n\r\n要自定义重映射，只需将其添加到`remappings.txt`中：\r\n\r\n```\r\necho \"@openzeppelin/=lib/openzeppelin-contracts/\" > remappings.txt\r\n```\r\n\r\n## 测试\r\n\r\n运行测试：\r\n\r\n```\r\nforge test\r\n```\r\n\r\n日志详细程度\r\n\r\n- `-vv` 显示`console.log`输出。\r\n- `-vvv` 显示失败测试的执行跟踪。\r\n- `-vvvv` 显示所有测试的执行跟踪，并显示失败测试的设置跟踪。\r\n- `-vvvvv` 显示所有测试的执行和设置跟踪。\r\n\r\n运行特定测试：\r\n\r\n- `--match-test` 运行与指定正则表达式匹配的测试。\r\n- `--match-contract` 运行与指定正则表达式匹配的合约中的测试。\r\n- `--match-path` 运行与指定路径匹配的源文件中的测试。\r\n\r\n### 分叉网络测试\r\n\r\n分叉网络：\r\n\r\n```\r\nforge test --fork-url <rpc_url>\r\n```\r\n\r\n要在分叉环境中识别合约，请使用`--etherscan-api-key`传递你的 Etherscan API 密钥：\r\n\r\n```\r\nforge test --fork-url <rpc_url> --etherscan-api-key <etherscan_api_key>\r\n```\r\n\r\n## Cheatcodes 作弊吗码\r\n\r\n参考[作弊码参考](https://learnblockchain.cn/docs/foundry/i18n/zh/cheatcodes/index.html)获取所有可用作弊码。\r\n\r\n### 上下文 context\r\n\r\n```\r\n// 设置 block.timestamp\r\nvm.warp(uint256 timestamp)\r\n\r\n// 增加指定秒数到 block.timestamp \r\nskip(uint256 time)\r\n\r\n// 减少指定秒数到 block.timestamp \r\nrewind(uint256 time) \r\n\r\n// 设置 block.number\r\nvm.roll(uint256 blockNumber)\r\n```\r\n\r\n#### 存储和内存\r\n\r\n```\r\n// 从地址上加载槽位\r\nvm.load(address account, bytes32 slot) \r\n\r\n// 写入数据到槽位\r\nvm.store(address account, bytes32 slot, bytes32 value)\r\n\r\n// 设置地址上的代码\r\nvm.etch(address addr, bytes calldata code)\r\n```\r\n\r\n### 调用者  Caller\r\n\r\n```\r\n// 为下一次调用设置 msg.sender\r\nvm.prank(address msgSender)\r\n\r\n// 为之后的一系列调用设置 msg.sender \r\nvm.startPrank(address msgSender)\r\n\r\n// 停止上次的设置\r\nvm.stopPrank()\r\n\r\n// 改变之后的一系列调用的 msg.sender \r\nchangePrank(address msgSender) \r\n\r\n// 设置 msg.sender 以及为下一次调用充 ether \r\nhoax(address who)\r\nhoax(address who, uint256 give)\r\nhoax(address who, address origin)\r\nhoax(address who, address origin, uint256 give)\r\n\r\n// 为之后的一系列调用设置msg.sender 以及充 ether \r\nstartHoax(address who)\r\nstartHoax(address who, uint256 give)\r\nstartHoax(address who, address origin)\r\nstartHoax(address who, address origin, uint256 give)\r\n```\r\n\r\n### 调用 call\r\n\r\n```solidity\r\n// 模拟对 `where` 的调用, 如果 `data` 匹配返回 `retdata`  \r\nvm.mockCall(address where, bytes calldata data, bytes calldata retdata);\r\n\r\n// 和上一条类似，但有 msg.value 需要匹配 `value`\r\nvm.mockCall(address where, uint256 value, bytes calldata data, bytes calldata retdata);\r\n```\r\n\r\n示例用法：\r\n\r\n```solidity\r\nfunction testMockCall() public {\r\n    // Without value\r\n    vm.mockCall(\r\n        address(token),\r\n        abi.encodeWithSelector(token.balanceOf.selector, ALICE),\r\n        abi.encode(10)\r\n    );\r\n    assertEq(token.balanceOf(ALICE), 10);\r\n\r\n    // With value\r\n    vm.mockCall(\r\n        address(market),\r\n        10 ether,\r\n        abi.encodeWithSignature(\"pay(address,uint256)\", ALICE, 10 ether),\r\n        abi.encode(true)\r\n    );\r\n    assertTrue(market.pay{value: 10 ether}(ALICE, 10 ether));\r\n}\r\n```\r\n\r\n### 回滚revert\r\n\r\n```\r\n// 期望一下一个调用revert \r\nvm.expectRevert()\r\n\r\n// 期望下一个调用 revert 并抛出 `message`\r\nvm.expectRevert(bytes calldata message)\r\n\r\n//  期望下一个调用 revert 并抛出  `bytes4 data` 错误(用于自定义的 error selectors)\r\nvm.expectRevert(bytes4 data)\r\n```\r\n\r\n### 快照\r\n\r\n```\r\n// 给当前状态快照\r\nuint256 snapshot = vm.snapshot();\r\n\r\n// 还原到快照\r\nvm.revertTo(uint256 snapshot);\r\n```\r\n\r\n## 标准库\r\n\r\n参考 [Forge Std 的`Test`](https://learnblockchain.cn/docs/foundry/i18n/zh/reference/forge-std/) 获取所有功能。\r\n\r\n### 余额\r\n\r\n```\r\n// 为某地址设置余额\r\ndeal(address to, uint256 balance)\r\n\r\n// 为某地址设置 ERC20 余额\r\ndeal(address token, address to, uint256 balance)\r\n\r\n//为某地址设置 ERC20 余额 ，如果 adjust 为 true，增加 totalSupply  \r\ndeal(address token, address to, uint256 balance, bool adjust)\r\n\r\n// 为某地址加指定 `id`  的 ERC721 token  \r\ndealERC721(address token, address to, uint256 id)\r\n\r\n// 为某地址加指定 `id`  指定数量的 ERC1155 token  \r\ndealERC1155(address token, address to, uint256 id, uint256 balance)\r\n\r\n// 为某地址加指定 `id`  指定数量的 ERC1155 token ， 如果 adjust 为 true，增加发行量  \r\ndealERC1155(address token, address to, uint256 id, uint256 balance, bool adjust)\r\n```\r\n\r\n### 错误\r\n\r\n来自 [`forge-std/std-errors`](https://learnblockchain.cn/docs/foundry/i18n/zh/reference/forge-std/std-errors.html)，用于捕获内部 Solidity 错误：\r\n\r\n- `stdError.assertionError` - `assert`失败。\r\n- `stdError.arithmeticError` - 算术运算失败（例如溢出/下溢）。\r\n- `stdError.divisionError` - 除法失败（例如除以零）。\r\n- `stdError.indexOOBError` - 访问超出范围的数组元素。\r\n- `stdError.popError` - 从空数组中弹出。不适用于外部合约中的空数组。\r\n- `stdError.enumConversionError` - 将大于枚举变体数量的数字转换为枚举。\r\n- `stdError.encodeStorageError` - 使用内联汇编访问损坏的存储中的数据。\r\n- `stdError.memOverflowError` - 分配具有超过 2^64-1 个项目的动态内存数组。\r\n- `stdError.zeroVarError` - 通过未初始化的函数指针调用函数。\r\n\r\n### 断言\r\n\r\n来自 [forge-std/std-assertions](https://learnblockchain.cn/docs/foundry/i18n/zh/reference/forge-std/std-assertions.html)。\r\n\r\n```\r\n// Fail a test with a message\r\nfail(string memory err)\r\n\r\n// Assert true/false\r\nassertTrue(bool data)\r\nassertTrue(bool data, string memory error)\r\n\r\nassertFalse(bool data)\r\nassertFalse(bool data, string memory err)\r\n\r\n// 断言相等\r\nassertEq(<type> a, <type> b)\r\nassertEq(<type> a, <type> b, string memory err)\r\n\r\n// Asserts `a` is approximately equal to `b` with delta in absolute value.\r\nassertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta)\r\nassertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err)\r\n\r\n// Asserts `a` is approximately equal to `b` with delta in percentage, where `1e18` is 100%. \r\nassertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta)\r\nassertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta, string memory err)\r\n```\r\n\r\n### 地址和密钥\r\n\r\n```\r\n// 创建一个标签地址\r\naddress addr = makeAddr(string memory name)\r\n\r\n// 创建一个标签地址 和私钥\r\n(address addr, uint256 privateKey) = makeAddrAndKey(string memory name)\r\n\r\n// 签名数据\r\n(uint8 v, bytes32 r, bytes32 s) = vm.sign(uint256 privateKey, bytes32 digest)\r\n```\r\n\r\n### 数学\r\n\r\n来自 [`forge-std/std-math`](https://learnblockchain.cn/docs/foundry/i18n/zh/reference/forge-std/std-math.html)。\r\n\r\n```\r\n// 返回绝对值\r\nuint256 v = abs(int256 a)\r\n\r\n// 返回绝对差值\r\nuint256 v = delta(uint256 a, uint256 b)\r\nuint256 v = delta(int256 a, int256 b)\r\n\r\n//  返回百分比差值， `1e18` 是 100%.\r\nuint256 v = percentDelta(uint256 a, uint256 b)\r\nuint256 v = percentDelta(int256 a, int256 b)\r\n```\r\n\r\n### 模糊测试\r\n\r\n使用`vm.assume()`指定输入条件。应仅用于狭窄检查：\r\n\r\n```\r\nfunction testSomething(uint256 v) public {\r\n    vm.assume(v != 0);\r\n    require(v != 0);\r\n    ... \r\n}\r\n```\r\n\r\n使用`bound()`限制输入到特定范围：\r\n\r\n```\r\nfunction testSomething(uint256 v) public {\r\n    v = bound(v, 100, 500);\r\n    require(v >= 100 && v <= 500);\r\n    ... \r\n}\r\n```\r\n\r\n---\r\n\r\n\r\n\r\n> 本翻译由 [DeCert.me](https://decert.me/) 协助支持， 在 DeCert 构建可信履历，为自己码一个未来。"},"author":{"user":"https://learnblockchain.cn/people/412","address":"0x9e64a306aB319811C5a1270F2CA9f6E1e4857c84"},"history":"bafkreifp76x3p4dgxggdfaasaxuppm65dzmmv2jhxvfq23znq7x3aly7tm","timestamp":1706620421,"version":1}