OpenZeppelin Upgrades Core & CLI
@openzeppelin/upgrades-core
包提供了一个 validate
命令,用于检查可升级合约的升级安全性和存储布局兼容性。它可以在你的整个开发过程中使用,以确保你的合约是升级安全的并且与以前的版本兼容。
它还提供了以编程方式执行这些检查的 API,并包含用于使用 OpenZeppelin Upgrades 插件执行这些检查的核心逻辑。
CLI: Validate 命令
从包含构建信息文件的目录中检测可升级合约,并验证它们是否升级安全。 如果你想从命令行、脚本或 CI/CD 管道中验证你项目的所有可升级合约,请使用此选项。
提示: "构建信息文件" 由你的编译工具链(Hardhat,Foundry)生成,并包含编译过程的输入和输出。
先决条件
在使用 validate
命令之前,你必须定义可升级合约以便可以检测和验证它们,定义用于存储布局比较的参考合约,并编译你的合约。
定义可升级合约
validate
命令对看起来像可升级合约的合约执行升级安全检查。 具体来说,它对满足以下任何条件的实现合约执行检查:
-
继承
Initializable
。 -
具有
upgradeTo(address)
或upgradeToAndCall(address,bytes)
函数。 对于继承UUPSUpgradeable
的合约就是这种情况。 -
具有 NatSpec 注释
@custom:oz-upgrades
-
具有根据 定义参考合约 中的 NatSpec 注释
@custom:oz-upgrades-from <reference>
。
提示: 只需将 NatSpec 注释 @custom:oz-upgrades
或 @custom:oz-upgrades-from <reference>
添加到每个实现合约,以便它可以被检测为可升级合约以进行验证。
定义参考合约
重要提示: 如果实现合约旨在作为现有代理的升级进行部署,你 必须 定义一个参考合约以进行存储布局比较。 否则,如果存在任何存储布局不兼容的情况,你将不会收到错误。
通过将 NatSpec 注释 @custom:oz-upgrades-from <reference>
添加到你的实现合约来定义参考合约,其中 <reference>
是用于存储布局比较的参考合约的合约名称或完全限定的合约名称。 该合约不需要在作用域内,并且如果合约名称在整个项目中不明确,则合约名称就足够了。
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:oz-upgrades-from MyContractV1
contract MyContractV2 {
...
}
或者:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:oz-upgrades-from contracts/MyContract.sol:MyContractV1
contract MyContractV2 {
...
}
编译具有存储布局的合约
编译你的合约并确保你的构建已配置为在构建信息目录中输出具有 Solidity 编译器输入和输出的 JSON 文件。 编译器输出必须包括存储布局。 如果存在任何先前的构建工件,则必须先清除它们以避免重复的合约定义。
用法
在执行先决条件后,运行 npx @openzeppelin/upgrades-core validate
命令以验证你的合约:
npx @openzeppelin/upgrades-core validate [<BUILD_INFO_DIR>] [<OPTIONS>]
如果发现任何错误,该命令将以非零退出代码退出,并将错误的详细报告打印到控制台。
参数:
-
<BUILD_INFO_DIR>
- 可选的构建信息目录的路径,其中包含带有 Solidity 编译器输入和输出的 JSON 文件。 对于 Hardhat 项目,默认为artifacts/build-info
,对于 Foundry 项目,默认为out/build-info
。 如果你的项目使用自定义输出目录,则必须在此处指定其构建信息目录。
选项:
-
--contract <CONTRACT>
- 要验证的合约的名称或完全限定的名称。 如果未指定,将验证构建信息目录中的所有可升级合约。 -
--reference <REFERENCE_CONTRACT>
- 只能在同时提供--contract
选项时使用。 用于存储布局比较的参考合约的名称或完全限定的名称。 如果未指定,则如果正在验证的合约中定义了@custom:oz-upgrades-from
注释,则使用该注释。 -
--requireReference
- 只能在同时提供--contract
选项时使用。 与--unsafeSkipStorageCheck
不兼容。 如果指定,则要求提供--reference
选项或合约具有@custom:oz-upgrades-from
注释。 -
--referenceBuildInfoDirs "<BUILD_INFO_DIR>[,<BUILD_INFO_DIR>…]"
- 用于存储布局比较的项目的早期版本的可选构建信息目录路径。 使用此选项时,在使用--reference
选项或@custom:oz-upgrades-from
注释中的合约名称或完全限定名称之前,使用前缀<dirName>:
引用这些目录之一,其中<dirName>
是目录短名称。 每个目录短名称必须是唯一的,包括与主构建信息目录相比。 如果传入多个目录,请用逗号分隔它们或多次调用该选项,每个目录一次。 -
--exclude "<GLOB_PATTERN>" [--exclude "<GLOB_PATTERN>"…]
- 排除对源文件路径中与任何给定 glob 模式匹配的合约的验证。 例如,--exclude "contracts/mocks/**/*.sol"
。 不适用于参考合约。 如果传入多个模式,请多次调用该选项,每个模式一次。 -
--unsafeAllow "<VALIDATION_ERROR>[,<VALIDATION_ERROR>…]"
- 选择性地禁用一个或多个验证错误或警告。 以逗号分隔的列表,包含以下一个或多个:-
错误:
state-variable-assignment, state-variable-immutable, external-library-linking, struct-definition, enum-definition, constructor, delegatecall, selfdestruct, missing-public-upgradeto, internal-function-storage, missing-initializer, missing-initializer-call, duplicate-initializer-call
-
警告:
incorrect-initializer-order
-
-
--unsafeAllowRenames
- 配置存储布局检查以允许变量重命名。 -
--unsafeSkipStorageCheck
- 跳过检查存储布局兼容性错误。 这是一个危险的选项,旨在作为最后的手段使用。
高级 API
高级 API 在编程上等效于 validate 命令。 如果你想从 JavaScript 或 TypeScript 环境中验证你项目的所有可升级合约,请使用此 API。
先决条件
与 validate 命令 相同的先决条件。
用法
导入 validateUpgradeSafety
函数:
import { validateUpgradeSafety } from '@openzeppelin/upgrades-core';
然后调用该函数以验证你的合约并获取包含验证结果的项目报告。
validateUpgradeSafety
validateUpgradeSafety(
buildInfoDir?: string,
contract?: string,
reference?: string,
opts: ValidateUpgradeSafetyOptions = {},
referenceBuildInfoDirs?: string[],
exclude?: string[],
): Promise<ProjectReport>
从构建信息目录中检测可升级合约,并验证它们是否升级安全。 返回一个带有结果的 项目报告。
请注意,此函数不会直接抛出验证错误。 相反,你必须使用项目报告来确定是否找到任何错误。
参数:
-
buildInfoDir
- 构建信息目录的路径,其中包含带有 Solidity 编译器输入和输出的 JSON 文件。 对于 Hardhat 项目,默认为artifacts/build-info
,对于 Foundry 项目,默认为out/build-info
。 如果你的项目使用自定义输出目录,则必须在此处指定其构建信息目录。 -
contract
- 要验证的合约的名称或完全限定的名称。 如果未指定,将验证构建信息目录中的所有可升级合约。 -
reference
- 只能在同时提供contract
参数时使用。 用于存储布局比较的参考合约的名称或完全限定的名称。 如果未指定,则如果正在验证的合约中定义了@custom:oz-upgrades-from
注释,则使用该注释。 -
opts
- 一个对象,其中包含 通用选项 中定义的以下选项:-
unsafeAllow
-
unsafeAllowRenames
-
unsafeSkipStorageCheck
-
requireReference
- 只能在同时提供contract
参数时使用。 与unsafeSkipStorageCheck
选项不兼容。 如果指定,则要求提供reference
参数或合约具有@custom:oz-upgrades-from
注释。
-
-
referenceBuildInfoDirs
- 用于存储布局比较的项目的早期版本的可选构建信息目录路径。 使用此选项时,在使用reference
参数或@custom:oz-upgrades-from
注释中的合约名称或完全限定名称之前,使用前缀<dirName>:
引用这些目录之一,其中<dirName>
是目录短名称。 每个目录短名称必须是唯一的,包括与主构建信息目录相比。 -
exclude
- 排除对源文件路径中与任何给定 glob 模式匹配的合约的验证。
返回值:
-
一个 项目报告。
低级 API
注意: 此低级 API 已弃用。 请改用 高级 API。
低级 API 使用 Solidity 输入和输出 JSON 对象,并允许你对单个合约执行升级安全检查和存储布局比较。 如果你想验证特定的合约而不是整个项目,请使用此 API。
先决条件
编译你的合约以生成 Solidity 输入和输出 JSON 对象。 编译器输出必须包括存储布局。
请注意,不需要 validate 命令 中的其他先决条件,因为低级 API 不会自动检测可升级合约。 相反,你必须为你想要验证的每个实现合约创建一个 UpgradeableContract
实例,并调用它上面的函数以获取升级安全性和存储布局报告。
用法
导入 UpgradeableContract
类:
import { UpgradeableContract } from '@openzeppelin/upgrades-core';
然后,为你想要验证的每个实现合约创建一个 UpgradeableContract
实例,并调用它上面的 .getErrorReport()
和/或 .getStorageLayoutReport()
以分别获取升级安全性和存储布局报告。
UpgradeableContract
此类表示可升级合约的实现,并提供对错误报告的访问。
constructor UpgradeableContract
constructor UpgradeableContract(
name: string,
solcInput: SolcInput,
solcOutput: SolcOutput,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
},
solcVersion?: string,
): UpgradeableContract
创建 UpgradeableContract
的新实例。
参数:
-
name
- 实现合约的名称,可以是完全限定的名称或合约名称。 如果多个合约具有相同的名称,则必须使用完全限定的名称,例如contracts/Bar.sol:Bar
。 -
solcInput
- 实现合约的 Solidity 输入 JSON 对象。 -
solcOutput
- 实现合约的 Solidity 输出 JSON 对象。 -
opts
- 一个对象,其中包含 通用选项 中定义的以下选项:-
kind
-
unsafeAllow
-
unsafeAllowRenames
-
unsafeSkipStorageCheck
-
-
solcVersion
- 用于编译实现合约的 Solidity 版本。
提示: 在 Hardhat 中,solcInput
和 solcOutput
可以从构建信息文件中获取,构建信息文件本身可以使用 hre.artifacts.getBuildInfo
检索。
.getStorageUpgradeReport
getStorageUpgradeReport(
upgradedContract: UpgradeableContract,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
},
): Report
将可升级合约的存储布局与提议的升级的存储布局进行比较。
参数:
-
upgradedContract
-UpgradeableContract
的另一个实例,表示提议的升级。 -
opts
- 一个对象,其中包含 通用选项 中定义的以下选项:-
kind
-
unsafeAllow
-
unsafeAllowRenames
-
unsafeSkipStorageCheck
-
返回值:
-
关于与代理合约相关的错误的报告,例如使用
selfdestruct
和存储布局冲突。