-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
UnsafeUpgrades
library variations, add LegacyUpgrades.sol (#50)
- Loading branch information
Showing
52 changed files
with
3,351 additions
and
1,132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,14 @@ | |
|
||
[](https://docs.openzeppelin.com/upgrades-plugins/foundry-upgrades) | ||
|
||
Foundry library for deploying and managing upgradeable contracts, which includes upgrade safety checks. | ||
Foundry library for deploying and managing upgradeable contracts, which includes upgrade safety validations. | ||
|
||
## Installing | ||
|
||
Follow one of the sections below depending on which version of OpenZeppelin Contracts you are using. OpenZeppelin Contracts v5 is required for new deployments. | ||
|
||
### Using OpenZeppelin Contracts v5 | ||
|
||
Run these commands: | ||
```console | ||
forge install foundry-rs/forge-std | ||
|
@@ -22,29 +26,38 @@ Set the following in `remappings.txt`, replacing any previous definitions of the | |
> **Note** | ||
> The above remappings mean that both `@openzeppelin/contracts/` (including proxy contracts deployed by this library) and `@openzeppelin/contracts-upgradeable/` come from your installation of the `openzeppelin-contracts-upgradeable` submodule and its subdirectories, which includes its own transitive copy of `openzeppelin-contracts` of the same release version number. This format is needed for Etherscan verification to work. Particularly, any copies of `openzeppelin-contracts` that you install separately are NOT used. | ||
### Windows installations | ||
### Using OpenZeppelin Contracts v4 | ||
|
||
If you are using Windows, set the `OPENZEPPELIN_BASH_PATH` environment variable to the fully qualified path of the `bash` executable. | ||
For example, if you are using [Git for Windows](https://gitforwindows.org/), add the following line in the `.env` file of your project (using forward slashes): | ||
```env | ||
OPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash" | ||
Run these commands, replacing `v4.9.6` with the specific version of OpenZeppelin Contracts that you are using: | ||
```console | ||
forge install foundry-rs/forge-std | ||
forge install OpenZeppelin/openzeppelin-foundry-upgrades | ||
forge install OpenZeppelin/[email protected] | ||
forge install OpenZeppelin/[email protected] | ||
``` | ||
|
||
Set the following in `remappings.txt`: | ||
``` | ||
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ | ||
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ | ||
``` | ||
|
||
> **Note** | ||
> Use [LegacyUpgrades.sol](src/LegacyUpgrades.sol) instead of `Upgrades.sol` to upgrade existing deployments that were created with OpenZeppelin Contracts v4. | ||
## OpenZeppelin Defender integration | ||
|
||
See [DEFENDER.md](DEFENDER.md) | ||
|
||
## Version Limitations | ||
## Foundry Requirements | ||
|
||
This library requires [forge-std](https://github.com/foundry-rs/forge-std) version 1.8.0 or higher. | ||
|
||
This library only supports proxy contracts and upgrade interfaces from OpenZeppelin Contracts versions 5.0 or higher. | ||
|
||
## Before Running | ||
|
||
This library uses the [OpenZeppelin Upgrades CLI](https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core) for upgrade safety checks, which are run by default during deployments and upgrades. | ||
This library uses the [OpenZeppelin Upgrades CLI](https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core) for upgrade safety validations, which are run by default during deployments and upgrades. | ||
|
||
If you want to be able to run upgrade safety checks, the following are needed: | ||
If you want to be able to run upgrade safety validations, the following are needed: | ||
1. Install [Node.js](https://nodejs.org/). | ||
2. Configure your `foundry.toml` to enable ffi, ast, build info and storage layout: | ||
```toml | ||
|
@@ -57,7 +70,7 @@ extra_output = ["storageLayout"] | |
3. If you are upgrading your contract from a previous version, add the `@custom:oz-upgrades-from <reference>` annotation to the new version of your contract according to [Define Reference Contracts](https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core#define-reference-contracts) or specify the `referenceContract` option when calling the library's functions. | ||
4. Run `forge clean` before running your Foundry script or tests, or include the `--force` option when running `forge script` or `forge test`. | ||
|
||
If you do not want to run upgrade safety checks, you can skip the above steps and use the `unsafeSkipAllChecks` option when calling the library's functions. Note that this is a dangerous option meant to be used as a last resort. | ||
If you do not want to run upgrade safety validations, you can skip the above steps and use the [`unsafeSkipAllChecks` option](src/Options.sol) when calling the `Upgrades` library's functions, or use the `UnsafeUpgrades` library instead. Note that these are dangerous options meant to be used as a last resort. | ||
|
||
### Optional: Custom output directory | ||
|
||
|
@@ -74,9 +87,25 @@ Then in a `.env` at your project root, set the `FOUNDRY_OUT` environment variabl | |
FOUNDRY_OUT=my-output-dir | ||
``` | ||
|
||
### Windows environments | ||
|
||
If you are using Windows, set the `OPENZEPPELIN_BASH_PATH` environment variable to the fully qualified path of the `bash` executable. | ||
For example, if you are using [Git for Windows](https://gitforwindows.org/), add the following line in the `.env` file of your project (using forward slashes): | ||
```env | ||
OPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash" | ||
``` | ||
|
||
## Usage | ||
|
||
Import the library in your Foundry scripts or tests: | ||
Depending on which major version of OpenZeppelin Contracts you are using, and whether you want to run upgrade safety validations and/or use OpenZeppelin Defender, use the table below to determine which library to import: | ||
|
||
| | OpenZeppelin Contracts v5 | OpenZeppelin Contracts v4 | | ||
| --- | --- | --- | | ||
| **Runs validations, supports Defender** | `import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";` | `import {Upgrades} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol";` | | ||
| **No validations, does not support Defender** | `import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";` | `import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol";` | | ||
|
||
|
||
Import one of the above libraries in your Foundry scripts or tests, for example: | ||
```solidity | ||
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; | ||
``` | ||
|
@@ -86,10 +115,12 @@ Also import the implementation contract that you want to validate, deploy, or up | |
import {MyToken} from "src/MyToken.sol"; | ||
``` | ||
|
||
Then call functions from [Upgrades.sol](src/Upgrades.sol) to run validations, deployments, or upgrades. | ||
Then call functions from the imported library to run validations, deployments, or upgrades. | ||
|
||
### Examples | ||
|
||
The following examples assume you are using OpenZeppelin Contracts v5 and want to run upgrade safety validations. | ||
|
||
Deploy a UUPS proxy: | ||
```solidity | ||
address proxy = Upgrades.deployUUPSProxy( | ||
|
@@ -158,6 +189,20 @@ Upgrade a beacon: | |
Upgrades.upgradeBeacon(beacon, "MyContractV2.sol"); | ||
``` | ||
|
||
### Coverage Testing | ||
|
||
To enable code coverage reports with `forge coverage`, use the following deployment pattern in your tests: instantiate your implementation contracts directly and use the `UnsafeUpgrades` library. For example: | ||
```solidity | ||
address implementation = address(new MyContract()); | ||
address proxy = Upgrades.deployUUPSProxy( | ||
implementation, | ||
abi.encodeCall(MyContract.initialize, ("arguments for the initialize function")) | ||
); | ||
``` | ||
|
||
> **Warning** | ||
`UnsafeUpgrades` is not recommended for use in Forge scripts. It does not validate whether your contracts are upgrade safe or whether new implementations are compatible with previous ones. Ensure you run validations before any actual deployments or upgrades, such as by using the `Upgrades` library in scripts. | ||
|
||
### Deploying and Verifying | ||
|
||
Run your script with `forge script` to broadcast and deploy. See Foundry's [Solidity Scripting](https://book.getfoundry.sh/tutorials/solidity-scripting) guide. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
:github-icon: pass:[<svg class="icon"><use href="#github-icon"/></svg>] | ||
:xref-Defender-Defender-deployContract-string-: xref:Defender.adoc#Defender-Defender-deployContract-string- | ||
:xref-Defender-Defender-deployContract-string-struct-DefenderOptions-: xref:Defender.adoc#Defender-Defender-deployContract-string-struct-DefenderOptions- | ||
:xref-Defender-Defender-deployContract-string-bytes-: xref:Defender.adoc#Defender-Defender-deployContract-string-bytes- | ||
:xref-Defender-Defender-deployContract-string-bytes-struct-DefenderOptions-: xref:Defender.adoc#Defender-Defender-deployContract-string-bytes-struct-DefenderOptions- | ||
:xref-Defender-Defender-proposeUpgrade-address-string-struct-Options-: xref:Defender.adoc#Defender-Defender-proposeUpgrade-address-string-struct-Options- | ||
:xref-Defender-Defender-getDeployApprovalProcess--: xref:Defender.adoc#Defender-Defender-getDeployApprovalProcess-- | ||
:xref-Defender-Defender-getUpgradeApprovalProcess--: xref:Defender.adoc#Defender-Defender-getUpgradeApprovalProcess-- | ||
:deployContract: pass:normal[xref:#Defender-Defender-deployContract-string-[`++deployContract++`]] | ||
:deployContract: pass:normal[xref:#Defender-Defender-deployContract-string-struct-DefenderOptions-[`++deployContract++`]] | ||
:deployContract: pass:normal[xref:#Defender-Defender-deployContract-string-bytes-[`++deployContract++`]] | ||
:deployContract: pass:normal[xref:#Defender-Defender-deployContract-string-bytes-struct-DefenderOptions-[`++deployContract++`]] | ||
:proposeUpgrade: pass:normal[xref:#Defender-Defender-proposeUpgrade-address-string-struct-Options-[`++proposeUpgrade++`]] | ||
:getDeployApprovalProcess: pass:normal[xref:#Defender-Defender-getDeployApprovalProcess--[`++getDeployApprovalProcess++`]] | ||
:getUpgradeApprovalProcess: pass:normal[xref:#Defender-Defender-getUpgradeApprovalProcess--[`++getUpgradeApprovalProcess++`]] | ||
|
||
[.contract] | ||
[[Defender-Defender]] | ||
=== `++Defender++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Defender.sol[{github-icon},role=heading-link] | ||
|
||
[.hljs-theme-light.nopadding] | ||
```solidity | ||
import { Defender } from "openzeppelin-foundry-upgrades/Defender.sol"; | ||
``` | ||
|
||
Library for interacting with OpenZeppelin Defender from Forge scripts or tests. | ||
|
||
[.contract-index] | ||
.Functions | ||
-- | ||
* {xref-Defender-Defender-deployContract-string-}[`++deployContract(contractName)++`] | ||
* {xref-Defender-Defender-deployContract-string-struct-DefenderOptions-}[`++deployContract(contractName, defenderOpts)++`] | ||
* {xref-Defender-Defender-deployContract-string-bytes-}[`++deployContract(contractName, constructorData)++`] | ||
* {xref-Defender-Defender-deployContract-string-bytes-struct-DefenderOptions-}[`++deployContract(contractName, constructorData, defenderOpts)++`] | ||
* {xref-Defender-Defender-proposeUpgrade-address-string-struct-Options-}[`++proposeUpgrade(proxyAddress, newImplementationContractName, opts)++`] | ||
* {xref-Defender-Defender-getDeployApprovalProcess--}[`++getDeployApprovalProcess()++`] | ||
* {xref-Defender-Defender-getUpgradeApprovalProcess--}[`++getUpgradeApprovalProcess()++`] | ||
|
||
-- | ||
|
||
[.contract-item] | ||
[[Defender-Defender-deployContract-string-]] | ||
==== `[.contract-item-name]#++deployContract++#++(string contractName) → address++` [.item-kind]#internal# | ||
|
||
Deploys a contract to the current network using OpenZeppelin Defender. | ||
|
||
WARNING: Do not use this function directly if you are deploying an upgradeable contract. This function does not validate whether the contract is upgrade safe. | ||
|
||
NOTE: If using an EOA or Safe to deploy, go to https://defender.openzeppelin.com/v2/#/deploy[Defender deploy] to submit the pending deployment while the script is running. | ||
The script waits for the deployment to complete before it continues. | ||
|
||
*Parameters:* | ||
|
||
* `contractName` (`string`) - Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory | ||
*Returns* | ||
|
||
* (`address`) - Address of the deployed contract | ||
[.contract-item] | ||
[[Defender-Defender-deployContract-string-struct-DefenderOptions-]] | ||
==== `[.contract-item-name]#++deployContract++#++(string contractName, struct DefenderOptions defenderOpts) → address++` [.item-kind]#internal# | ||
|
||
Deploys a contract to the current network using OpenZeppelin Defender. | ||
|
||
WARNING: Do not use this function directly if you are deploying an upgradeable contract. This function does not validate whether the contract is upgrade safe. | ||
|
||
NOTE: If using an EOA or Safe to deploy, go to https://defender.openzeppelin.com/v2/#/deploy[Defender deploy] to submit the pending deployment while the script is running. | ||
The script waits for the deployment to complete before it continues. | ||
|
||
*Parameters:* | ||
|
||
* `contractName` (`string`) - Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory | ||
* `defenderOpts` (`struct DefenderOptions`) - Defender deployment options. Note that the `useDefenderDeploy` option is always treated as `true` when called from this function. | ||
*Returns* | ||
|
||
* (`address`) - Address of the deployed contract | ||
[.contract-item] | ||
[[Defender-Defender-deployContract-string-bytes-]] | ||
==== `[.contract-item-name]#++deployContract++#++(string contractName, bytes constructorData) → address++` [.item-kind]#internal# | ||
|
||
Deploys a contract with constructor arguments to the current network using OpenZeppelin Defender. | ||
|
||
WARNING: Do not use this function directly if you are deploying an upgradeable contract. This function does not validate whether the contract is upgrade safe. | ||
|
||
NOTE: If using an EOA or Safe to deploy, go to https://defender.openzeppelin.com/v2/#/deploy[Defender deploy] to submit the pending deployment while the script is running. | ||
The script waits for the deployment to complete before it continues. | ||
|
||
*Parameters:* | ||
|
||
* `contractName` (`string`) - Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory | ||
* `constructorData` (`bytes`) - Encoded constructor arguments | ||
*Returns* | ||
|
||
* (`address`) - Address of the deployed contract | ||
[.contract-item] | ||
[[Defender-Defender-deployContract-string-bytes-struct-DefenderOptions-]] | ||
==== `[.contract-item-name]#++deployContract++#++(string contractName, bytes constructorData, struct DefenderOptions defenderOpts) → address++` [.item-kind]#internal# | ||
|
||
Deploys a contract with constructor arguments to the current network using OpenZeppelin Defender. | ||
|
||
WARNING: Do not use this function directly if you are deploying an upgradeable contract. This function does not validate whether the contract is upgrade safe. | ||
|
||
NOTE: If using an EOA or Safe to deploy, go to https://defender.openzeppelin.com/v2/#/deploy[Defender deploy] to submit the pending deployment while the script is running. | ||
The script waits for the deployment to complete before it continues. | ||
|
||
*Parameters:* | ||
|
||
* `contractName` (`string`) - Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory | ||
* `constructorData` (`bytes`) - Encoded constructor arguments | ||
* `defenderOpts` (`struct DefenderOptions`) - Defender deployment options. Note that the `useDefenderDeploy` option is always treated as `true` when called from this function. | ||
*Returns* | ||
|
||
* (`address`) - Address of the deployed contract | ||
[.contract-item] | ||
[[Defender-Defender-proposeUpgrade-address-string-struct-Options-]] | ||
==== `[.contract-item-name]#++proposeUpgrade++#++(address proxyAddress, string newImplementationContractName, struct Options opts) → struct ProposeUpgradeResponse++` [.item-kind]#internal# | ||
|
||
Proposes an upgrade to an upgradeable proxy using OpenZeppelin Defender. | ||
|
||
This function validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract using Defender, | ||
and proposes an upgrade to the new implementation contract using an upgrade approval process on Defender. | ||
|
||
Supported for UUPS or Transparent proxies. Not currently supported for beacon proxies or beacons. | ||
For beacons, use `Upgrades.prepareUpgrade` along with a transaction proposal on Defender to upgrade the beacon to the deployed implementation. | ||
|
||
Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from <reference>` annotation. | ||
|
||
WARNING: Ensure that the reference contract is the same as the current implementation contract that the proxy is pointing to. | ||
This function does not validate that the reference contract is the current implementation. | ||
|
||
NOTE: If using an EOA or Safe to deploy, go to https://defender.openzeppelin.com/v2/#/deploy[Defender deploy] to submit the pending deployment of the new implementation contract while the script is running. | ||
The script waits for the deployment to complete before it continues. | ||
|
||
*Parameters:* | ||
|
||
* `proxyAddress` (`address`) - The proxy address | ||
* `newImplementationContractName` (`string`) - Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory | ||
* `opts` (`struct Options`) - Common options. Note that the `defender.useDefenderDeploy` option is always treated as `true` when called from this function. | ||
*Returns* | ||
|
||
* (`struct ProposeUpgradeResponse`) - Struct containing the proposal ID and URL for the upgrade proposal | ||
[.contract-item] | ||
[[Defender-Defender-getDeployApprovalProcess--]] | ||
==== `[.contract-item-name]#++getDeployApprovalProcess++#++() → struct ApprovalProcessResponse++` [.item-kind]#internal# | ||
|
||
Gets the default deploy approval process configured for your deployment environment on OpenZeppelin Defender. | ||
|
||
*Returns* | ||
|
||
* (`struct ApprovalProcessResponse`) - Struct with the default deploy approval process ID and the associated address, such as a Relayer, EOA, or multisig wallet address. | ||
[.contract-item] | ||
[[Defender-Defender-getUpgradeApprovalProcess--]] | ||
==== `[.contract-item-name]#++getUpgradeApprovalProcess++#++() → struct ApprovalProcessResponse++` [.item-kind]#internal# | ||
|
||
Gets the default upgrade approval process configured for your deployment environment on OpenZeppelin Defender. | ||
For example, this is useful for determining the default multisig wallet that you can use in your scripts to assign as the owner of your proxy. | ||
|
||
*Returns* | ||
|
||
* (`struct ApprovalProcessResponse`) - Struct with the default upgrade approval process ID and the associated address, such as a multisig or governor contract address. | ||
Oops, something went wrong.