本文介绍了如何使用 Foundry 库来部署和管理可升级合约,包括安装 OpenZeppelin Contracts v5 或 v4 的步骤、使用 NPM 或 Soldeer 进行替代安装的方法、Foundry 的要求、运行前的配置(如安装 Node.js、配置 foundry.toml)以及在 Windows 环境下的设置等。
用于部署和管理可升级合约的 Foundry 库,其中包括升级安全验证。
根据你使用的 OpenZeppelin Contracts 版本,按照以下部分之一进行操作。 新的部署需要 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.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 创建的现有部署。 |
按照上面的步骤操作,但不要运行 forge install OpenZeppelin/openzeppelin-foundry-upgrades
,而是使用以下命令:
npm install @openzeppelin/foundry-upgrades
然后,除了上面描述的之外,在 remappings.txt
中添加以下附加行:
openzeppelin-foundry-upgrades/=node_modules/@openzeppelin/foundry-upgrades/src/
按照上面的步骤操作,但不要运行 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/
此库需要 forge-std 1.9.5 或更高版本。
此库使用 OpenZeppelin Upgrades CLI 进行升级安全验证,默认情况下在部署和升级期间运行。
如果你想要能够运行升级安全验证,则需要以下内容:
安装 Node.js。
配置你的 foundry.toml
以启用 ffi、ast、build info 和 storage layout:
[profile.default]
ffi = true
ast = true
build_info = true
extra_output = ["storageLayout"]
如果你要从以前的版本升级你的合约,请根据 定义参考合约 将 @custom:oz-upgrades-from <reference>
注释添加到新版本的合约中,或者在调用库的函数时指定 referenceContract
选项。
在运行 Foundry 脚本或测试之抢跑 forge clean
,或者在运行 forge script
或 forge 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,请将 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 文档,请参阅 Foundry Upgrades API。
- 原文链接: docs.openzeppelin.com/up...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!