OpenZeppelin Upgrades Core & CLI
The @openzeppelin/upgrades-core
package provides a validate
command to check for upgrade safety and storage layout compatibility in upgradeable contracts. It can be used throughout your development process to ensure that your contracts are upgrade safe and compatible with previous versions.
It also provides APIs to perform these checks programmatically, and contains the core logic for these checks to be performed with the OpenZeppelin Upgrades plugins.
CLI: Validate Command
Detects upgradeable contracts from a directory containing build info files and validates whether they are upgrade safe. Use this if you want to validate all of your project’s upgradeable contracts from the command line, in a script, or as part of your CI/CD pipeline.
"Build info files" are generated by your compilation toolchain (Hardhat, Foundry) and contain the inputs and outputs of the compilation process. |
Prerequisites
Before using the validate
command, you must define upgradeable contracts so that they can be detected and validated, define reference contracts for storage layout comparisons, and compile your contracts.
Define Upgradeable Contracts
The validate
command performs upgrade safety checks on contracts that look like upgradeable contracts. Specifically, it performs checks on implementation contracts that meet any of the following criteria:
-
Inherits
Initializable
. -
Has an
upgradeTo(address)
orupgradeToAndCall(address,bytes)
function. This is the case for contracts that inheritUUPSUpgradeable
. -
Has the NatSpec annotation
@custom:oz-upgrades
-
Has the NatSpec annotation
@custom:oz-upgrades-from <reference>
according to Define Reference Contracts below.
Simply add the NatSpec annotation @custom:oz-upgrades or @custom:oz-upgrades-from <reference> to each implementation contract so that it can be detected as an upgradeable contract for validation.
|
Define Reference Contracts
If an implementation contract is meant to deployed as an upgrade to an existing proxy, you MUST define a reference contract for storage layout comparisons. Otherwise, you will not receive errors if there are any storage layout incompatibilities. |
Define a reference contract by adding the NatSpec annotation @custom:oz-upgrades-from <reference>
to your implementation contract, where <reference>
is the contract name or fully qualified contract name of the reference contract to use for storage layout comparisons. The contract does not need to be in scope, and the contract name will suffice if it is unambiguous across the project.
Example:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:oz-upgrades-from MyContractV1
contract MyContractV2 {
...
}
Or:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:oz-upgrades-from contracts/MyContract.sol:MyContractV1
contract MyContractV2 {
...
}
Compile Contracts with Storage Layouts
Compile your contracts and ensure that your build is configured to output JSON files with Solidity compiler inputs and outputs in a build info directory. The compiler output must include storage layouts. If any previous build artifacts exist, they must be cleaned first to avoid duplicate contract definitions.
Usage
After performing the prerequisites, run the npx @openzeppelin/upgrades-core validate
command to validate your contracts:
npx @openzeppelin/upgrades-core validate [<BUILD_INFO_DIR>] [<OPTIONS>]
If any errors are found, the command will exit with a non-zero exit code and print a detailed report of the errors to the console.
Parameters:
-
<BUILD_INFO_DIR>
- Optional path to the build info directory which contains JSON files with Solidity compiler input and output. Defaults toartifacts/build-info
for Hardhat projects orout/build-info
for Foundry projects. If your project uses a custom output directory, you must specify its build info directory here.
Options:
-
--contract <CONTRACT>
- The name or fully qualified name of the contract to validate. If not specified, all upgradeable contracts in the build info directory will be validated. -
--reference <REFERENCE_CONTRACT>
- Can only be used when the--contract
option is also provided. The name or fully qualified name of the reference contract to use for storage layout comparisons. If not specified, uses the@custom:oz-upgrades-from
annotation if it is defined in the contract that is being validated. -
--requireReference
- Can only be used when the--contract
option is also provided. Not compatible with--unsafeSkipStorageCheck
. If specified, requires either the--reference
option to be provided or the contract to have a@custom:oz-upgrades-from
annotation. -
--referenceBuildInfoDirs "<BUILD_INFO_DIR>[,<BUILD_INFO_DIR>…]"
- Optional paths of additional build info directories from previous versions of the project to use for storage layout comparisons. When using this option, refer to one of these directories using prefix<dirName>:
before the contract name or fully qualified name in the--reference
option or@custom:oz-upgrades-from
annotation, where<dirName>
is the directory short name. Each directory short name must be unique, including compared to the main build info directory. If passing in multiple directories, separate them with commas or call the option multiple times, once for each directory. -
--exclude "<GLOB_PATTERN>" [--exclude "<GLOB_PATTERN>"…]
- Exclude validations for contracts in source file paths that match any of the given glob patterns. For example,--exclude "contracts/mocks/**/*.sol"
. Does not apply to reference contracts. If passing in multiple patterns, call the option multiple times, once for each pattern. -
--unsafeAllow "<VALIDATION_ERROR>[,<VALIDATION_ERROR>…]"
- Selectively disable one or more validation errors. Comma-separated list with one or more of the following:state-variable-assignment, state-variable-immutable, external-library-linking, struct-definition, enum-definition, constructor, delegatecall, selfdestruct, missing-public-upgradeto, internal-function-storage
-
--unsafeAllowRenames
- Configure storage layout check to allow variable renaming. -
--unsafeSkipStorageCheck
- Skips checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort.
High-Level API
The high-level API is a programmatic equivalent to the validate command. Use this API if you want to validate all of your project’s upgradeable contracts from a JavaScript or TypeScript environment.
Prerequisites
Same prerequisites as the validate command.
Usage
Import the validateUpgradeSafety
function:
import { validateUpgradeSafety } from '@openzeppelin/upgrades-core';
Then call the function to validate your contracts and get a project report with the validation results.
validateUpgradeSafety
validateUpgradeSafety(
buildInfoDir?: string,
contract?: string,
reference?: string,
opts: ValidateUpgradeSafetyOptions = {},
referenceBuildInfoDirs?: string[],
exclude?: string[],
): Promise<ProjectReport>
Detects upgradeable contracts from a build info directory and validates whether they are upgrade safe. Returns a project report with the results.
Note that this function does not throw validation errors directly. Instead, you must use the project report to determine whether any errors were found.
Parameters:
-
buildInfoDir
- the path to the build info directory which contains JSON files with Solidity compiler input and output. Defaults toartifacts/build-info
for Hardhat projects orout/build-info
for Foundry projects. If your project uses a custom output directory, you must specify its build info directory here. -
contract
- The name or fully qualified name of the contract to validate. If not specified, all upgradeable contracts in the build info directory will be validated. -
reference
- Can only be used when thecontract
argument is also provided. The name or fully qualified name of the reference contract to use for storage layout comparisons. If not specified, uses the@custom:oz-upgrades-from
annotation if it is defined in the contract that is being validated. -
opts
- an object with the following options as defined in Common Options:-
unsafeAllow
-
unsafeAllowRenames
-
unsafeSkipStorageCheck
-
requireReference
- Can only be used when thecontract
argument is also provided. Not compatible with theunsafeSkipStorageCheck
option. If specified, requires either thereference
argument to be provided or the contract to have a@custom:oz-upgrades-from
annotation.
-
-
referenceBuildInfoDirs
- Optional paths of additional build info directories from previous versions of the project to use for storage layout comparisons. When using this option, refer to one of these directories using prefix<dirName>:
before the contract name or fully qualified name in thereference
param or@custom:oz-upgrades-from
annotation, where<dirName>
is the directory short name. Each directory short name must be unique, including compared to the main build info directory. -
exclude
- Exclude validations for contracts in source file paths that match any of the given glob patterns.
Returns:
ProjectReport
interface ProjectReport {
ok: boolean;
explain(color?: boolean): string;
numPassed: number;
numTotal: number;
}
An object that represents the result of upgrade safety checks and storage layout comparisons, and contains a report of all errors found.
Members:
-
ok
-false
if any errors were found, otherwisetrue
. -
explain()
- returns a message explaining the errors in detail, if any. -
numPassed
- number of contracts that passed upgrade safety checks. -
numTotal
- total number of upgradeable contracts detected.
Low-Level API
This low-level API is deprecated. Use the High-Level API instead. |
The low-level API works with Solidity input and output JSON objects and lets you perform upgrade safety checks and storage layout comparisons on individual contracts. Use this API if you want to validate specific contracts rather than a whole project.
Prerequisites
Compile your contracts to generate Solidity input and output JSON objects. The compiler output must include storage layouts.
Note that the other prerequisites from the validate command are not required, because the low-level API does not detect upgradeable contracts automatically. Instead, you must create an instance of UpgradeableContract
for each implementation contract that you want to validate, and call functions on it to get the upgrade safety and storage layout reports.
Usage
Import the UpgradeableContract
class:
import { UpgradeableContract } from '@openzeppelin/upgrades-core';
Then create an instance of UpgradeableContract
for each implementation contract that you want to validate, and call .getErrorReport()
and/or .getStorageLayoutReport()
on it to get the upgrade safety and storage layout reports, respectively.
UpgradeableContract
This class represents the implementation for an upgradeable contract and gives access to error reports.
constructor UpgradeableContract
constructor UpgradeableContract(
name: string,
solcInput: SolcInput,
solcOutput: SolcOutput,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
},
solcVersion?: string,
): UpgradeableContract
Creates a new instance of UpgradeableContract
.
Parameters:
-
name
- the name of the implementation contract as either a fully qualified name or contract name. If multiple contracts have the same name, you must use the fully qualified name e.g.,contracts/Bar.sol:Bar
. -
solcInput
- the Solidity input JSON object for the implementation contract. -
solcOutput
- the Solidity output JSON object for the implementation contract. -
opts
- an object with the following options as defined in Common Options:-
kind
-
unsafeAllow
-
unsafeAllowRenames
-
unsafeSkipStorageCheck
-
-
solcVersion
- the Solidity version used to compile the implementation contract.
In Hardhat, solcInput and solcOutput can be obtained from the Build Info file, which itself can be retrieved with hre.artifacts.getBuildInfo .
|
.getErrorReport
getErrorReport(): Report
Returns:
-
a report about errors pertaining to proxied contracts, e.g. the use of
selfdestruct
.
.getStorageUpgradeReport
getStorageUpgradeReport(
upgradedContract: UpgradeableContract,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
},
): Report
Compares the storage layout of an upgradeable contract with that of a proposed upgrade.
Parameters:
-
upgradedContract
- another instance ofUpgradeableContract
representing the proposed upgrade. -
opts
- an object with the following options as defined in Common Options:-
kind
-
unsafeAllow
-
unsafeAllowRenames
-
unsafeSkipStorageCheck
-
Returns:
-
a report about errors pertaining to proxied contracts, e.g. the use of
selfdestruct
, and storage layout conflicts.