与 Foundry 一起使用 - OpenZeppelin 文档

本文介绍了如何使用 Foundry 库来部署和管理可升级合约,包括安装 OpenZeppelin Contracts v5 或 v4 的步骤、使用 NPM 或 Soldeer 进行替代安装的方法、Foundry 的要求、运行前的配置(如安装 Node.js、配置 foundry.toml)以及在 Windows 环境下的设置等。

与 Foundry 一起使用

用于部署和管理可升级合约的 Foundry 库,其中包括升级安全验证。

安装

根据你使用的 OpenZeppelin Contracts 版本,按照以下部分之一进行操作。 新的部署需要 OpenZeppelin Contracts v5。

使用 OpenZeppelin Contracts v5

运行以下命令:

forge install foundry-rs/forge-std
forge install OpenZeppelin/openzeppelin-foundry-upgrades
forge install OpenZeppelin/openzeppelin-contracts-upgradeable

remappings.txt 中设置以下内容,替换这些重映射的任何先前定义:

@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
上面的重映射意味着 @openzeppelin/contracts/(包括此库部署的代理合约)和 @openzeppelin/contracts-upgradeable/ 都来自你安装的 openzeppelin-contracts-upgradeable 子模块及其子目录,其中包括它自己的相同发布版本号的 openzeppelin-contracts 的传递副本。这种格式是 Etherscan 验证工作所必需的。 特别是,你单独安装的任何 openzeppelin-contracts 副本都会被使用。

使用 OpenZeppelin Contracts v4

运行以下命令,用你正在使用的 OpenZeppelin Contracts 的特定版本替换 v4.9.6

forge install foundry-rs/forge-std
forge install OpenZeppelin/openzeppelin-foundry-upgrades
forge install OpenZeppelin/openzeppelin-contracts@v4.9.6
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v4.9.6

remappings.txt 中设置以下内容:

@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
使用 LegacyUpgrades.sol 而不是 Upgrades.sol 来升级使用 OpenZeppelin Contracts v4 创建的现有部署。

可选:其他安装方法

NPM

按照上面的步骤操作,但不要运行 forge install OpenZeppelin/openzeppelin-foundry-upgrades,而是使用以下命令:

npm install @openzeppelin/foundry-upgrades

然后,除了上面描述的之外,在 remappings.txt 中添加以下附加行:

openzeppelin-foundry-upgrades/=node_modules/@openzeppelin/foundry-upgrades/src/
Soldeer

按照上面的步骤操作,但不要运行 forge install OpenZeppelin/openzeppelin-foundry-upgrades,而是使用 https://soldeer.xyz/project/openzeppelin-foundry-upgrades 中描述的安装命令之一。

然后,除了上面描述的之外,在 remappings.txt 中添加以下附加行(将 0.3.6 替换为你安装的插件版本):

openzeppelin-foundry-upgrades/=dependencies/openzeppelin-foundry-upgrades-0.3.6/src/

Foundry 要求

此库需要 forge-std 1.9.5 或更高版本。

运行前

此库使用 OpenZeppelin Upgrades CLI 进行升级安全验证,默认情况下在部署和升级期间运行。

如果你想要能够运行升级安全验证,则需要以下内容:

  1. 安装 Node.js

  2. 配置你的 foundry.toml 以启用 ffi、ast、build info 和 storage layout:

[profile.default]
ffi = true
ast = true
build_info = true
extra_output = ["storageLayout"]
  1. 如果你要从以前的版本升级你的合约,请根据 定义参考合约@custom:oz-upgrades-from <reference> 注释添加到新版本的合约中,或者在调用库的函数时指定 referenceContract 选项。

  2. 在运行 Foundry 脚本或测试之抢跑 forge clean,或者在运行 forge scriptforge test 时包含 --force 选项。

如果你不想运行升级安全验证,你可以跳过上述步骤,并在调用 Upgrades 库的函数时使用 unsafeSkipAllChecks 选项,或者改用 UnsafeUpgrades 库。 请注意,这些是危险的选项,旨在作为最后的手段使用。

可选:自定义输出目录

默认情况下,此库假定你的 Foundry 输出目录设置为“out”。

如果你想使用自定义输出目录,请在你的 foundry.toml 中设置它,并为该目录提供读取权限。 例如(用你想要使用的目录替换 my-output-dir):

[profile.default]
out = "my-output-dir"
fs_permissions = [{ access = "read", path = "my-output-dir" }]

然后在项目根目录下的 .env 中,将 FOUNDRY_OUT 环境变量设置为与自定义输出目录匹配,例如:

FOUNDRY_OUT=my-output-dir

Windows 环境

如果你使用的是 Windows,请将 OPENZEPPELIN_BASH_PATH 环境变量设置为 bash 可执行文件的完全限定路径。 例如,如果你使用 Git for Windows,请在项目的 .env 文件中添加以下行(使用正斜杠):

OPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash"

用法

根据你使用的 OpenZeppelin Contracts 的主要版本,以及你是否要运行升级安全验证和/或使用 OpenZeppelin Defender,使用下表来确定要导入哪个库:

OpenZeppelin Contracts v5 OpenZeppelin Contracts v4
运行验证,支持 Defender import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol";
无验证,不支持 Defender import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol";

在你的 Foundry 脚本或测试中导入上述库之一,例如:

import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";

还要导入你想要验证、部署或升级到的实现合约,例如:

import {MyToken} from "src/MyToken.sol";

然后从导入的库中调用函数以运行验证、部署或升级。

示例

以下示例假设你使用的是 OpenZeppelin Contracts v5 并且想要运行升级安全验证。

部署代理

部署 UUPS 代理:

address proxy = Upgrades.deployUUPSProxy(
    "MyContract.sol",
    abi.encodeCall(MyContract.initialize, ("initialize 函数的参数"))
);

部署透明代理:

address proxy = Upgrades.deployTransparentProxy(
    "MyContract.sol",
    PROXY_ADMIN 的 INITIAL_OWNER_ADDRESS,
    abi.encodeCall(MyContract.initialize, ("initialize 函数的参数"))
);

部署可升级信标和信标代理:

address beacon = Upgrades.deployBeacon("MyContract.sol", BEACON 的 INITIAL_OWNER_ADDRESS);

address proxy = Upgrades.deployBeaconProxy(
    beacon,
    abi.encodeCall(MyContract.initialize, ("initialize 函数的参数"))
);

使用你的合约

像往常一样调用合约的函数,但请记住始终使用代理地址:

MyContract instance = MyContract(proxy);
instance.myFunction();

升级代理或信标

升级透明或 UUPS 代理并在升级过程中调用任意函数(例如重新初始化程序):

Upgrades.upgradeProxy(
    transparentProxy,
    "MyContractV2.sol",
    abi.encodeCall(MyContractV2.foo, ("foo 的参数"))
);

升级透明或 UUPS 代理而不调用任何其他函数:

Upgrades.upgradeProxy(
    transparentProxy,
    "MyContractV2.sol",
    ""
);

升级信标:

Upgrades.upgradeBeacon(beacon, "MyContractV2.sol");
升级代理或信标时,请确保新合约的 @custom:oz-upgrades-from <reference> 注释设置为代理或信标使用的旧实现合约的名称,或者使用 referenceContract 选项进行设置,例如:
Options memory opts;
opts.referenceContract = "MyContractV1.sol";
Upgrades.upgradeProxy(proxy, "MyContractV2.sol", "", opts);
// or Upgrades.upgradeBeacon(beacon, "MyContractV2.sol", opts);
如果可能,请将旧版本的实现合约的源代码保存在项目中的某个位置,以便用作上述参考。 这要求新版本位于不同的目录、Solidity 文件中,或使用不同的合约名称。 否则,如果你想为新版本使用相同的目录和名称,请保留先前部署的构建信息目录(或从项目存储库的较旧分支构建它)并按如下方式引用它:
Options memory opts;
opts.referenceBuildInfoDir = "/old-builds/build-info-v1";
opts.referenceContract = "build-info-v1:MyContract";
Upgrades.upgradeProxy(proxy, "MyContract.sol", "", opts);
// or Upgrades.upgradeBeacon(beacon, "MyContract.sol", opts);

覆盖率测试

要在 forge coverage 中启用代码覆盖率报告,请在测试中使用以下部署模式:直接实例化你的实现合约并使用 UnsafeUpgrades 库。 例如:

address implementation = address(new MyContract());
address proxy = UnsafeUpgrades.deployUUPSProxy(
    implementation,
    abi.encodeCall(MyContract.initialize, ("initialize 函数的参数"))
);
不建议在 Forge 脚本中使用 UnsafeUpgrades。 它不验证你的合约是否升级安全,也不验证新实现是否与以前的实现兼容。 确保在任何实际部署或升级之抢跑验证,例如通过在脚本中使用 Upgrades 库。

部署和验证

使用 forge script 运行你的脚本以进行广播和部署。 请参阅 Foundry 的 Solidity 脚本 指南。

执行升级时,为 forge script 命令包含 --sender <ADDRESS> 标志,指定拥有代理或代理管理员的地址。 否则,会发生 OwnableUnauthorizedAccount 错误。
如果你想验证源代码(例如在 Etherscan 上),为 forge script 命令包含 --verify 标志。 这将验证你的实现合约以及作为部署一部分的任何代理合约。

API

有关完整的 API 文档,请参阅 Foundry Upgrades API

← 网络文件

OpenZeppelin Defender 集成 →

  • 原文链接: docs.openzeppelin.com/up...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。