Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UnsafeUpgrades library variations, add LegacyUpgrades.sol #50

Merged
merged 44 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
009fc9d
Handle UPGRADES_INTERFACE_VERSION
ericglau May 2, 2024
2a72a66
Refactor common functions into internal libs
ericglau May 3, 2024
dd58f84
Test with actual contracts using 4.x and 5.x
ericglau May 6, 2024
13d0559
Simplify interface NatSpec
ericglau May 6, 2024
b8bfcee
Support solc 0.8.0
ericglau May 6, 2024
6ce910d
Move v4 tests to allow auto remappings
ericglau May 7, 2024
bc66b5d
Add direct deployment functions
ericglau May 7, 2024
db1a1a4
Rename and refactor
ericglau May 7, 2024
350bd62
Update comments
ericglau May 7, 2024
229bb91
Refactor and rearrange
ericglau May 7, 2024
913d74c
Add missing fn
ericglau May 7, 2024
73e8a60
Add unsafe legacy upgrades
ericglau May 7, 2024
4c96c26
Reorganize
ericglau May 7, 2024
4bc9850
Move folders
ericglau May 7, 2024
c111765
Update package scripts
ericglau May 7, 2024
4e2b445
Add UnsafeUpgrades test
ericglau May 7, 2024
63b5f7b
Add test
ericglau May 7, 2024
c35f790
Cleanup
ericglau May 7, 2024
e6920b8
Lint
ericglau May 7, 2024
1405ef4
Rename to test-profiles
ericglau May 7, 2024
ddc4da4
Revert proxy deployment to use artifacts
ericglau May 7, 2024
302ed91
Cleanup comments and imports
ericglau May 7, 2024
f60480c
Regenerate docs
ericglau May 7, 2024
5056018
Add filename as part of doc links
ericglau May 8, 2024
dd64aad
Use filename in anchor
ericglau May 8, 2024
5f5c941
Generates separate doc pages for each solidity file
ericglau May 8, 2024
9f95fcb
Fix lint
ericglau May 8, 2024
09812ab
Update notes and comments
ericglau May 8, 2024
f6833e5
Add notes
ericglau May 8, 2024
ccea62e
Add UnsafeUpgrades doc
ericglau May 8, 2024
2a83207
Add test profile
ericglau May 8, 2024
488228b
Add v4 to docs
ericglau May 8, 2024
239d32b
Refactor test profiles
ericglau May 8, 2024
4ed614b
Revert "Refactor test profiles"
ericglau May 8, 2024
ca5e4d6
Cleanup imports
ericglau May 8, 2024
9d48d21
Update docs
ericglau May 8, 2024
19bd903
Update docs, add table
ericglau May 8, 2024
75052ad
Update changelog
ericglau May 8, 2024
7d37c6f
Update comments
ericglau May 8, 2024
db9e280
Remove deploy impl from legacy, cleanup cheatcodes
ericglau May 8, 2024
02c2f66
Cleanup use of vm
ericglau May 8, 2024
fd9e087
Cleanup unused constants
ericglau May 8, 2024
1ff1337
Revert changes from Solditity limitations for test
ericglau May 8, 2024
8a21314
Update version in changelog
ericglau May 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.3.0 (2024-05-14)

- Adds library variations to support `forge coverage` or upgrade existing deployments using OpenZeppelin Contracts v4. ([#50](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/pull/50))

### Breaking changes
- Removed the `CHEATCODE_ADDRESS` internal constant from `Upgrades.sol`.

## 0.2.3 (2024-05-02)

- Defender: Add `txOverrides` option. ([#49](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/pull/49))
Expand Down
73 changes: 59 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

[![Docs](https://img.shields.io/badge/docs-%F0%9F%93%84-blue)](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
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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";
```
Expand All @@ -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(
Expand Down Expand Up @@ -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.
Expand Down
12 changes: 1 addition & 11 deletions docs/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,5 @@ module.exports = {
templates: 'docs/templates',
exclude: ['internal'],
pageExtension: '.adoc',
pages: (_, file, config) => {
// For each contract file, find the closest README.adoc and return its location as the output page path.
const sourcesDir = path.resolve(config.root, config.sourcesDir);
let dir = path.resolve(config.root, file.absolutePath);
while (dir.startsWith(sourcesDir)) {
dir = path.dirname(dir);
if (fs.existsSync(path.join(dir, 'README.adoc'))) {
return path.relative(sourcesDir, dir) + config.pageExtension;
}
}
},
pages: 'files',
};
171 changes: 171 additions & 0 deletions docs/modules/api/pages/Defender.adoc
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.

Loading