diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e252576..ea330b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) diff --git a/README.md b/README.md index ceed3e4e..f25c44be 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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/openzeppelin-contracts@v4.9.6 +forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v4.9.6 ``` +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 ` 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. diff --git a/docs/config.js b/docs/config.js index 4196b857..a4da339e 100644 --- a/docs/config.js +++ b/docs/config.js @@ -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', }; diff --git a/docs/modules/api/pages/Defender.adoc b/docs/modules/api/pages/Defender.adoc new file mode 100644 index 00000000..5d70ba3b --- /dev/null +++ b/docs/modules/api/pages/Defender.adoc @@ -0,0 +1,171 @@ +:github-icon: pass:[] +: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 ` 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. + diff --git a/docs/modules/api/pages/LegacyUpgrades.adoc b/docs/modules/api/pages/LegacyUpgrades.adoc new file mode 100644 index 00000000..a542426b --- /dev/null +++ b/docs/modules/api/pages/LegacyUpgrades.adoc @@ -0,0 +1,423 @@ +:github-icon: pass:[] +:xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options- +:xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes- +:xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address- +:xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-address- +:xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options- +:xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeBeacon-address-string- +:xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address- +:xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-address- +:xref-LegacyUpgrades-Upgrades-validateUpgrade-string-struct-Options-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-validateUpgrade-string-struct-Options- +:xref-LegacyUpgrades-Upgrades-prepareUpgrade-string-struct-Options-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-prepareUpgrade-string-struct-Options- +:xref-LegacyUpgrades-Upgrades-getAdminAddress-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-getAdminAddress-address- +:xref-LegacyUpgrades-Upgrades-getImplementationAddress-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-getImplementationAddress-address- +:xref-LegacyUpgrades-Upgrades-getBeaconAddress-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-Upgrades-getBeaconAddress-address- +:xref-LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes- +:xref-LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address- +:xref-LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address- +:xref-LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-address- +:xref-LegacyUpgrades-UnsafeUpgrades-getAdminAddress-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-getAdminAddress-address- +:xref-LegacyUpgrades-UnsafeUpgrades-getImplementationAddress-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-getImplementationAddress-address- +:xref-LegacyUpgrades-UnsafeUpgrades-getBeaconAddress-address-: xref:LegacyUpgrades.adoc#LegacyUpgrades-UnsafeUpgrades-getBeaconAddress-address- +:upgradeProxy: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-address-[`++upgradeProxy++`]] +:upgradeBeacon: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#LegacyUpgrades-Upgrades-upgradeBeacon-address-string-address-[`++upgradeBeacon++`]] +:validateUpgrade: pass:normal[xref:#LegacyUpgrades-Upgrades-validateUpgrade-string-struct-Options-[`++validateUpgrade++`]] +:prepareUpgrade: pass:normal[xref:#LegacyUpgrades-Upgrades-prepareUpgrade-string-struct-Options-[`++prepareUpgrade++`]] +:getAdminAddress: pass:normal[xref:#LegacyUpgrades-Upgrades-getAdminAddress-address-[`++getAdminAddress++`]] +:getImplementationAddress: pass:normal[xref:#LegacyUpgrades-Upgrades-getImplementationAddress-address-[`++getImplementationAddress++`]] +:getBeaconAddress: pass:normal[xref:#LegacyUpgrades-Upgrades-getBeaconAddress-address-[`++getBeaconAddress++`]] + +[.contract] +[[LegacyUpgrades-Upgrades]] +=== `++Upgrades++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/LegacyUpgrades.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { Upgrades } from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol"; +``` + +Library for managing upgradeable contracts from Forge scripts or tests. + +NOTE: Only for upgrading existing deployments using OpenZeppelin Contracts v4. +For new deployments, use OpenZeppelin Contracts v5 and Upgrades.sol. + +[.contract-index] +.Functions +-- +* {xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-}[`++upgradeProxy(proxy, contractName, data, opts)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-}[`++upgradeProxy(proxy, contractName, data)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-}[`++upgradeProxy(proxy, contractName, data, opts, tryCaller)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-address-}[`++upgradeProxy(proxy, contractName, data, tryCaller)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-}[`++upgradeBeacon(beacon, contractName, opts)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-}[`++upgradeBeacon(beacon, contractName)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-}[`++upgradeBeacon(beacon, contractName, opts, tryCaller)++`] +* {xref-LegacyUpgrades-Upgrades-upgradeBeacon-address-string-address-}[`++upgradeBeacon(beacon, contractName, tryCaller)++`] +* {xref-LegacyUpgrades-Upgrades-validateUpgrade-string-struct-Options-}[`++validateUpgrade(contractName, opts)++`] +* {xref-LegacyUpgrades-Upgrades-prepareUpgrade-string-struct-Options-}[`++prepareUpgrade(contractName, opts)++`] +* {xref-LegacyUpgrades-Upgrades-getAdminAddress-address-}[`++getAdminAddress(proxy)++`] +* {xref-LegacyUpgrades-Upgrades-getImplementationAddress-address-}[`++getImplementationAddress(proxy)++`] +* {xref-LegacyUpgrades-Upgrades-getBeaconAddress-address-}[`++getBeaconAddress(proxy)++`] + +-- + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, struct Options opts)++` [.item-kind]#internal# + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `opts` (`struct Options`) - Common options + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data)++` [.item-kind]#internal# + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, struct Options opts, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `opts` (`struct Options`) - Common options +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeProxy-address-string-bytes-address-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, struct Options opts)++` [.item-kind]#internal# + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeBeacon-address-string-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName)++` [.item-kind]#internal# + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, struct Options opts, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + +[.contract-item] +[[LegacyUpgrades-Upgrades-upgradeBeacon-address-string-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + +[.contract-item] +[[LegacyUpgrades-Upgrades-validateUpgrade-string-struct-Options-]] +==== `[.contract-item-name]#++validateUpgrade++#++(string contractName, struct Options opts)++` [.item-kind]#internal# + +Validates a new implementation contract in comparison with a reference contract, but does not deploy it. + +Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `opts` (`struct Options`) - Common options + +[.contract-item] +[[LegacyUpgrades-Upgrades-prepareUpgrade-string-struct-Options-]] +==== `[.contract-item-name]#++prepareUpgrade++#++(string contractName, struct Options opts) → address++` [.item-kind]#internal# + +Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, +and returns its address. + +Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + +Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. + +*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 +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Address of the new implementation contract + +[.contract-item] +[[LegacyUpgrades-Upgrades-getAdminAddress-address-]] +==== `[.contract-item-name]#++getAdminAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent proxy + +*Returns* + +* (`address`) - Admin address + +[.contract-item] +[[LegacyUpgrades-Upgrades-getImplementationAddress-address-]] +==== `[.contract-item-name]#++getImplementationAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent or UUPS proxy + +*Returns* + +* (`address`) - Implementation address + +[.contract-item] +[[LegacyUpgrades-Upgrades-getBeaconAddress-address-]] +==== `[.contract-item-name]#++getBeaconAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a beacon proxy + +*Returns* + +* (`address`) - Beacon address + +:upgradeProxy: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-[`++upgradeProxy++`]] +:upgradeBeacon: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-[`++upgradeBeacon++`]] +:getAdminAddress: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-getAdminAddress-address-[`++getAdminAddress++`]] +:getImplementationAddress: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-getImplementationAddress-address-[`++getImplementationAddress++`]] +:getBeaconAddress: pass:normal[xref:#LegacyUpgrades-UnsafeUpgrades-getBeaconAddress-address-[`++getBeaconAddress++`]] + +[.contract] +[[LegacyUpgrades-UnsafeUpgrades]] +=== `++UnsafeUpgrades++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/LegacyUpgrades.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { UnsafeUpgrades } from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol"; +``` + +Library for managing upgradeable contracts from Forge tests, without validations. + +Can be used with `forge coverage`. Requires implementation contracts to be instantiated first. +Does not require `--ffi` and does not require a clean compilation before each run. + +Not supported for OpenZeppelin Defender deployments. + +WARNING: Not recommended for use in Forge scripts. +`UnsafeUpgrades` does not validate whether your contracts are upgrade safe or whether new implementations are compatible with previous ones. +Use `Upgrades` if you want validations to be run. + +NOTE: Only for upgrading existing deployments using OpenZeppelin Contracts v4. +For new deployments, use OpenZeppelin Contracts v5 and Upgrades.sol. + +[.contract-index] +.Functions +-- +* {xref-LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-}[`++upgradeProxy(proxy, newImpl, data)++`] +* {xref-LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-}[`++upgradeProxy(proxy, newImpl, data, tryCaller)++`] +* {xref-LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-}[`++upgradeBeacon(beacon, newImpl)++`] +* {xref-LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-}[`++upgradeBeacon(beacon, newImpl, tryCaller)++`] +* {xref-LegacyUpgrades-UnsafeUpgrades-getAdminAddress-address-}[`++getAdminAddress(proxy)++`] +* {xref-LegacyUpgrades-UnsafeUpgrades-getImplementationAddress-address-}[`++getImplementationAddress(proxy)++`] +* {xref-LegacyUpgrades-UnsafeUpgrades-getBeaconAddress-address-}[`++getBeaconAddress(proxy)++`] + +-- + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, address newImpl, bytes data)++` [.item-kind]#internal# + +Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, address newImpl, bytes data, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, address newImpl)++` [.item-kind]#internal# + +Upgrades a beacon to a new implementation contract address. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, address newImpl, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a beacon to a new implementation contract address. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-getAdminAddress-address-]] +==== `[.contract-item-name]#++getAdminAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent proxy + +*Returns* + +* (`address`) - Admin address + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-getImplementationAddress-address-]] +==== `[.contract-item-name]#++getImplementationAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent or UUPS proxy + +*Returns* + +* (`address`) - Implementation address + +[.contract-item] +[[LegacyUpgrades-UnsafeUpgrades-getBeaconAddress-address-]] +==== `[.contract-item-name]#++getBeaconAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a beacon proxy + +*Returns* + +* (`address`) - Beacon address + diff --git a/docs/modules/api/pages/Options.adoc b/docs/modules/api/pages/Options.adoc new file mode 100644 index 00000000..6b24e59c --- /dev/null +++ b/docs/modules/api/pages/Options.adoc @@ -0,0 +1,59 @@ +:github-icon: pass:[] + +[[Options-Options]] +=== `++Options++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { Options } from "openzeppelin-foundry-upgrades/Options.sol"; +``` + +```solidity +struct Options { + string referenceContract; + bytes constructorData; + string unsafeAllow; + bool unsafeAllowRenames; + bool unsafeSkipStorageCheck; + bool unsafeSkipAllChecks; + struct DefenderOptions defender; +} +``` + +[[Options-DefenderOptions]] +=== `++DefenderOptions++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { DefenderOptions } from "openzeppelin-foundry-upgrades/Options.sol"; +``` + +```solidity +struct DefenderOptions { + bool useDefenderDeploy; + bool skipVerifySourceCode; + string relayerId; + bytes32 salt; + string upgradeApprovalProcessId; + string licenseType; + bool skipLicenseType; + struct TxOverrides txOverrides; +} +``` + +[[Options-TxOverrides]] +=== `++TxOverrides++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { TxOverrides } from "openzeppelin-foundry-upgrades/Options.sol"; +``` + +```solidity +struct TxOverrides { + uint256 gasLimit; + uint256 gasPrice; + uint256 maxFeePerGas; + uint256 maxPriorityFeePerGas; +} +``` diff --git a/docs/modules/api/pages/Upgrades.adoc b/docs/modules/api/pages/Upgrades.adoc new file mode 100644 index 00000000..13c1c931 --- /dev/null +++ b/docs/modules/api/pages/Upgrades.adoc @@ -0,0 +1,676 @@ +:github-icon: pass:[] +:xref-Upgrades-Upgrades-deployUUPSProxy-string-bytes-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-deployUUPSProxy-string-bytes-struct-Options- +:xref-Upgrades-Upgrades-deployUUPSProxy-string-bytes-: xref:Upgrades.adoc#Upgrades-Upgrades-deployUUPSProxy-string-bytes- +:xref-Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options- +:xref-Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-: xref:Upgrades.adoc#Upgrades-Upgrades-deployTransparentProxy-string-address-bytes- +:xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options- +:xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeProxy-address-string-bytes- +:xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address- +:xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-address-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeProxy-address-string-bytes-address- +:xref-Upgrades-Upgrades-deployBeacon-string-address-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-deployBeacon-string-address-struct-Options- +:xref-Upgrades-Upgrades-deployBeacon-string-address-: xref:Upgrades.adoc#Upgrades-Upgrades-deployBeacon-string-address- +:xref-Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options- +:xref-Upgrades-Upgrades-upgradeBeacon-address-string-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeBeacon-address-string- +:xref-Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address- +:xref-Upgrades-Upgrades-upgradeBeacon-address-string-address-: xref:Upgrades.adoc#Upgrades-Upgrades-upgradeBeacon-address-string-address- +:xref-Upgrades-Upgrades-deployBeaconProxy-address-bytes-: xref:Upgrades.adoc#Upgrades-Upgrades-deployBeaconProxy-address-bytes- +:xref-Upgrades-Upgrades-deployBeaconProxy-address-bytes-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-deployBeaconProxy-address-bytes-struct-Options- +:xref-Upgrades-Upgrades-validateImplementation-string-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-validateImplementation-string-struct-Options- +:xref-Upgrades-Upgrades-deployImplementation-string-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-deployImplementation-string-struct-Options- +:xref-Upgrades-Upgrades-validateUpgrade-string-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-validateUpgrade-string-struct-Options- +:xref-Upgrades-Upgrades-prepareUpgrade-string-struct-Options-: xref:Upgrades.adoc#Upgrades-Upgrades-prepareUpgrade-string-struct-Options- +:xref-Upgrades-Upgrades-getAdminAddress-address-: xref:Upgrades.adoc#Upgrades-Upgrades-getAdminAddress-address- +:xref-Upgrades-Upgrades-getImplementationAddress-address-: xref:Upgrades.adoc#Upgrades-Upgrades-getImplementationAddress-address- +:xref-Upgrades-Upgrades-getBeaconAddress-address-: xref:Upgrades.adoc#Upgrades-Upgrades-getBeaconAddress-address- +:xref-Upgrades-UnsafeUpgrades-deployUUPSProxy-address-bytes-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-deployUUPSProxy-address-bytes- +:xref-Upgrades-UnsafeUpgrades-deployTransparentProxy-address-address-bytes-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-deployTransparentProxy-address-address-bytes- +:xref-Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes- +:xref-Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address- +:xref-Upgrades-UnsafeUpgrades-deployBeacon-address-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-deployBeacon-address-address- +:xref-Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-upgradeBeacon-address-address- +:xref-Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-address- +:xref-Upgrades-UnsafeUpgrades-deployBeaconProxy-address-bytes-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-deployBeaconProxy-address-bytes- +:xref-Upgrades-UnsafeUpgrades-getAdminAddress-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-getAdminAddress-address- +:xref-Upgrades-UnsafeUpgrades-getImplementationAddress-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-getImplementationAddress-address- +:xref-Upgrades-UnsafeUpgrades-getBeaconAddress-address-: xref:Upgrades.adoc#Upgrades-UnsafeUpgrades-getBeaconAddress-address- +:deployUUPSProxy: pass:normal[xref:#Upgrades-Upgrades-deployUUPSProxy-string-bytes-struct-Options-[`++deployUUPSProxy++`]] +:deployUUPSProxy: pass:normal[xref:#Upgrades-Upgrades-deployUUPSProxy-string-bytes-[`++deployUUPSProxy++`]] +:deployTransparentProxy: pass:normal[xref:#Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-[`++deployTransparentProxy++`]] +:deployTransparentProxy: pass:normal[xref:#Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-[`++deployTransparentProxy++`]] +:upgradeProxy: pass:normal[xref:#Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#Upgrades-Upgrades-upgradeProxy-address-string-bytes-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#Upgrades-Upgrades-upgradeProxy-address-string-bytes-address-[`++upgradeProxy++`]] +:deployBeacon: pass:normal[xref:#Upgrades-Upgrades-deployBeacon-string-address-struct-Options-[`++deployBeacon++`]] +:deployBeacon: pass:normal[xref:#Upgrades-Upgrades-deployBeacon-string-address-[`++deployBeacon++`]] +:upgradeBeacon: pass:normal[xref:#Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#Upgrades-Upgrades-upgradeBeacon-address-string-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#Upgrades-Upgrades-upgradeBeacon-address-string-address-[`++upgradeBeacon++`]] +:deployBeaconProxy: pass:normal[xref:#Upgrades-Upgrades-deployBeaconProxy-address-bytes-[`++deployBeaconProxy++`]] +:deployBeaconProxy: pass:normal[xref:#Upgrades-Upgrades-deployBeaconProxy-address-bytes-struct-Options-[`++deployBeaconProxy++`]] +:validateImplementation: pass:normal[xref:#Upgrades-Upgrades-validateImplementation-string-struct-Options-[`++validateImplementation++`]] +:deployImplementation: pass:normal[xref:#Upgrades-Upgrades-deployImplementation-string-struct-Options-[`++deployImplementation++`]] +:validateUpgrade: pass:normal[xref:#Upgrades-Upgrades-validateUpgrade-string-struct-Options-[`++validateUpgrade++`]] +:prepareUpgrade: pass:normal[xref:#Upgrades-Upgrades-prepareUpgrade-string-struct-Options-[`++prepareUpgrade++`]] +:getAdminAddress: pass:normal[xref:#Upgrades-Upgrades-getAdminAddress-address-[`++getAdminAddress++`]] +:getImplementationAddress: pass:normal[xref:#Upgrades-Upgrades-getImplementationAddress-address-[`++getImplementationAddress++`]] +:getBeaconAddress: pass:normal[xref:#Upgrades-Upgrades-getBeaconAddress-address-[`++getBeaconAddress++`]] + +[.contract] +[[Upgrades-Upgrades]] +=== `++Upgrades++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Upgrades.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; +``` + +Library for deploying and managing upgradeable contracts from Forge scripts or tests. + +NOTE: Requires OpenZeppelin Contracts v5 or higher. + +[.contract-index] +.Functions +-- +* {xref-Upgrades-Upgrades-deployUUPSProxy-string-bytes-struct-Options-}[`++deployUUPSProxy(contractName, initializerData, opts)++`] +* {xref-Upgrades-Upgrades-deployUUPSProxy-string-bytes-}[`++deployUUPSProxy(contractName, initializerData)++`] +* {xref-Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-}[`++deployTransparentProxy(contractName, initialOwner, initializerData, opts)++`] +* {xref-Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-}[`++deployTransparentProxy(contractName, initialOwner, initializerData)++`] +* {xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-}[`++upgradeProxy(proxy, contractName, data, opts)++`] +* {xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-}[`++upgradeProxy(proxy, contractName, data)++`] +* {xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-}[`++upgradeProxy(proxy, contractName, data, opts, tryCaller)++`] +* {xref-Upgrades-Upgrades-upgradeProxy-address-string-bytes-address-}[`++upgradeProxy(proxy, contractName, data, tryCaller)++`] +* {xref-Upgrades-Upgrades-deployBeacon-string-address-struct-Options-}[`++deployBeacon(contractName, initialOwner, opts)++`] +* {xref-Upgrades-Upgrades-deployBeacon-string-address-}[`++deployBeacon(contractName, initialOwner)++`] +* {xref-Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-}[`++upgradeBeacon(beacon, contractName, opts)++`] +* {xref-Upgrades-Upgrades-upgradeBeacon-address-string-}[`++upgradeBeacon(beacon, contractName)++`] +* {xref-Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-}[`++upgradeBeacon(beacon, contractName, opts, tryCaller)++`] +* {xref-Upgrades-Upgrades-upgradeBeacon-address-string-address-}[`++upgradeBeacon(beacon, contractName, tryCaller)++`] +* {xref-Upgrades-Upgrades-deployBeaconProxy-address-bytes-}[`++deployBeaconProxy(beacon, data)++`] +* {xref-Upgrades-Upgrades-deployBeaconProxy-address-bytes-struct-Options-}[`++deployBeaconProxy(beacon, data, opts)++`] +* {xref-Upgrades-Upgrades-validateImplementation-string-struct-Options-}[`++validateImplementation(contractName, opts)++`] +* {xref-Upgrades-Upgrades-deployImplementation-string-struct-Options-}[`++deployImplementation(contractName, opts)++`] +* {xref-Upgrades-Upgrades-validateUpgrade-string-struct-Options-}[`++validateUpgrade(contractName, opts)++`] +* {xref-Upgrades-Upgrades-prepareUpgrade-string-struct-Options-}[`++prepareUpgrade(contractName, opts)++`] +* {xref-Upgrades-Upgrades-getAdminAddress-address-}[`++getAdminAddress(proxy)++`] +* {xref-Upgrades-Upgrades-getImplementationAddress-address-}[`++getImplementationAddress(proxy)++`] +* {xref-Upgrades-Upgrades-getBeaconAddress-address-}[`++getBeaconAddress(proxy)++`] + +-- + +[.contract-item] +[[Upgrades-Upgrades-deployUUPSProxy-string-bytes-struct-Options-]] +==== `[.contract-item-name]#++deployUUPSProxy++#++(string contractName, bytes initializerData, struct Options opts) → address++` [.item-kind]#internal# + +Deploys a UUPS proxy using the given contract as the implementation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-Upgrades-deployUUPSProxy-string-bytes-]] +==== `[.contract-item-name]#++deployUUPSProxy++#++(string contractName, bytes initializerData) → address++` [.item-kind]#internal# + +Deploys a UUPS proxy using the given contract as the implementation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-]] +==== `[.contract-item-name]#++deployTransparentProxy++#++(string contractName, address initialOwner, bytes initializerData, struct Options opts) → address++` [.item-kind]#internal# + +Deploys a transparent proxy using the given contract as the implementation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `initialOwner` (`address`) - Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy +* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-Upgrades-deployTransparentProxy-string-address-bytes-]] +==== `[.contract-item-name]#++deployTransparentProxy++#++(string contractName, address initialOwner, bytes initializerData) → address++` [.item-kind]#internal# + +Deploys a transparent proxy using the given contract as the implementation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `initialOwner` (`address`) - Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy +* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, struct Options opts)++` [.item-kind]#internal# + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `opts` (`struct Options`) - Common options + +[.contract-item] +[[Upgrades-Upgrades-upgradeProxy-address-string-bytes-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data)++` [.item-kind]#internal# + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + +[.contract-item] +[[Upgrades-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, struct Options opts, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `opts` (`struct Options`) - Common options +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + +[.contract-item] +[[Upgrades-Upgrades-upgradeProxy-address-string-bytes-address-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `contractName` (`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 +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + +[.contract-item] +[[Upgrades-Upgrades-deployBeacon-string-address-struct-Options-]] +==== `[.contract-item-name]#++deployBeacon++#++(string contractName, address initialOwner, struct Options opts) → address++` [.item-kind]#internal# + +Deploys an upgradeable beacon using the given contract as the implementation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `initialOwner` (`address`) - Address to set as the owner of the UpgradeableBeacon contract which gets deployed +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Beacon address + +[.contract-item] +[[Upgrades-Upgrades-deployBeacon-string-address-]] +==== `[.contract-item-name]#++deployBeacon++#++(string contractName, address initialOwner) → address++` [.item-kind]#internal# + +Deploys an upgradeable beacon using the given contract as the implementation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `initialOwner` (`address`) - Address to set as the owner of the UpgradeableBeacon contract which gets deployed + +*Returns* + +* (`address`) - Beacon address + +[.contract-item] +[[Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, struct Options opts)++` [.item-kind]#internal# + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 + +[.contract-item] +[[Upgrades-Upgrades-upgradeBeacon-address-string-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName)++` [.item-kind]#internal# + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 + +[.contract-item] +[[Upgrades-Upgrades-upgradeBeacon-address-string-struct-Options-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, struct Options opts, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + +[.contract-item] +[[Upgrades-Upgrades-upgradeBeacon-address-string-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a beacon to a new implementation contract. + +Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `contractName` (`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 +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + +[.contract-item] +[[Upgrades-Upgrades-deployBeaconProxy-address-bytes-]] +==== `[.contract-item-name]#++deployBeaconProxy++#++(address beacon, bytes data) → address++` [.item-kind]#internal# + +Deploys a beacon proxy using the given beacon and call data. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to use +* `data` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-Upgrades-deployBeaconProxy-address-bytes-struct-Options-]] +==== `[.contract-item-name]#++deployBeaconProxy++#++(address beacon, bytes data, struct Options opts) → address++` [.item-kind]#internal# + +Deploys a beacon proxy using the given beacon and call data. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to use +* `data` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-Upgrades-validateImplementation-string-struct-Options-]] +==== `[.contract-item-name]#++validateImplementation++#++(string contractName, struct Options opts)++` [.item-kind]#internal# + +Validates an implementation contract, but does not deploy it. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `opts` (`struct Options`) - Common options + +[.contract-item] +[[Upgrades-Upgrades-deployImplementation-string-struct-Options-]] +==== `[.contract-item-name]#++deployImplementation++#++(string contractName, struct Options opts) → address++` [.item-kind]#internal# + +Validates and deploys an implementation contract, and returns its address. + +*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 +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Address of the implementation contract + +[.contract-item] +[[Upgrades-Upgrades-validateUpgrade-string-struct-Options-]] +==== `[.contract-item-name]#++validateUpgrade++#++(string contractName, struct Options opts)++` [.item-kind]#internal# + +Validates a new implementation contract in comparison with a reference contract, but does not deploy it. + +Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + +*Parameters:* + +* `contractName` (`string`) - Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory +* `opts` (`struct Options`) - Common options + +[.contract-item] +[[Upgrades-Upgrades-prepareUpgrade-string-struct-Options-]] +==== `[.contract-item-name]#++prepareUpgrade++#++(string contractName, struct Options opts) → address++` [.item-kind]#internal# + +Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, +and returns its address. + +Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + +Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. + +*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 +* `opts` (`struct Options`) - Common options + +*Returns* + +* (`address`) - Address of the new implementation contract + +[.contract-item] +[[Upgrades-Upgrades-getAdminAddress-address-]] +==== `[.contract-item-name]#++getAdminAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent proxy + +*Returns* + +* (`address`) - Admin address + +[.contract-item] +[[Upgrades-Upgrades-getImplementationAddress-address-]] +==== `[.contract-item-name]#++getImplementationAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent or UUPS proxy + +*Returns* + +* (`address`) - Implementation address + +[.contract-item] +[[Upgrades-Upgrades-getBeaconAddress-address-]] +==== `[.contract-item-name]#++getBeaconAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a beacon proxy + +*Returns* + +* (`address`) - Beacon address + +:deployUUPSProxy: pass:normal[xref:#Upgrades-UnsafeUpgrades-deployUUPSProxy-address-bytes-[`++deployUUPSProxy++`]] +:deployTransparentProxy: pass:normal[xref:#Upgrades-UnsafeUpgrades-deployTransparentProxy-address-address-bytes-[`++deployTransparentProxy++`]] +:upgradeProxy: pass:normal[xref:#Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-[`++upgradeProxy++`]] +:upgradeProxy: pass:normal[xref:#Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-[`++upgradeProxy++`]] +:deployBeacon: pass:normal[xref:#Upgrades-UnsafeUpgrades-deployBeacon-address-address-[`++deployBeacon++`]] +:upgradeBeacon: pass:normal[xref:#Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-[`++upgradeBeacon++`]] +:upgradeBeacon: pass:normal[xref:#Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-[`++upgradeBeacon++`]] +:deployBeaconProxy: pass:normal[xref:#Upgrades-UnsafeUpgrades-deployBeaconProxy-address-bytes-[`++deployBeaconProxy++`]] +:getAdminAddress: pass:normal[xref:#Upgrades-UnsafeUpgrades-getAdminAddress-address-[`++getAdminAddress++`]] +:getImplementationAddress: pass:normal[xref:#Upgrades-UnsafeUpgrades-getImplementationAddress-address-[`++getImplementationAddress++`]] +:getBeaconAddress: pass:normal[xref:#Upgrades-UnsafeUpgrades-getBeaconAddress-address-[`++getBeaconAddress++`]] + +[.contract] +[[Upgrades-UnsafeUpgrades]] +=== `++UnsafeUpgrades++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Upgrades.sol[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import { UnsafeUpgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; +``` + +Library for deploying and managing upgradeable contracts from Forge tests, without validations. + +Can be used with `forge coverage`. Requires implementation contracts to be instantiated first. +Does not require `--ffi` and does not require a clean compilation before each run. + +Not supported for OpenZeppelin Defender deployments. + +WARNING: Not recommended for use in Forge scripts. +`UnsafeUpgrades` does not validate whether your contracts are upgrade safe or whether new implementations are compatible with previous ones. +Use `Upgrades` if you want validations to be run. + +NOTE: Requires OpenZeppelin Contracts v5 or higher. + +[.contract-index] +.Functions +-- +* {xref-Upgrades-UnsafeUpgrades-deployUUPSProxy-address-bytes-}[`++deployUUPSProxy(impl, initializerData)++`] +* {xref-Upgrades-UnsafeUpgrades-deployTransparentProxy-address-address-bytes-}[`++deployTransparentProxy(impl, initialOwner, initializerData)++`] +* {xref-Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-}[`++upgradeProxy(proxy, newImpl, data)++`] +* {xref-Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-}[`++upgradeProxy(proxy, newImpl, data, tryCaller)++`] +* {xref-Upgrades-UnsafeUpgrades-deployBeacon-address-address-}[`++deployBeacon(impl, initialOwner)++`] +* {xref-Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-}[`++upgradeBeacon(beacon, newImpl)++`] +* {xref-Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-}[`++upgradeBeacon(beacon, newImpl, tryCaller)++`] +* {xref-Upgrades-UnsafeUpgrades-deployBeaconProxy-address-bytes-}[`++deployBeaconProxy(beacon, data)++`] +* {xref-Upgrades-UnsafeUpgrades-getAdminAddress-address-}[`++getAdminAddress(proxy)++`] +* {xref-Upgrades-UnsafeUpgrades-getImplementationAddress-address-}[`++getImplementationAddress(proxy)++`] +* {xref-Upgrades-UnsafeUpgrades-getBeaconAddress-address-}[`++getBeaconAddress(proxy)++`] + +-- + +[.contract-item] +[[Upgrades-UnsafeUpgrades-deployUUPSProxy-address-bytes-]] +==== `[.contract-item-name]#++deployUUPSProxy++#++(address impl, bytes initializerData) → address++` [.item-kind]#internal# + +Deploys a UUPS proxy using the given contract address as the implementation. + +*Parameters:* + +* `impl` (`address`) - Address of the contract to use as the implementation +* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-UnsafeUpgrades-deployTransparentProxy-address-address-bytes-]] +==== `[.contract-item-name]#++deployTransparentProxy++#++(address impl, address initialOwner, bytes initializerData) → address++` [.item-kind]#internal# + +Deploys a transparent proxy using the given contract address as the implementation. + +*Parameters:* + +* `impl` (`address`) - Address of the contract to use as the implementation +* `initialOwner` (`address`) - Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy +* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, address newImpl, bytes data)++` [.item-kind]#internal# + +Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + +[.contract-item] +[[Upgrades-UnsafeUpgrades-upgradeProxy-address-address-bytes-address-]] +==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, address newImpl, bytes data, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `proxy` (`address`) - Address of the proxy to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to +* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + +[.contract-item] +[[Upgrades-UnsafeUpgrades-deployBeacon-address-address-]] +==== `[.contract-item-name]#++deployBeacon++#++(address impl, address initialOwner) → address++` [.item-kind]#internal# + +Deploys an upgradeable beacon using the given contract address as the implementation. + +*Parameters:* + +* `impl` (`address`) - Address of the contract to use as the implementation +* `initialOwner` (`address`) - Address to set as the owner of the UpgradeableBeacon contract which gets deployed + +*Returns* + +* (`address`) - Beacon address + +[.contract-item] +[[Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, address newImpl)++` [.item-kind]#internal# + +Upgrades a beacon to a new implementation contract address. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to + +[.contract-item] +[[Upgrades-UnsafeUpgrades-upgradeBeacon-address-address-address-]] +==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, address newImpl, address tryCaller)++` [.item-kind]#internal# + +NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + +Upgrades a beacon to a new implementation contract address. + +This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. +Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to upgrade +* `newImpl` (`address`) - Address of the new implementation contract to upgrade to +* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + +[.contract-item] +[[Upgrades-UnsafeUpgrades-deployBeaconProxy-address-bytes-]] +==== `[.contract-item-name]#++deployBeaconProxy++#++(address beacon, bytes data) → address++` [.item-kind]#internal# + +Deploys a beacon proxy using the given beacon and call data. + +*Parameters:* + +* `beacon` (`address`) - Address of the beacon to use +* `data` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + +*Returns* + +* (`address`) - Proxy address + +[.contract-item] +[[Upgrades-UnsafeUpgrades-getAdminAddress-address-]] +==== `[.contract-item-name]#++getAdminAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent proxy + +*Returns* + +* (`address`) - Admin address + +[.contract-item] +[[Upgrades-UnsafeUpgrades-getImplementationAddress-address-]] +==== `[.contract-item-name]#++getImplementationAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a transparent or UUPS proxy + +*Returns* + +* (`address`) - Implementation address + +[.contract-item] +[[Upgrades-UnsafeUpgrades-getBeaconAddress-address-]] +==== `[.contract-item-name]#++getBeaconAddress++#++(address proxy) → address++` [.item-kind]#internal# + +Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + +*Parameters:* + +* `proxy` (`address`) - Address of a beacon proxy + +*Returns* + +* (`address`) - Beacon address + diff --git a/docs/modules/api/pages/api-foundry-upgrades.adoc b/docs/modules/api/pages/api-foundry-upgrades.adoc index c008dbaa..d521c11d 100644 --- a/docs/modules/api/pages/api-foundry-upgrades.adoc +++ b/docs/modules/api/pages/api-foundry-upgrades.adoc @@ -1,716 +1,19 @@ -:github-icon: pass:[] -:xref-Upgrades-deployUUPSProxy-string-bytes-struct-Options-: xref:#Upgrades-deployUUPSProxy-string-bytes-struct-Options- -:xref-Upgrades-deployUUPSProxy-string-bytes-: xref:#Upgrades-deployUUPSProxy-string-bytes- -:xref-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-: xref:#Upgrades-deployTransparentProxy-string-address-bytes-struct-Options- -:xref-Upgrades-deployTransparentProxy-string-address-bytes-: xref:#Upgrades-deployTransparentProxy-string-address-bytes- -:xref-Upgrades-upgradeProxy-address-string-bytes-struct-Options-: xref:#Upgrades-upgradeProxy-address-string-bytes-struct-Options- -:xref-Upgrades-upgradeProxy-address-string-bytes-: xref:#Upgrades-upgradeProxy-address-string-bytes- -:xref-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-: xref:#Upgrades-upgradeProxy-address-string-bytes-struct-Options-address- -:xref-Upgrades-upgradeProxy-address-string-bytes-address-: xref:#Upgrades-upgradeProxy-address-string-bytes-address- -:xref-Upgrades-deployBeacon-string-address-struct-Options-: xref:#Upgrades-deployBeacon-string-address-struct-Options- -:xref-Upgrades-deployBeacon-string-address-: xref:#Upgrades-deployBeacon-string-address- -:xref-Upgrades-upgradeBeacon-address-string-struct-Options-: xref:#Upgrades-upgradeBeacon-address-string-struct-Options- -:xref-Upgrades-upgradeBeacon-address-string-: xref:#Upgrades-upgradeBeacon-address-string- -:xref-Upgrades-upgradeBeacon-address-string-struct-Options-address-: xref:#Upgrades-upgradeBeacon-address-string-struct-Options-address- -:xref-Upgrades-upgradeBeacon-address-string-address-: xref:#Upgrades-upgradeBeacon-address-string-address- -:xref-Upgrades-deployBeaconProxy-address-bytes-: xref:#Upgrades-deployBeaconProxy-address-bytes- -:xref-Upgrades-deployBeaconProxy-address-bytes-struct-Options-: xref:#Upgrades-deployBeaconProxy-address-bytes-struct-Options- -:xref-Upgrades-validateImplementation-string-struct-Options-: xref:#Upgrades-validateImplementation-string-struct-Options- -:xref-Upgrades-deployImplementation-string-struct-Options-: xref:#Upgrades-deployImplementation-string-struct-Options- -:xref-Upgrades-validateUpgrade-string-struct-Options-: xref:#Upgrades-validateUpgrade-string-struct-Options- -:xref-Upgrades-prepareUpgrade-string-struct-Options-: xref:#Upgrades-prepareUpgrade-string-struct-Options- -:xref-Upgrades-getAdminAddress-address-: xref:#Upgrades-getAdminAddress-address- -:xref-Upgrades-getImplementationAddress-address-: xref:#Upgrades-getImplementationAddress-address- -:xref-Upgrades-getBeaconAddress-address-: xref:#Upgrades-getBeaconAddress-address- -:xref-Upgrades-tryPrank-address-: xref:#Upgrades-tryPrank-address- -:xref-Upgrades-CHEATCODE_ADDRESS-address: xref:#Upgrades-CHEATCODE_ADDRESS-address -:xref-Defender-deployContract-string-: xref:#Defender-deployContract-string- -:xref-Defender-deployContract-string-struct-DefenderOptions-: xref:#Defender-deployContract-string-struct-DefenderOptions- -:xref-Defender-deployContract-string-bytes-: xref:#Defender-deployContract-string-bytes- -:xref-Defender-deployContract-string-bytes-struct-DefenderOptions-: xref:#Defender-deployContract-string-bytes-struct-DefenderOptions- -:xref-Defender-proposeUpgrade-address-string-struct-Options-: xref:#Defender-proposeUpgrade-address-string-struct-Options- -:xref-Defender-getDeployApprovalProcess--: xref:#Defender-getDeployApprovalProcess-- -:xref-Defender-getUpgradeApprovalProcess--: xref:#Defender-getUpgradeApprovalProcess-- = OpenZeppelin Foundry Upgrades API == Common Options The following options can be used with some of the below functions. See https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[Options.sol] for detailed descriptions of each option. -[[Options]] -=== `++Options++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[{github-icon},role=heading-link] +include::Options.adoc[] -[.hljs-theme-light.nopadding] -```solidity -import { Options } from "openzeppelin-foundry-upgrades/Options.sol"; -``` +== Upgrades.sol -```solidity -struct Options { - string referenceContract; - bytes constructorData; - string unsafeAllow; - bool unsafeAllowRenames; - bool unsafeSkipStorageCheck; - bool unsafeSkipAllChecks; - struct DefenderOptions defender; -} -``` +include::Upgrades.adoc[] -[[DefenderOptions]] -=== `++DefenderOptions++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[{github-icon},role=heading-link] +== LegacyUpgrades.sol -[.hljs-theme-light.nopadding] -```solidity -import { DefenderOptions } from "openzeppelin-foundry-upgrades/Options.sol"; -``` +include::LegacyUpgrades.adoc[] -```solidity -struct DefenderOptions { - bool useDefenderDeploy; - bool skipVerifySourceCode; - string relayerId; - bytes32 salt; - string upgradeApprovalProcessId; - string licenseType; - bool skipLicenseType; - struct TxOverrides txOverrides; -} -``` - -[[TxOverrides]] -=== `++TxOverrides++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[{github-icon},role=heading-link] - -[.hljs-theme-light.nopadding] -```solidity -import { TxOverrides } from "openzeppelin-foundry-upgrades/Options.sol"; -``` - -```solidity -struct TxOverrides { - uint256 gasLimit; - uint256 gasPrice; - uint256 maxFeePerGas; - uint256 maxPriorityFeePerGas; -} -``` - -== Foundry Upgrades - -:deployUUPSProxy: pass:normal[xref:#Upgrades-deployUUPSProxy-string-bytes-struct-Options-[`++deployUUPSProxy++`]] -:deployUUPSProxy: pass:normal[xref:#Upgrades-deployUUPSProxy-string-bytes-[`++deployUUPSProxy++`]] -:deployTransparentProxy: pass:normal[xref:#Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-[`++deployTransparentProxy++`]] -:deployTransparentProxy: pass:normal[xref:#Upgrades-deployTransparentProxy-string-address-bytes-[`++deployTransparentProxy++`]] -:upgradeProxy: pass:normal[xref:#Upgrades-upgradeProxy-address-string-bytes-struct-Options-[`++upgradeProxy++`]] -:upgradeProxy: pass:normal[xref:#Upgrades-upgradeProxy-address-string-bytes-[`++upgradeProxy++`]] -:upgradeProxy: pass:normal[xref:#Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-[`++upgradeProxy++`]] -:upgradeProxy: pass:normal[xref:#Upgrades-upgradeProxy-address-string-bytes-address-[`++upgradeProxy++`]] -:deployBeacon: pass:normal[xref:#Upgrades-deployBeacon-string-address-struct-Options-[`++deployBeacon++`]] -:deployBeacon: pass:normal[xref:#Upgrades-deployBeacon-string-address-[`++deployBeacon++`]] -:upgradeBeacon: pass:normal[xref:#Upgrades-upgradeBeacon-address-string-struct-Options-[`++upgradeBeacon++`]] -:upgradeBeacon: pass:normal[xref:#Upgrades-upgradeBeacon-address-string-[`++upgradeBeacon++`]] -:upgradeBeacon: pass:normal[xref:#Upgrades-upgradeBeacon-address-string-struct-Options-address-[`++upgradeBeacon++`]] -:upgradeBeacon: pass:normal[xref:#Upgrades-upgradeBeacon-address-string-address-[`++upgradeBeacon++`]] -:deployBeaconProxy: pass:normal[xref:#Upgrades-deployBeaconProxy-address-bytes-[`++deployBeaconProxy++`]] -:deployBeaconProxy: pass:normal[xref:#Upgrades-deployBeaconProxy-address-bytes-struct-Options-[`++deployBeaconProxy++`]] -:validateImplementation: pass:normal[xref:#Upgrades-validateImplementation-string-struct-Options-[`++validateImplementation++`]] -:deployImplementation: pass:normal[xref:#Upgrades-deployImplementation-string-struct-Options-[`++deployImplementation++`]] -:validateUpgrade: pass:normal[xref:#Upgrades-validateUpgrade-string-struct-Options-[`++validateUpgrade++`]] -:prepareUpgrade: pass:normal[xref:#Upgrades-prepareUpgrade-string-struct-Options-[`++prepareUpgrade++`]] -:getAdminAddress: pass:normal[xref:#Upgrades-getAdminAddress-address-[`++getAdminAddress++`]] -:getImplementationAddress: pass:normal[xref:#Upgrades-getImplementationAddress-address-[`++getImplementationAddress++`]] -:getBeaconAddress: pass:normal[xref:#Upgrades-getBeaconAddress-address-[`++getBeaconAddress++`]] -:tryPrank: pass:normal[xref:#Upgrades-tryPrank-address-[`++tryPrank++`]] -:CHEATCODE_ADDRESS: pass:normal[xref:#Upgrades-CHEATCODE_ADDRESS-address[`++CHEATCODE_ADDRESS++`]] - -[.contract] -[[Upgrades]] -=== `++Upgrades++` link:https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Upgrades.sol[{github-icon},role=heading-link] - -[.hljs-theme-light.nopadding] -```solidity -import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; -``` - -Library for deploying and managing upgradeable contracts from Forge scripts or tests. - -[.contract-index] -.Functions --- -* {xref-Upgrades-deployUUPSProxy-string-bytes-struct-Options-}[`++deployUUPSProxy(contractName, initializerData, opts)++`] -* {xref-Upgrades-deployUUPSProxy-string-bytes-}[`++deployUUPSProxy(contractName, initializerData)++`] -* {xref-Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-}[`++deployTransparentProxy(contractName, initialOwner, initializerData, opts)++`] -* {xref-Upgrades-deployTransparentProxy-string-address-bytes-}[`++deployTransparentProxy(contractName, initialOwner, initializerData)++`] -* {xref-Upgrades-upgradeProxy-address-string-bytes-struct-Options-}[`++upgradeProxy(proxy, contractName, data, opts)++`] -* {xref-Upgrades-upgradeProxy-address-string-bytes-}[`++upgradeProxy(proxy, contractName, data)++`] -* {xref-Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-}[`++upgradeProxy(proxy, contractName, data, opts, tryCaller)++`] -* {xref-Upgrades-upgradeProxy-address-string-bytes-address-}[`++upgradeProxy(proxy, contractName, data, tryCaller)++`] -* {xref-Upgrades-deployBeacon-string-address-struct-Options-}[`++deployBeacon(contractName, initialOwner, opts)++`] -* {xref-Upgrades-deployBeacon-string-address-}[`++deployBeacon(contractName, initialOwner)++`] -* {xref-Upgrades-upgradeBeacon-address-string-struct-Options-}[`++upgradeBeacon(beacon, contractName, opts)++`] -* {xref-Upgrades-upgradeBeacon-address-string-}[`++upgradeBeacon(beacon, contractName)++`] -* {xref-Upgrades-upgradeBeacon-address-string-struct-Options-address-}[`++upgradeBeacon(beacon, contractName, opts, tryCaller)++`] -* {xref-Upgrades-upgradeBeacon-address-string-address-}[`++upgradeBeacon(beacon, contractName, tryCaller)++`] -* {xref-Upgrades-deployBeaconProxy-address-bytes-}[`++deployBeaconProxy(beacon, data)++`] -* {xref-Upgrades-deployBeaconProxy-address-bytes-struct-Options-}[`++deployBeaconProxy(beacon, data, opts)++`] -* {xref-Upgrades-validateImplementation-string-struct-Options-}[`++validateImplementation(contractName, opts)++`] -* {xref-Upgrades-deployImplementation-string-struct-Options-}[`++deployImplementation(contractName, opts)++`] -* {xref-Upgrades-validateUpgrade-string-struct-Options-}[`++validateUpgrade(contractName, opts)++`] -* {xref-Upgrades-prepareUpgrade-string-struct-Options-}[`++prepareUpgrade(contractName, opts)++`] -* {xref-Upgrades-getAdminAddress-address-}[`++getAdminAddress(proxy)++`] -* {xref-Upgrades-getImplementationAddress-address-}[`++getImplementationAddress(proxy)++`] -* {xref-Upgrades-getBeaconAddress-address-}[`++getBeaconAddress(proxy)++`] - --- - -[.contract-index] -.Modifiers --- -* {xref-Upgrades-tryPrank-address-}[`++tryPrank(deployer)++`] --- - -[.contract-index] -.Internal Variables --- -* {xref-Upgrades-CHEATCODE_ADDRESS-address}[`++address constant CHEATCODE_ADDRESS++`] - --- - -[.contract-item] -[[Upgrades-deployUUPSProxy-string-bytes-struct-Options-]] -==== `[.contract-item-name]#++deployUUPSProxy++#++(string contractName, bytes initializerData, struct Options opts) → address++` [.item-kind]#internal# - -Deploys a UUPS proxy using the given contract as the implementation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required -* `opts` (`struct Options`) - Common options - -*Returns* - -* (`address`) - Proxy address - -[.contract-item] -[[Upgrades-deployUUPSProxy-string-bytes-]] -==== `[.contract-item-name]#++deployUUPSProxy++#++(string contractName, bytes initializerData) → address++` [.item-kind]#internal# - -Deploys a UUPS proxy using the given contract as the implementation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - -*Returns* - -* (`address`) - Proxy address - -[.contract-item] -[[Upgrades-deployTransparentProxy-string-address-bytes-struct-Options-]] -==== `[.contract-item-name]#++deployTransparentProxy++#++(string contractName, address initialOwner, bytes initializerData, struct Options opts) → address++` [.item-kind]#internal# - -Deploys a transparent proxy using the given contract as the implementation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `initialOwner` (`address`) - Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy -* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required -* `opts` (`struct Options`) - Common options - -*Returns* - -* (`address`) - Proxy address - -[.contract-item] -[[Upgrades-deployTransparentProxy-string-address-bytes-]] -==== `[.contract-item-name]#++deployTransparentProxy++#++(string contractName, address initialOwner, bytes initializerData) → address++` [.item-kind]#internal# - -Deploys a transparent proxy using the given contract as the implementation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `initialOwner` (`address`) - Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy -* `initializerData` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - -*Returns* - -* (`address`) - Proxy address - -[.contract-item] -[[Upgrades-upgradeProxy-address-string-bytes-struct-Options-]] -==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, struct Options opts)++` [.item-kind]#internal# - -Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -*Parameters:* - -* `proxy` (`address`) - Address of the proxy to upgrade -* `contractName` (`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 -* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade -* `opts` (`struct Options`) - Common options - -[.contract-item] -[[Upgrades-upgradeProxy-address-string-bytes-]] -==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data)++` [.item-kind]#internal# - -Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -*Parameters:* - -* `proxy` (`address`) - Address of the proxy to upgrade -* `contractName` (`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 -* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - -[.contract-item] -[[Upgrades-upgradeProxy-address-string-bytes-struct-Options-address-]] -==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, struct Options opts, address tryCaller)++` [.item-kind]#internal# - -NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. - -Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. -Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. - -*Parameters:* - -* `proxy` (`address`) - Address of the proxy to upgrade -* `contractName` (`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 -* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade -* `opts` (`struct Options`) - Common options -* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. - -[.contract-item] -[[Upgrades-upgradeProxy-address-string-bytes-address-]] -==== `[.contract-item-name]#++upgradeProxy++#++(address proxy, string contractName, bytes data, address tryCaller)++` [.item-kind]#internal# - -NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. - -Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. -Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. - -*Parameters:* - -* `proxy` (`address`) - Address of the proxy to upgrade -* `contractName` (`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 -* `data` (`bytes`) - Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade -* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. - -[.contract-item] -[[Upgrades-deployBeacon-string-address-struct-Options-]] -==== `[.contract-item-name]#++deployBeacon++#++(string contractName, address initialOwner, struct Options opts) → address++` [.item-kind]#internal# - -Deploys an upgradeable beacon using the given contract as the implementation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `initialOwner` (`address`) - Address to set as the owner of the UpgradeableBeacon contract which gets deployed -* `opts` (`struct Options`) - Common options - -*Returns* - -* (`address`) - Beacon address - -[.contract-item] -[[Upgrades-deployBeacon-string-address-]] -==== `[.contract-item-name]#++deployBeacon++#++(string contractName, address initialOwner) → address++` [.item-kind]#internal# - -Deploys an upgradeable beacon using the given contract as the implementation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `initialOwner` (`address`) - Address to set as the owner of the UpgradeableBeacon contract which gets deployed - -*Returns* - -* (`address`) - Beacon address - -[.contract-item] -[[Upgrades-upgradeBeacon-address-string-struct-Options-]] -==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, struct Options opts)++` [.item-kind]#internal# - -Upgrades a beacon to a new implementation contract. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -*Parameters:* - -* `beacon` (`address`) - Address of the beacon to upgrade -* `contractName` (`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 - -[.contract-item] -[[Upgrades-upgradeBeacon-address-string-]] -==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName)++` [.item-kind]#internal# - -Upgrades a beacon to a new implementation contract. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -*Parameters:* - -* `beacon` (`address`) - Address of the beacon to upgrade -* `contractName` (`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 - -[.contract-item] -[[Upgrades-upgradeBeacon-address-string-struct-Options-address-]] -==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, struct Options opts, address tryCaller)++` [.item-kind]#internal# - -NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. - -Upgrades a beacon to a new implementation contract. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. -Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. - -*Parameters:* - -* `beacon` (`address`) - Address of the beacon to upgrade -* `contractName` (`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 -* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. - -[.contract-item] -[[Upgrades-upgradeBeacon-address-string-address-]] -==== `[.contract-item-name]#++upgradeBeacon++#++(address beacon, string contractName, address tryCaller)++` [.item-kind]#internal# - -NOTE: For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. - -Upgrades a beacon to a new implementation contract. - -Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - -This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. -Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. - -*Parameters:* - -* `beacon` (`address`) - Address of the beacon to upgrade -* `contractName` (`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 -* `tryCaller` (`address`) - Address to use as the caller of the upgrade function. This should be the address that owns the beacon. - -[.contract-item] -[[Upgrades-deployBeaconProxy-address-bytes-]] -==== `[.contract-item-name]#++deployBeaconProxy++#++(address beacon, bytes data) → address++` [.item-kind]#internal# - -Deploys a beacon proxy using the given beacon and call data. - -*Parameters:* - -* `beacon` (`address`) - Address of the beacon to use -* `data` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - -*Returns* - -* (`address`) - Proxy address - -[.contract-item] -[[Upgrades-deployBeaconProxy-address-bytes-struct-Options-]] -==== `[.contract-item-name]#++deployBeaconProxy++#++(address beacon, bytes data, struct Options opts) → address++` [.item-kind]#internal# - -Deploys a beacon proxy using the given beacon and call data. - -*Parameters:* - -* `beacon` (`address`) - Address of the beacon to use -* `data` (`bytes`) - Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required -* `opts` (`struct Options`) - Common options - -*Returns* - -* (`address`) - Proxy address - -[.contract-item] -[[Upgrades-validateImplementation-string-struct-Options-]] -==== `[.contract-item-name]#++validateImplementation++#++(string contractName, struct Options opts)++` [.item-kind]#internal# - -Validates an implementation contract, but does not deploy it. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `opts` (`struct Options`) - Common options - -[.contract-item] -[[Upgrades-deployImplementation-string-struct-Options-]] -==== `[.contract-item-name]#++deployImplementation++#++(string contractName, struct Options opts) → address++` [.item-kind]#internal# - -Validates and deploys an implementation contract, and returns its address. - -*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 -* `opts` (`struct Options`) - Common options - -*Returns* - -* (`address`) - Address of the implementation contract - -[.contract-item] -[[Upgrades-validateUpgrade-string-struct-Options-]] -==== `[.contract-item-name]#++validateUpgrade++#++(string contractName, struct Options opts)++` [.item-kind]#internal# - -Validates a new implementation contract in comparison with a reference contract, but does not deploy it. - -Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. - -*Parameters:* - -* `contractName` (`string`) - Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory -* `opts` (`struct Options`) - Common options - -[.contract-item] -[[Upgrades-prepareUpgrade-string-struct-Options-]] -==== `[.contract-item-name]#++prepareUpgrade++#++(string contractName, struct Options opts) → address++` [.item-kind]#internal# - -Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, -and returns its address. - -Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. - -Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. - -*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 -* `opts` (`struct Options`) - Common options - -*Returns* - -* (`address`) - Address of the new implementation contract - -[.contract-item] -[[Upgrades-getAdminAddress-address-]] -==== `[.contract-item-name]#++getAdminAddress++#++(address proxy) → address++` [.item-kind]#internal# - -Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. - -*Parameters:* - -* `proxy` (`address`) - Address of a transparent proxy - -*Returns* - -* (`address`) - Admin address - -[.contract-item] -[[Upgrades-getImplementationAddress-address-]] -==== `[.contract-item-name]#++getImplementationAddress++#++(address proxy) → address++` [.item-kind]#internal# - -Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. - -*Parameters:* - -* `proxy` (`address`) - Address of a transparent or UUPS proxy - -*Returns* - -* (`address`) - Implementation address - -[.contract-item] -[[Upgrades-getBeaconAddress-address-]] -==== `[.contract-item-name]#++getBeaconAddress++#++(address proxy) → address++` [.item-kind]#internal# - -Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. - -*Parameters:* - -* `proxy` (`address`) - Address of a beacon proxy - -*Returns* - -* (`address`) - Beacon address - -[.contract-item] -[[Upgrades-tryPrank-address-]] -==== `[.contract-item-name]#++tryPrank++#++(address deployer)++` [.item-kind]#modifier# - -Runs a function as a prank, or just runs the function normally if the prank could not be started. - -[.contract-item] -[[Upgrades-CHEATCODE_ADDRESS-address]] -==== `address [.contract-item-name]#++CHEATCODE_ADDRESS++#` [.item-kind]#internal constant# - -== Foundry Defender - -:deployContract: pass:normal[xref:#Defender-deployContract-string-[`++deployContract++`]] -:deployContract: pass:normal[xref:#Defender-deployContract-string-struct-DefenderOptions-[`++deployContract++`]] -:deployContract: pass:normal[xref:#Defender-deployContract-string-bytes-[`++deployContract++`]] -:deployContract: pass:normal[xref:#Defender-deployContract-string-bytes-struct-DefenderOptions-[`++deployContract++`]] -:proposeUpgrade: pass:normal[xref:#Defender-proposeUpgrade-address-string-struct-Options-[`++proposeUpgrade++`]] -:getDeployApprovalProcess: pass:normal[xref:#Defender-getDeployApprovalProcess--[`++getDeployApprovalProcess++`]] -:getUpgradeApprovalProcess: pass:normal[xref:#Defender-getUpgradeApprovalProcess--[`++getUpgradeApprovalProcess++`]] - -[.contract] -[[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-deployContract-string-}[`++deployContract(contractName)++`] -* {xref-Defender-deployContract-string-struct-DefenderOptions-}[`++deployContract(contractName, defenderOpts)++`] -* {xref-Defender-deployContract-string-bytes-}[`++deployContract(contractName, constructorData)++`] -* {xref-Defender-deployContract-string-bytes-struct-DefenderOptions-}[`++deployContract(contractName, constructorData, defenderOpts)++`] -* {xref-Defender-proposeUpgrade-address-string-struct-Options-}[`++proposeUpgrade(proxyAddress, newImplementationContractName, opts)++`] -* {xref-Defender-getDeployApprovalProcess--}[`++getDeployApprovalProcess()++`] -* {xref-Defender-getUpgradeApprovalProcess--}[`++getUpgradeApprovalProcess()++`] - --- - -[.contract-item] -[[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-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-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-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-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 ` 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-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-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. +== Defender.sol +include::Defender.adoc[] diff --git a/docs/modules/pages/foundry-upgrades.adoc b/docs/modules/pages/foundry-upgrades.adoc index 16df89bc..0c3c2ee8 100644 --- a/docs/modules/pages/foundry-upgrades.adoc +++ b/docs/modules/pages/foundry-upgrades.adoc @@ -1,9 +1,13 @@ = Using with Foundry -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. == Installation +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: [source,console] @@ -23,27 +27,37 @@ 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 https://gitforwindows.org/[Git for Windows], add the following line in the `.env` file of your project (using forward slashes): +Run these commands, replacing `v4.9.6` with the specific version of OpenZeppelin Contracts that you are using: + +[source,console] +---- +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 +---- + +Set the following in `remappings.txt`: [source] ---- -OPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash" +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ ---- -== Version Limitations +NOTE: Use `LegacyUpgrades.sol` instead of `Upgrades.sol` to upgrade existing deployments that were created with OpenZeppelin Contracts v4. -This library requires https://github.com/foundry-rs/forge-std[forge-std] version 1.8.0 or higher. +== Foundry Requirements -This library currently only supports proxy contracts and upgrade interfaces from OpenZeppelin Contracts versions 5.0 or higher. +This library requires https://github.com/foundry-rs/forge-std[forge-std] version 1.8.0 or higher. == Before Running -This library uses the https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core[OpenZeppelin Upgrades CLI] for upgrade safety checks, which are run by default during deployments and upgrades. +This library uses the https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core[OpenZeppelin Upgrades CLI] 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 https://nodejs.org/[Node.js]. @@ -63,7 +77,7 @@ extra_output = ["storageLayout"] 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 xref:api-foundry-upgrades.adoc#Options[`unsafeSkipAllChecks` option] 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 @@ -84,9 +98,28 @@ 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 https://gitforwindows.org/[Git for Windows], add the following line in the `.env` file of your project (using forward slashes): + +[source] +---- +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: + +[options="header"] +|=== +| | 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: [source,solidity] ---- import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; @@ -98,10 +131,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` 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: [source,solidity] ---- @@ -178,6 +213,19 @@ 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 https://book.getfoundry.sh/tutorials/solidity-scripting[Solidity Scripting] guide. diff --git a/docs/templates/helpers.js b/docs/templates/helpers.js index 88bfe8af..8d390d62 100644 --- a/docs/templates/helpers.js +++ b/docs/templates/helpers.js @@ -4,7 +4,7 @@ const path = require('path'); module.exports['oz-version'] = () => version; module.exports['readme-path'] = opts => { - return path.join('src/', opts.data.root.id.replace(/\.adoc$/, ''), 'README.adoc'); + return path.join('src/', opts.data.root.id); }; module.exports.names = params => params?.map(p => p.name).join(', '); diff --git a/docs/templates/properties.js b/docs/templates/properties.js index 52eebac5..e6c76501 100644 --- a/docs/templates/properties.js +++ b/docs/templates/properties.js @@ -3,6 +3,10 @@ const { slug } = require('./helpers'); module.exports.anchor = function anchor({ item, contract }) { let res = ''; + + const fileName = item.__item_context.file.relativePath.replace(/\.sol$/, ''); + res += fileName + '-'; + if (contract) { res += contract.name + '-'; } diff --git a/foundry.toml b/foundry.toml index f5066028..1d8bb794 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,3 +4,19 @@ out = "out" libs = ["node_modules", "lib"] build_info = true extra_output = ["storageLayout"] + +[profile.openzeppelin-contracts-v4] +src = "test-profiles/openzeppelin-contracts-v4/src" +test = "test-profiles/openzeppelin-contracts-v4/test" +remappings = [ + "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts-v4", + "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable-v4" +] + +[profile.openzeppelin-contracts-v4-with-v5-proxies] +src = "test-profiles/openzeppelin-contracts-v4-with-v5-proxies/src" +test = "test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test" +remappings = [ + "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts", + "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable-v4" +] \ No newline at end of file diff --git a/package.json b/package.json index 52625d62..ef55d3b0 100644 --- a/package.json +++ b/package.json @@ -5,17 +5,24 @@ "scripts": { "clean": "forge clean && hardhat clean", "compile": "forge build", - "test": "npm run forge:test && npm run forge:script", - "forge:test": "forge test -vvv --ffi --force", - "forge:script": "forge script test/Upgrades.s.sol --ffi --force", + "test": "npm run forge:test && npm run forge:script && npm run forge:test-v4 && npm run forge:script-v4 && npm run forge:test-v4-with-v5-proxies && npm run forge:script-v4-with-v5-proxies", + "forge:test": "FOUNDRY_PROFILE=default forge test -vvv --ffi --force", + "forge:script": "FOUNDRY_PROFILE=default forge script test/Upgrades.s.sol --ffi --force", + "forge:test-v4": "FOUNDRY_PROFILE=openzeppelin-contracts-v4 forge test -vvv --ffi --force --use solc:0.8.2", + "forge:script-v4": "FOUNDRY_PROFILE=openzeppelin-contracts-v4 forge script test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.s.sol --ffi --force --use solc:0.8.2", + "forge:test-v4-with-v5-proxies": "FOUNDRY_PROFILE=openzeppelin-contracts-v4-with-v5-proxies forge test -vvv --ffi --force", + "forge:script-v4-with-v5-proxies": "FOUNDRY_PROFILE=openzeppelin-contracts-v4-with-v5-proxies forge script test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.s.sol --ffi --force", "lint": "prettier --log-level warn --ignore-path .gitignore '{src,test}/**/*.sol' --check && solhint 'src/**/*.sol'", "lint:fix": "prettier --log-level warn --ignore-path .gitignore '{src,test}/**/*.sol' --write", - "docgen": "hardhat clean && hardhat compile && hardhat docgen && mv docs/modules/api/pages/.adoc docs/modules/api/pages/api-foundry-upgrades.adoc", - "docgen:test": "yarn docgen && git diff --exit-code docs/modules/api/pages/api-foundry-upgrades.adoc" + "docgen": "hardhat clean && hardhat compile && hardhat docgen", + "docgen:test": "yarn docgen && git diff --exit-code docs/modules/api/pages" }, "devDependencies": { "@nomicfoundation/hardhat-foundry": "^1.1.1", "@openzeppelin/contracts": "^5.0.2", + "@openzeppelin/contracts-upgradeable": "^5.0.2", + "@openzeppelin/contracts-v4": "npm:@openzeppelin/contracts@^v4.9.6", + "@openzeppelin/contracts-upgradeable-v4": "npm:@openzeppelin/contracts-upgradeable@^v4.9.6", "@openzeppelin/defender-deploy-client-cli": "0.0.1-alpha.7", "@openzeppelin/upgrades-core": "^1.32.3", "hardhat": "^2.21.0", diff --git a/src/Defender.adoc b/src/Defender.adoc new file mode 100644 index 00000000..37dfe58a --- /dev/null +++ b/src/Defender.adoc @@ -0,0 +1 @@ +{{Defender}} \ No newline at end of file diff --git a/src/Defender.sol b/src/Defender.sol index a6683e0d..d1432398 100644 --- a/src/Defender.sol +++ b/src/Defender.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; import {Options, DefenderOptions} from "./Options.sol"; -import {Upgrades} from "./Upgrades.sol"; +import {Core} from "./internal/Core.sol"; import {DefenderDeploy} from "./internal/DefenderDeploy.sol"; /** @@ -109,8 +109,8 @@ library Defender { Options memory opts ) internal returns (ProposeUpgradeResponse memory) { opts.defender.useDefenderDeploy = true; - address proxyAdminAddress = Upgrades.getAdminAddress(proxyAddress); - address newImplementationAddress = Upgrades.prepareUpgrade(newImplementationContractName, opts); + address proxyAdminAddress = Core.getAdminAddress(proxyAddress); + address newImplementationAddress = Core.prepareUpgrade(newImplementationContractName, opts); return DefenderDeploy.proposeUpgrade( proxyAddress, diff --git a/src/LegacyUpgrades.adoc b/src/LegacyUpgrades.adoc new file mode 100644 index 00000000..4e15a299 --- /dev/null +++ b/src/LegacyUpgrades.adoc @@ -0,0 +1,3 @@ +{{Upgrades}} + +{{UnsafeUpgrades}} \ No newline at end of file diff --git a/src/LegacyUpgrades.sol b/src/LegacyUpgrades.sol new file mode 100644 index 00000000..9fabf395 --- /dev/null +++ b/src/LegacyUpgrades.sol @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Options} from "../src/Options.sol"; +import {Core} from "../src/internal/Core.sol"; + +/** + * @dev Library for managing upgradeable contracts from Forge scripts or tests. + * + * NOTE: Only for upgrading existing deployments using OpenZeppelin Contracts v4. + * For new deployments, use OpenZeppelin Contracts v5 and Upgrades.sol. + */ +library Upgrades { + /** + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param proxy Address of the proxy to upgrade + * @param contractName 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 + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param opts Common options + */ + function upgradeProxy(address proxy, string memory contractName, bytes memory data, Options memory opts) internal { + Core.upgradeProxy(proxy, contractName, data, opts); + } + + /** + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param proxy Address of the proxy to upgrade + * @param contractName 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 + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + */ + function upgradeProxy(address proxy, string memory contractName, bytes memory data) internal { + Options memory opts; + Core.upgradeProxy(proxy, contractName, data, opts); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param contractName 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 + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param opts Common options + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxy( + address proxy, + string memory contractName, + bytes memory data, + Options memory opts, + address tryCaller + ) internal { + Core.upgradeProxy(proxy, contractName, data, opts, tryCaller); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param contractName 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 + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxy(address proxy, string memory contractName, bytes memory data, address tryCaller) internal { + Options memory opts; + Core.upgradeProxy(proxy, contractName, data, opts, tryCaller); + } + + /** + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param beacon Address of the beacon to upgrade + * @param contractName 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 + * @param opts Common options + */ + function upgradeBeacon(address beacon, string memory contractName, Options memory opts) internal { + Core.upgradeBeacon(beacon, contractName, opts); + } + + /** + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param beacon Address of the beacon to upgrade + * @param contractName 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 + */ + function upgradeBeacon(address beacon, string memory contractName) internal { + Options memory opts; + Core.upgradeBeacon(beacon, contractName, opts); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param contractName 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 + * @param opts Common options + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeacon( + address beacon, + string memory contractName, + Options memory opts, + address tryCaller + ) internal { + Core.upgradeBeacon(beacon, contractName, opts, tryCaller); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param contractName 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 + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeacon(address beacon, string memory contractName, address tryCaller) internal { + Options memory opts; + Core.upgradeBeacon(beacon, contractName, opts, tryCaller); + } + + /** + * @dev Validates a new implementation contract in comparison with a reference contract, but does not deploy it. + * + * Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param contractName Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param opts Common options + */ + function validateUpgrade(string memory contractName, Options memory opts) internal { + Core.validateUpgrade(contractName, opts); + } + + /** + * @dev Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, + * and returns its address. + * + * Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + * + * Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. + * + * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param opts Common options + * @return Address of the new implementation contract + */ + function prepareUpgrade(string memory contractName, Options memory opts) internal returns (address) { + return Core.prepareUpgrade(contractName, opts); + } + + /** + * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + * + * @param proxy Address of a transparent proxy + * @return Admin address + */ + function getAdminAddress(address proxy) internal view returns (address) { + return Core.getAdminAddress(proxy); + } + + /** + * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + * + * @param proxy Address of a transparent or UUPS proxy + * @return Implementation address + */ + function getImplementationAddress(address proxy) internal view returns (address) { + return Core.getImplementationAddress(proxy); + } + + /** + * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + * + * @param proxy Address of a beacon proxy + * @return Beacon address + */ + function getBeaconAddress(address proxy) internal view returns (address) { + return Core.getBeaconAddress(proxy); + } +} + +/** + * @dev Library for managing upgradeable contracts from Forge tests, without validations. + * + * Can be used with `forge coverage`. Requires implementation contracts to be instantiated first. + * Does not require `--ffi` and does not require a clean compilation before each run. + * + * Not supported for OpenZeppelin Defender deployments. + * + * WARNING: Not recommended for use in Forge scripts. + * `UnsafeUpgrades` does not validate whether your contracts are upgrade safe or whether new implementations are compatible with previous ones. + * Use `Upgrades` if you want validations to be run. + * + * NOTE: Only for upgrading existing deployments using OpenZeppelin Contracts v4. + * For new deployments, use OpenZeppelin Contracts v5 and Upgrades.sol. + */ +library UnsafeUpgrades { + /** + * @dev Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + * + * @param proxy Address of the proxy to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + */ + function upgradeProxy(address proxy, address newImpl, bytes memory data) internal { + Core.upgradeProxyTo(proxy, newImpl, data); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxy(address proxy, address newImpl, bytes memory data, address tryCaller) internal { + Core.upgradeProxyTo(proxy, newImpl, data, tryCaller); + } + + /** + * @dev Upgrades a beacon to a new implementation contract address. + * + * @param beacon Address of the beacon to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + */ + function upgradeBeacon(address beacon, address newImpl) internal { + Core.upgradeBeaconTo(beacon, newImpl); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract address. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeacon(address beacon, address newImpl, address tryCaller) internal { + Core.upgradeBeaconTo(beacon, newImpl, tryCaller); + } + + /** + * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + * + * @param proxy Address of a transparent proxy + * @return Admin address + */ + function getAdminAddress(address proxy) internal view returns (address) { + return Core.getAdminAddress(proxy); + } + + /** + * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + * + * @param proxy Address of a transparent or UUPS proxy + * @return Implementation address + */ + function getImplementationAddress(address proxy) internal view returns (address) { + return Core.getImplementationAddress(proxy); + } + + /** + * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + * + * @param proxy Address of a beacon proxy + * @return Beacon address + */ + function getBeaconAddress(address proxy) internal view returns (address) { + return Core.getBeaconAddress(proxy); + } +} diff --git a/src/Options.adoc b/src/Options.adoc new file mode 100644 index 00000000..840322f0 --- /dev/null +++ b/src/Options.adoc @@ -0,0 +1,5 @@ +{{Options}} + +{{DefenderOptions}} + +{{TxOverrides}} \ No newline at end of file diff --git a/src/Options.sol b/src/Options.sol index 09d5fc1f..1d2bbf85 100644 --- a/src/Options.sol +++ b/src/Options.sol @@ -1,39 +1,39 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; /** * Common options. */ struct Options { - /** + /* * The reference contract to use for storage layout comparisons, e.g. "ContractV1.sol" or "ContractV1.sol:ContractV1". * If not set, attempts to use the `@custom:oz-upgrades-from ` annotation from the contract. */ string referenceContract; - /** + /* * Encoded constructor arguments for the implementation contract. * Note that these are different from initializer arguments, and will be used in the deployment of the implementation contract itself. * Can be used to initialize immutable variables. */ bytes constructorData; - /** + /* * Selectively disable one or more validation errors. Comma-separated list that must be compatible with the * --unsafeAllow option described in https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core#usage */ string unsafeAllow; - /** + /* * Configure storage layout check to allow variable renaming */ bool unsafeAllowRenames; - /** + /* * Skips checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort. */ bool unsafeSkipStorageCheck; - /** + /* * Skips all upgrade safety checks. This is a dangerous option meant to be used as a last resort. */ bool unsafeSkipAllChecks; - /** + /* * Options for OpenZeppelin Defender deployments. */ DefenderOptions defender; @@ -43,22 +43,22 @@ struct Options { * Options for OpenZeppelin Defender deployments. */ struct DefenderOptions { - /** + /* * Deploys contracts using OpenZeppelin Defender instead of broadcasting deployments through Forge. Defaults to `false`. See DEFENDER.md. * * NOTE: If using an EOA or Safe to deploy, go to https://defender.openzeppelin.com/v2/#/deploy[Defender deploy] to submit the pending deployment(s) while the script is running. * The script waits for each deployment to complete before it continues. */ bool useDefenderDeploy; - /** + /* * When using OpenZeppelin Defender deployments, whether to skip verifying source code on block explorers. Defaults to `false`. */ bool skipVerifySourceCode; - /** + /* * When using OpenZeppelin Defender deployments, the ID of the relayer to use for the deployment. Defaults to the relayer configured for your deployment environment on Defender. */ string relayerId; - /** + /* * Applies to OpenZeppelin Defender deployments only. * If this is not set, deployments will be performed using the CREATE opcode. * If this is set, deployments will be performed using the CREATE2 opcode with the provided salt. @@ -67,25 +67,25 @@ struct DefenderOptions { * WARNING: CREATE2 affects `msg.sender` behavior. See https://docs.openzeppelin.com/defender/v2/tutorial/deploy#deploy-caveat for more information. */ bytes32 salt; - /** + /* * The ID of the upgrade approval process to use when proposing an upgrade. * Defaults to the upgrade approval process configured for your deployment environment on Defender. */ string upgradeApprovalProcessId; - /** + /* * License type to display on block explorers for verified source code. * See https://etherscan.io/contract-license-types for supported values and use the string found in brackets, e.g. MIT. * If not set, infers the license type by using the SPDX license identifier from the contract's Solidity file. * Cannot be set if `skipLicenseType` or `skipVerifySourceCode` is `true`. */ string licenseType; - /** + /* * If set to `true`, does not set the license type on block explorers for verified source code. * Use this if your contract's license type is not supported by block explorers. * Defaults to `false`. */ bool skipLicenseType; - /** + /* * Transaction overrides for OpenZeppelin Defender deployments. */ TxOverrides txOverrides; @@ -95,19 +95,19 @@ struct DefenderOptions { * Transaction overrides for OpenZeppelin Defender deployments. */ struct TxOverrides { - /** + /* * Maximum amount of gas to allow the deployment transaction to use. */ uint256 gasLimit; - /** + /* * Gas price for legacy transactions, in wei. */ uint256 gasPrice; - /** + /* * Maximum total fee per gas, in wei. */ uint256 maxFeePerGas; - /** + /* * Maximum priority fee per gas, in wei. */ uint256 maxPriorityFeePerGas; diff --git a/src/README.adoc b/src/README.adoc deleted file mode 100644 index ac26760a..00000000 --- a/src/README.adoc +++ /dev/null @@ -1,19 +0,0 @@ -= OpenZeppelin Foundry Upgrades API - -== Common Options - -The following options can be used with some of the below functions. See https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades/blob/main/src/Options.sol[Options.sol] for detailed descriptions of each option. - -{{Options}} - -{{DefenderOptions}} - -{{TxOverrides}} - -== Foundry Upgrades - -{{Upgrades}} - -== Foundry Defender - -{{Defender}} diff --git a/src/Upgrades.adoc b/src/Upgrades.adoc new file mode 100644 index 00000000..4e15a299 --- /dev/null +++ b/src/Upgrades.adoc @@ -0,0 +1,3 @@ +{{Upgrades}} + +{{UnsafeUpgrades}} \ No newline at end of file diff --git a/src/Upgrades.sol b/src/Upgrades.sol index d1d9af9c..e354a378 100644 --- a/src/Upgrades.sol +++ b/src/Upgrades.sol @@ -2,23 +2,17 @@ pragma solidity ^0.8.20; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import {ITransparentUpgradeableProxy, TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import {Vm} from "forge-std/Vm.sol"; -import {console} from "forge-std/console.sol"; -import {strings} from "solidity-stringutils/src/strings.sol"; - import {Options} from "./Options.sol"; -import {Versions} from "./internal/Versions.sol"; -import {Utils} from "./internal/Utils.sol"; -import {DefenderDeploy} from "./internal/DefenderDeploy.sol"; +import {Core} from "./internal/Core.sol"; /** * @dev Library for deploying and managing upgradeable contracts from Forge scripts or tests. + * + * NOTE: Requires OpenZeppelin Contracts v5 or higher. */ library Upgrades { /** @@ -35,7 +29,8 @@ library Upgrades { Options memory opts ) internal returns (address) { address impl = deployImplementation(contractName, opts); - return address(_deploy("ERC1967Proxy.sol:ERC1967Proxy", abi.encode(impl, initializerData), opts)); + + return Core.deploy("ERC1967Proxy.sol:ERC1967Proxy", abi.encode(impl, initializerData), opts); } /** @@ -66,13 +61,12 @@ library Upgrades { Options memory opts ) internal returns (address) { address impl = deployImplementation(contractName, opts); + return - address( - _deploy( - "TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy", - abi.encode(impl, initialOwner, initializerData), - opts - ) + Core.deploy( + "TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy", + abi.encode(impl, initialOwner, initializerData), + opts ); } @@ -104,18 +98,7 @@ library Upgrades { * @param opts Common options */ function upgradeProxy(address proxy, string memory contractName, bytes memory data, Options memory opts) internal { - address newImpl = prepareUpgrade(contractName, opts); - - Vm vm = Vm(CHEATCODE_ADDRESS); - - bytes32 adminSlot = vm.load(proxy, ERC1967Utils.ADMIN_SLOT); - if (adminSlot == bytes32(0)) { - // No admin contract: upgrade directly using interface - ITransparentUpgradeableProxy(proxy).upgradeToAndCall(newImpl, data); - } else { - ProxyAdmin admin = ProxyAdmin(address(uint160(uint256(adminSlot)))); - admin.upgradeAndCall(ITransparentUpgradeableProxy(proxy), newImpl, data); - } + Core.upgradeProxy(proxy, contractName, data, opts); } /** @@ -129,7 +112,7 @@ library Upgrades { */ function upgradeProxy(address proxy, string memory contractName, bytes memory data) internal { Options memory opts; - upgradeProxy(proxy, contractName, data, opts); + Core.upgradeProxy(proxy, contractName, data, opts); } /** @@ -154,8 +137,8 @@ library Upgrades { bytes memory data, Options memory opts, address tryCaller - ) internal tryPrank(tryCaller) { - upgradeProxy(proxy, contractName, data, opts); + ) internal { + Core.upgradeProxy(proxy, contractName, data, opts, tryCaller); } /** @@ -173,14 +156,9 @@ library Upgrades { * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. */ - function upgradeProxy( - address proxy, - string memory contractName, - bytes memory data, - address tryCaller - ) internal tryPrank(tryCaller) { + function upgradeProxy(address proxy, string memory contractName, bytes memory data, address tryCaller) internal { Options memory opts; - upgradeProxy(proxy, contractName, data, opts, tryCaller); + Core.upgradeProxy(proxy, contractName, data, opts, tryCaller); } /** @@ -197,7 +175,8 @@ library Upgrades { Options memory opts ) internal returns (address) { address impl = deployImplementation(contractName, opts); - return _deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner), opts); + + return Core.deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner), opts); } /** @@ -222,8 +201,7 @@ library Upgrades { * @param opts Common options */ function upgradeBeacon(address beacon, string memory contractName, Options memory opts) internal { - address newImpl = prepareUpgrade(contractName, opts); - UpgradeableBeacon(beacon).upgradeTo(newImpl); + Core.upgradeBeacon(beacon, contractName, opts); } /** @@ -236,7 +214,7 @@ library Upgrades { */ function upgradeBeacon(address beacon, string memory contractName) internal { Options memory opts; - upgradeBeacon(beacon, contractName, opts); + Core.upgradeBeacon(beacon, contractName, opts); } /** @@ -259,8 +237,8 @@ library Upgrades { string memory contractName, Options memory opts, address tryCaller - ) internal tryPrank(tryCaller) { - upgradeBeacon(beacon, contractName, opts); + ) internal { + Core.upgradeBeacon(beacon, contractName, opts, tryCaller); } /** @@ -277,9 +255,9 @@ library Upgrades { * @param contractName 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 * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. */ - function upgradeBeacon(address beacon, string memory contractName, address tryCaller) internal tryPrank(tryCaller) { + function upgradeBeacon(address beacon, string memory contractName, address tryCaller) internal { Options memory opts; - upgradeBeacon(beacon, contractName, opts, tryCaller); + Core.upgradeBeacon(beacon, contractName, opts, tryCaller); } /** @@ -303,7 +281,7 @@ library Upgrades { * @return Proxy address */ function deployBeaconProxy(address beacon, bytes memory data, Options memory opts) internal returns (address) { - return _deploy("BeaconProxy.sol:BeaconProxy", abi.encode(beacon, data), opts); + return Core.deploy("BeaconProxy.sol:BeaconProxy", abi.encode(beacon, data), opts); } /** @@ -313,7 +291,7 @@ library Upgrades { * @param opts Common options */ function validateImplementation(string memory contractName, Options memory opts) internal { - _validate(contractName, opts, false); + Core.validateImplementation(contractName, opts); } /** @@ -324,8 +302,7 @@ library Upgrades { * @return Address of the implementation contract */ function deployImplementation(string memory contractName, Options memory opts) internal returns (address) { - validateImplementation(contractName, opts); - return _deploy(contractName, opts.constructorData, opts); + return Core.deployImplementation(contractName, opts); } /** @@ -337,7 +314,7 @@ library Upgrades { * @param opts Common options */ function validateUpgrade(string memory contractName, Options memory opts) internal { - _validate(contractName, opts, true); + Core.validateUpgrade(contractName, opts); } /** @@ -353,8 +330,7 @@ library Upgrades { * @return Address of the new implementation contract */ function prepareUpgrade(string memory contractName, Options memory opts) internal returns (address) { - validateUpgrade(contractName, opts); - return _deploy(contractName, opts.constructorData, opts); + return Core.prepareUpgrade(contractName, opts); } /** @@ -364,10 +340,7 @@ library Upgrades { * @return Admin address */ function getAdminAddress(address proxy) internal view returns (address) { - Vm vm = Vm(CHEATCODE_ADDRESS); - - bytes32 adminSlot = vm.load(proxy, ERC1967Utils.ADMIN_SLOT); - return address(uint160(uint256(adminSlot))); + return Core.getAdminAddress(proxy); } /** @@ -377,10 +350,7 @@ library Upgrades { * @return Implementation address */ function getImplementationAddress(address proxy) internal view returns (address) { - Vm vm = Vm(CHEATCODE_ADDRESS); - - bytes32 implSlot = vm.load(proxy, ERC1967Utils.IMPLEMENTATION_SLOT); - return address(uint160(uint256(implSlot))); + return Core.getImplementationAddress(proxy); } /** @@ -390,143 +360,155 @@ library Upgrades { * @return Beacon address */ function getBeaconAddress(address proxy) internal view returns (address) { - Vm vm = Vm(CHEATCODE_ADDRESS); - - bytes32 beaconSlot = vm.load(proxy, ERC1967Utils.BEACON_SLOT); - return address(uint160(uint256(beaconSlot))); + return Core.getBeaconAddress(proxy); } +} +/** + * @dev Library for deploying and managing upgradeable contracts from Forge tests, without validations. + * + * Can be used with `forge coverage`. Requires implementation contracts to be instantiated first. + * Does not require `--ffi` and does not require a clean compilation before each run. + * + * Not supported for OpenZeppelin Defender deployments. + * + * WARNING: Not recommended for use in Forge scripts. + * `UnsafeUpgrades` does not validate whether your contracts are upgrade safe or whether new implementations are compatible with previous ones. + * Use `Upgrades` if you want validations to be run. + * + * NOTE: Requires OpenZeppelin Contracts v5 or higher. + */ +library UnsafeUpgrades { /** - * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * @dev Deploys a UUPS proxy using the given contract address as the implementation. * - * @dev Runs a function as a prank, or just runs the function normally if the prank could not be started. + * @param impl Address of the contract to use as the implementation + * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + * @return Proxy address */ - modifier tryPrank(address deployer) { - Vm vm = Vm(CHEATCODE_ADDRESS); - - try vm.startPrank(deployer) { - _; - vm.stopPrank(); - } catch { - _; - } + function deployUUPSProxy(address impl, bytes memory initializerData) internal returns (address) { + return address(new ERC1967Proxy(impl, initializerData)); } - using strings for *; - address constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - - function _validate(string memory contractName, Options memory opts, bool requireReference) private { - if (opts.unsafeSkipAllChecks) { - return; - } - - string[] memory inputs = _buildValidateCommand(contractName, opts, requireReference); - Vm.FfiResult memory result = Utils.runAsBashCommand(inputs); - string memory stdout = string(result.stdout); - - // CLI validate command uses exit code to indicate if the validation passed or failed. - // As an extra precaution, we also check stdout for "SUCCESS" to ensure it actually ran. - if (result.exitCode == 0 && stdout.toSlice().contains("SUCCESS".toSlice())) { - return; - } else if (result.stderr.length > 0) { - // Validations failed to run - revert(string.concat("Failed to run upgrade safety validation: ", string(result.stderr))); - } else { - // Validations ran but some contracts were not upgrade safe - revert(string.concat("Upgrade safety validation failed:\n", stdout)); - } + /** + * @dev Deploys a transparent proxy using the given contract address as the implementation. + * + * @param impl Address of the contract to use as the implementation + * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy + * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + * @return Proxy address + */ + function deployTransparentProxy( + address impl, + address initialOwner, + bytes memory initializerData + ) internal returns (address) { + return address(new TransparentUpgradeableProxy(impl, initialOwner, initializerData)); } - function _buildValidateCommand( - string memory contractName, - Options memory opts, - bool requireReference - ) private view returns (string[] memory) { - string memory outDir = Utils.getOutDir(); - - string[] memory inputBuilder = new string[](255); - - uint8 i = 0; + /** + * @dev Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + * + * @param proxy Address of the proxy to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + */ + function upgradeProxy(address proxy, address newImpl, bytes memory data) internal { + Core.upgradeProxyTo(proxy, newImpl, data); + } - inputBuilder[i++] = "npx"; - inputBuilder[i++] = string.concat("@openzeppelin/upgrades-core@", Versions.UPGRADES_CORE); - inputBuilder[i++] = "validate"; - inputBuilder[i++] = string.concat(outDir, "/build-info"); - inputBuilder[i++] = "--contract"; - inputBuilder[i++] = Utils.getFullyQualifiedName(contractName, outDir); + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract address. Only supported for UUPS or transparent proxies. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxy(address proxy, address newImpl, bytes memory data, address tryCaller) internal { + Core.upgradeProxyTo(proxy, newImpl, data, tryCaller); + } - if (bytes(opts.referenceContract).length != 0) { - inputBuilder[i++] = "--reference"; - inputBuilder[i++] = Utils.getFullyQualifiedName(opts.referenceContract, outDir); - } + /** + * @dev Deploys an upgradeable beacon using the given contract address as the implementation. + * + * @param impl Address of the contract to use as the implementation + * @param initialOwner Address to set as the owner of the UpgradeableBeacon contract which gets deployed + * @return Beacon address + */ + function deployBeacon(address impl, address initialOwner) internal returns (address) { + return address(new UpgradeableBeacon(impl, initialOwner)); + } - if (opts.unsafeSkipStorageCheck) { - inputBuilder[i++] = "--unsafeSkipStorageCheck"; - } else if (requireReference) { - inputBuilder[i++] = "--requireReference"; - } + /** + * @dev Upgrades a beacon to a new implementation contract address. + * + * @param beacon Address of the beacon to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + */ + function upgradeBeacon(address beacon, address newImpl) internal { + Core.upgradeBeaconTo(beacon, newImpl); + } - if (bytes(opts.unsafeAllow).length != 0) { - inputBuilder[i++] = "--unsafeAllow"; - inputBuilder[i++] = opts.unsafeAllow; - } + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract address. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeacon(address beacon, address newImpl, address tryCaller) internal { + Core.upgradeBeaconTo(beacon, newImpl, tryCaller); + } - if (opts.unsafeAllowRenames) { - inputBuilder[i++] = "--unsafeAllowRenames"; - } + /** + * @dev Deploys a beacon proxy using the given beacon and call data. + * + * @param beacon Address of the beacon to use + * @param data Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + * @return Proxy address + */ + function deployBeaconProxy(address beacon, bytes memory data) internal returns (address) { + return address(new BeaconProxy(beacon, data)); + } - // Create a copy of inputs but with the correct length - string[] memory inputs = new string[](i); - for (uint8 j = 0; j < i; j++) { - inputs[j] = inputBuilder[j]; - } + /** + * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + * + * @param proxy Address of a transparent proxy + * @return Admin address + */ + function getAdminAddress(address proxy) internal view returns (address) { + return Core.getAdminAddress(proxy); + } - return inputs; + /** + * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + * + * @param proxy Address of a transparent or UUPS proxy + * @return Implementation address + */ + function getImplementationAddress(address proxy) internal view returns (address) { + return Core.getImplementationAddress(proxy); } - function _deploy( - string memory contractName, - bytes memory constructorData, - Options memory opts - ) private returns (address) { - if (opts.defender.useDefenderDeploy) { - return DefenderDeploy.deploy(contractName, constructorData, opts.defender); - } else { - bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); - if (deployedAddress == address(0)) { - revert( - string.concat( - "Failed to deploy contract ", - contractName, - ' using constructor data "', - string(constructorData), - '"' - ) - ); - } - return deployedAddress; - } - } - - function _deployFromBytecode(bytes memory bytecode) private returns (address) { - address addr; - assembly { - addr := create(0, add(bytecode, 32), mload(bytecode)) - } - return addr; - } - - /** - * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. - * - * NOTE: This function is never called and has no effect, but must be kept to ensure that the proxy contracts are included in the compilation. - */ - function _precompileProxyContracts() private pure { - bytes memory dummy; - dummy = type(ERC1967Proxy).creationCode; - dummy = type(TransparentUpgradeableProxy).creationCode; - dummy = type(UpgradeableBeacon).creationCode; - dummy = type(BeaconProxy).creationCode; + /** + * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + * + * @param proxy Address of a beacon proxy + * @return Beacon address + */ + function getBeaconAddress(address proxy) internal view returns (address) { + return Core.getBeaconAddress(proxy); } } diff --git a/src/internal/Core.sol b/src/internal/Core.sol new file mode 100644 index 00000000..79dbed4d --- /dev/null +++ b/src/internal/Core.sol @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; +import {console} from "forge-std/console.sol"; +import {strings} from "solidity-stringutils/src/strings.sol"; + +import {Options} from "../Options.sol"; +import {Versions} from "./Versions.sol"; +import {Utils} from "./Utils.sol"; +import {DefenderDeploy} from "./DefenderDeploy.sol"; + +import {IUpgradeableProxy} from "./interfaces/IUpgradeableProxy.sol"; +import {IProxyAdmin} from "./interfaces/IProxyAdmin.sol"; +import {IUpgradeableBeacon} from "./interfaces/IUpgradeableBeacon.sol"; + +/** + * @dev Internal helper methods to validate/deploy implementations and perform upgrades. + * + * WARNING: DO NOT USE DIRECTLY. Use Upgrades.sol, LegacyUpgrades.sol or Defender.sol instead. + */ +library Core { + /** + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param proxy Address of the proxy to upgrade + * @param contractName 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 + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param opts Common options + */ + function upgradeProxy(address proxy, string memory contractName, bytes memory data, Options memory opts) internal { + address newImpl = prepareUpgrade(contractName, opts); + upgradeProxyTo(proxy, newImpl, data); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param contractName 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 + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param opts Common options + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxy( + address proxy, + string memory contractName, + bytes memory data, + Options memory opts, + address tryCaller + ) internal tryPrank(tryCaller) { + upgradeProxy(proxy, contractName, data, opts); + } + + /** + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * @param proxy Address of the proxy to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + */ + function upgradeProxyTo(address proxy, address newImpl, bytes memory data) internal { + Vm vm = Vm(Utils.CHEATCODE_ADDRESS); + + bytes32 adminSlot = vm.load(proxy, ADMIN_SLOT); + if (adminSlot == bytes32(0)) { + string memory upgradeInterfaceVersion = _getUpgradeInterfaceVersion(proxy); + if (upgradeInterfaceVersion.toSlice().equals("5.0.0".toSlice()) || data.length > 0) { + IUpgradeableProxy(proxy).upgradeToAndCall(newImpl, data); + } else { + IUpgradeableProxy(proxy).upgradeTo(newImpl); + } + } else { + address admin = address(uint160(uint256(adminSlot))); + string memory upgradeInterfaceVersion = _getUpgradeInterfaceVersion(admin); + if (upgradeInterfaceVersion.toSlice().equals("5.0.0".toSlice()) || data.length > 0) { + IProxyAdmin(admin).upgradeAndCall(proxy, newImpl, data); + } else { + IProxyAdmin(admin).upgrade(proxy, newImpl); + } + } + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxyTo( + address proxy, + address newImpl, + bytes memory data, + address tryCaller + ) internal tryPrank(tryCaller) { + upgradeProxyTo(proxy, newImpl, data); + } + + /** + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param beacon Address of the beacon to upgrade + * @param contractName 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 + * @param opts Common options + */ + function upgradeBeacon(address beacon, string memory contractName, Options memory opts) internal { + address newImpl = prepareUpgrade(contractName, opts); + upgradeBeaconTo(beacon, newImpl); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param contractName 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 + * @param opts Common options + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeacon( + address beacon, + string memory contractName, + Options memory opts, + address tryCaller + ) internal tryPrank(tryCaller) { + upgradeBeacon(beacon, contractName, opts); + } + + /** + * @dev Upgrades a beacon to a new implementation contract address. + * + * @param beacon Address of the beacon to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + */ + function upgradeBeaconTo(address beacon, address newImpl) internal { + IUpgradeableBeacon(beacon).upgradeTo(newImpl); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param newImpl Address of the new implementation contract to upgrade to + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeaconTo(address beacon, address newImpl, address tryCaller) internal tryPrank(tryCaller) { + upgradeBeaconTo(beacon, newImpl); + } + + /** + * @dev Validates an implementation contract, but does not deploy it. + * + * @param contractName Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param opts Common options + */ + function validateImplementation(string memory contractName, Options memory opts) internal { + _validate(contractName, opts, false); + } + + /** + * @dev Validates and deploys an implementation contract, and returns its address. + * + * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param opts Common options + * @return Address of the implementation contract + */ + function deployImplementation(string memory contractName, Options memory opts) internal returns (address) { + validateImplementation(contractName, opts); + return deploy(contractName, opts.constructorData, opts); + } + + /** + * @dev Validates a new implementation contract in comparison with a reference contract, but does not deploy it. + * + * Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param contractName Name of the contract to validate, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param opts Common options + */ + function validateUpgrade(string memory contractName, Options memory opts) internal { + _validate(contractName, opts, true); + } + + /** + * @dev Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, + * and returns its address. + * + * Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + * + * Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. + * + * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param opts Common options + * @return Address of the new implementation contract + */ + function prepareUpgrade(string memory contractName, Options memory opts) internal returns (address) { + validateUpgrade(contractName, opts); + return deploy(contractName, opts.constructorData, opts); + } + + /** + * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + * + * @param proxy Address of a transparent proxy + * @return Admin address + */ + function getAdminAddress(address proxy) internal view returns (address) { + Vm vm = Vm(Utils.CHEATCODE_ADDRESS); + + bytes32 adminSlot = vm.load(proxy, ADMIN_SLOT); + return address(uint160(uint256(adminSlot))); + } + + /** + * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + * + * @param proxy Address of a transparent or UUPS proxy + * @return Implementation address + */ + function getImplementationAddress(address proxy) internal view returns (address) { + Vm vm = Vm(Utils.CHEATCODE_ADDRESS); + + bytes32 implSlot = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(implSlot))); + } + + /** + * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + * + * @param proxy Address of a beacon proxy + * @return Beacon address + */ + function getBeaconAddress(address proxy) internal view returns (address) { + Vm vm = Vm(Utils.CHEATCODE_ADDRESS); + + bytes32 beaconSlot = vm.load(proxy, BEACON_SLOT); + return address(uint160(uint256(beaconSlot))); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Runs a function as a prank, or just runs the function normally if the prank could not be started. + */ + modifier tryPrank(address deployer) { + Vm vm = Vm(Utils.CHEATCODE_ADDRESS); + + try vm.startPrank(deployer) { + _; + vm.stopPrank(); + } catch { + _; + } + } + + /** + * @dev Storage slot with the address of the implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. + */ + bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Storage slot with the admin of the proxy. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. + */ + bytes32 private constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Storage slot with the UpgradeableBeacon contract which defines the implementation for the proxy. + * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. + */ + bytes32 private constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + using strings for *; + + function _getUpgradeInterfaceVersion(address addr) private returns (string memory) { + (bool success, bytes memory returndata) = addr.call(abi.encodeWithSignature("getUpgradeInterfaceVersion()")); + if (success) { + return abi.decode(returndata, (string)); + } else { + return ""; + } + } + + function _validate(string memory contractName, Options memory opts, bool requireReference) private { + if (opts.unsafeSkipAllChecks) { + return; + } + + string[] memory inputs = _buildValidateCommand(contractName, opts, requireReference); + Vm.FfiResult memory result = Utils.runAsBashCommand(inputs); + string memory stdout = string(result.stdout); + + // CLI validate command uses exit code to indicate if the validation passed or failed. + // As an extra precaution, we also check stdout for "SUCCESS" to ensure it actually ran. + if (result.exitCode == 0 && stdout.toSlice().contains("SUCCESS".toSlice())) { + return; + } else if (result.stderr.length > 0) { + // Validations failed to run + revert(string(abi.encodePacked("Failed to run upgrade safety validation: ", string(result.stderr)))); + } else { + // Validations ran but some contracts were not upgrade safe + revert(string(abi.encodePacked("Upgrade safety validation failed:\n", stdout))); + } + } + + function _buildValidateCommand( + string memory contractName, + Options memory opts, + bool requireReference + ) private view returns (string[] memory) { + string memory outDir = Utils.getOutDir(); + + string[] memory inputBuilder = new string[](255); + + uint8 i = 0; + + inputBuilder[i++] = "npx"; + inputBuilder[i++] = string(abi.encodePacked("@openzeppelin/upgrades-core@", Versions.UPGRADES_CORE)); + inputBuilder[i++] = "validate"; + inputBuilder[i++] = string(abi.encodePacked(outDir, "/build-info")); + inputBuilder[i++] = "--contract"; + inputBuilder[i++] = Utils.getFullyQualifiedName(contractName, outDir); + + if (bytes(opts.referenceContract).length != 0) { + inputBuilder[i++] = "--reference"; + inputBuilder[i++] = Utils.getFullyQualifiedName(opts.referenceContract, outDir); + } + + if (opts.unsafeSkipStorageCheck) { + inputBuilder[i++] = "--unsafeSkipStorageCheck"; + } else if (requireReference) { + inputBuilder[i++] = "--requireReference"; + } + + if (bytes(opts.unsafeAllow).length != 0) { + inputBuilder[i++] = "--unsafeAllow"; + inputBuilder[i++] = opts.unsafeAllow; + } + + if (opts.unsafeAllowRenames) { + inputBuilder[i++] = "--unsafeAllowRenames"; + } + + // Create a copy of inputs but with the correct length + string[] memory inputs = new string[](i); + for (uint8 j = 0; j < i; j++) { + inputs[j] = inputBuilder[j]; + } + + return inputs; + } + + function deploy( + string memory contractName, + bytes memory constructorData, + Options memory opts + ) internal returns (address) { + if (opts.defender.useDefenderDeploy) { + return DefenderDeploy.deploy(contractName, constructorData, opts.defender); + } else { + return _deploy(contractName, constructorData); + } + } + + function _deploy(string memory contractName, bytes memory constructorData) private returns (address) { + bytes memory creationCode = Vm(Utils.CHEATCODE_ADDRESS).getCode(contractName); + address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); + if (deployedAddress == address(0)) { + revert( + string( + abi.encodePacked( + "Failed to deploy contract ", + contractName, + ' using constructor data "', + string(constructorData), + '"' + ) + ) + ); + } + return deployedAddress; + } + + function _deployFromBytecode(bytes memory bytecode) private returns (address) { + address addr; + assembly { + addr := create(0, add(bytecode, 32), mload(bytecode)) + } + return addr; + } +} diff --git a/src/internal/DefenderDeploy.sol b/src/internal/DefenderDeploy.sol index 27063f71..1fdc4c91 100644 --- a/src/internal/DefenderDeploy.sol +++ b/src/internal/DefenderDeploy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; import {Vm} from "forge-std/Vm.sol"; import {console} from "forge-std/console.sol"; @@ -15,7 +15,7 @@ import {ProposeUpgradeResponse, ApprovalProcessResponse} from "../Defender.sol"; /** * @dev Internal helper methods for Defender deployments. * - * DO NOT USE DIRECTLY. Use Defender.sol instead. + * WARNING: DO NOT USE DIRECTLY. Use Defender.sol instead. */ library DefenderDeploy { using strings for *; @@ -39,7 +39,7 @@ library DefenderDeploy { string memory stdout = string(result.stdout); if (result.exitCode != 0) { - revert(string.concat("Failed to deploy contract ", contractName, ": ", string(result.stderr))); + revert(string(abi.encodePacked("Failed to deploy contract ", contractName, ": ", string(result.stderr)))); } string memory deployedAddress = _parseLine("Deployed to address: ", stdout, true); @@ -67,9 +67,8 @@ library DefenderDeploy { uint8 i = 0; inputBuilder[i++] = "npx"; - inputBuilder[i++] = string.concat( - "@openzeppelin/defender-deploy-client-cli@", - Versions.DEFENDER_DEPLOY_CLIENT_CLI + inputBuilder[i++] = string( + abi.encodePacked("@openzeppelin/defender-deploy-client-cli@", Versions.DEFENDER_DEPLOY_CLIENT_CLI) ); inputBuilder[i++] = "deploy"; inputBuilder[i++] = "--contractName"; @@ -89,10 +88,10 @@ library DefenderDeploy { inputBuilder[i++] = "false"; } else if (!(defenderOpts.licenseType).toSlice().empty()) { inputBuilder[i++] = "--licenseType"; - inputBuilder[i++] = string.concat('"', defenderOpts.licenseType, '"'); + inputBuilder[i++] = string(abi.encodePacked('"', defenderOpts.licenseType, '"')); } else if (!defenderOpts.skipLicenseType && !(contractInfo.license).toSlice().empty()) { inputBuilder[i++] = "--licenseType"; - inputBuilder[i++] = string.concat('"', _toLicenseType(contractInfo), '"'); + inputBuilder[i++] = string(abi.encodePacked('"', _toLicenseType(contractInfo), '"')); } if (!(defenderOpts.relayerId).toSlice().empty()) { inputBuilder[i++] = "--relayerId"; @@ -160,12 +159,14 @@ library DefenderDeploy { return "BSL 1.1"; } else { revert( - string.concat( - "SPDX license identifier ", - contractInfo.license, - " in ", - contractInfo.contractPath, - " does not look like a supported license for block explorer verification. Use the `licenseType` option to specify a license type, or set the `skipLicenseType` option to `true` to skip." + string( + abi.encodePacked( + "SPDX license identifier ", + contractInfo.license, + " in ", + contractInfo.contractPath, + " does not look like a supported license for block explorer verification. Use the `licenseType` option to specify a license type, or set the `skipLicenseType` option to `true` to skip." + ) ) ); } @@ -196,11 +197,13 @@ library DefenderDeploy { if (result.exitCode != 0) { revert( - string.concat( - "Failed to propose upgrade for proxy ", - vm.toString(proxyAddress), - ": ", - string(result.stderr) + string( + abi.encodePacked( + "Failed to propose upgrade for proxy ", + vm.toString(proxyAddress), + ": ", + string(result.stderr) + ) ) ); } @@ -229,7 +232,9 @@ library DefenderDeploy { } return slice.toString(); } else if (required) { - revert(string.concat("Failed to find line with prefix '", expectedPrefix, "' in output: ", stdout)); + revert( + string(abi.encodePacked("Failed to find line with prefix '", expectedPrefix, "' in output: ", stdout)) + ); } else { return ""; } @@ -249,9 +254,8 @@ library DefenderDeploy { uint8 i = 0; inputBuilder[i++] = "npx"; - inputBuilder[i++] = string.concat( - "@openzeppelin/defender-deploy-client-cli@", - Versions.DEFENDER_DEPLOY_CLIENT_CLI + inputBuilder[i++] = string( + abi.encodePacked("@openzeppelin/defender-deploy-client-cli@", Versions.DEFENDER_DEPLOY_CLIENT_CLI) ); inputBuilder[i++] = "proposeUpgrade"; inputBuilder[i++] = "--proxyAddress"; @@ -287,7 +291,7 @@ library DefenderDeploy { string memory stdout = string(result.stdout); if (result.exitCode != 0) { - revert(string.concat("Failed to get approval process: ", string(result.stderr))); + revert(string(abi.encodePacked("Failed to get approval process: ", string(result.stderr)))); } return parseApprovalProcessResponse(stdout); @@ -316,9 +320,8 @@ library DefenderDeploy { uint8 i = 0; inputBuilder[i++] = "npx"; - inputBuilder[i++] = string.concat( - "@openzeppelin/defender-deploy-client-cli@", - Versions.DEFENDER_DEPLOY_CLIENT_CLI + inputBuilder[i++] = string( + abi.encodePacked("@openzeppelin/defender-deploy-client-cli@", Versions.DEFENDER_DEPLOY_CLIENT_CLI) ); inputBuilder[i++] = command; inputBuilder[i++] = "--chainId"; diff --git a/src/internal/Utils.sol b/src/internal/Utils.sol index 7837a6ce..b3c149de 100644 --- a/src/internal/Utils.sol +++ b/src/internal/Utils.sol @@ -1,28 +1,28 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; import {Vm} from "forge-std/Vm.sol"; import {console} from "forge-std/console.sol"; import {strings} from "solidity-stringutils/src/strings.sol"; struct ContractInfo { - /** + /* * Contract path, e.g. "src/MyContract.sol" */ string contractPath; - /** + /* * Contract short name, e.g. "MyContract" */ string shortName; - /** + /* * License identifier from the compiled artifact. Empty if not found. */ string license; - /** + /* * keccak256 hash of the source code from metadata */ string sourceCodeHash; - /** + /* * Artifact file path e.g. the path of the file 'out/MyContract.sol/MyContract.json' */ string artifactPath; @@ -46,7 +46,7 @@ library Utils { string memory outDir ) internal view returns (string memory) { ContractInfo memory info = getContractInfo(contractName, outDir); - return string.concat(info.contractPath, ":", info.shortName); + return string(abi.encodePacked(info.contractPath, ":", info.shortName)); } /** @@ -68,21 +68,20 @@ library Utils { string memory fileName = _toFileName(contractName); - string memory artifactPath = string.concat( - vm.projectRoot(), - "/", - outDir, - "/", - fileName, - "/", - info.shortName, - ".json" + string memory artifactPath = string( + abi.encodePacked(vm.projectRoot(), "/", outDir, "/", fileName, "/", info.shortName, ".json") ); string memory artifactJson = vm.readFile(artifactPath); if (!vm.keyExistsJson(artifactJson, ".ast")) { revert( - string.concat("Could not find AST in artifact ", artifactPath, ". Set `ast = true` in foundry.toml") + string( + abi.encodePacked( + "Could not find AST in artifact ", + artifactPath, + ". Set `ast = true` in foundry.toml" + ) + ) ); } info.contractPath = vm.parseJsonString(artifactJson, ".ast.absolutePath"); @@ -91,7 +90,7 @@ library Utils { } info.sourceCodeHash = vm.parseJsonString( artifactJson, - string.concat(".metadata.sources.['", info.contractPath, "'].keccak256") + string(abi.encodePacked(".metadata.sources.['", info.contractPath, "'].keccak256")) ); info.artifactPath = artifactPath; @@ -116,17 +115,19 @@ library Utils { string[] memory inputs = new string[](4); inputs[0] = "grep"; inputs[1] = "-rl"; - inputs[2] = string.concat('"', sourceCodeHash, '"'); - inputs[3] = string.concat(outDir, "/build-info"); + inputs[2] = string(abi.encodePacked('"', sourceCodeHash, '"')); + inputs[3] = string(abi.encodePacked(outDir, "/build-info")); Vm.FfiResult memory result = runAsBashCommand(inputs); string memory stdout = string(result.stdout); if (!stdout.toSlice().endsWith(".json".toSlice())) { revert( - string.concat( - "Could not find build-info file with matching source code hash for contract ", - contractName + string( + abi.encodePacked( + "Could not find build-info file with matching source code hash for contract ", + contractName + ) ) ); } @@ -170,10 +171,12 @@ library Utils { } revert( - string.concat( - "Contract name ", - contractName, - " must be in the format MyContract.sol:MyContract or MyContract.sol or out/MyContract.sol/MyContract.json" + string( + abi.encodePacked( + "Contract name ", + contractName, + " must be in the format MyContract.sol:MyContract or MyContract.sol or out/MyContract.sol/MyContract.json" + ) ) ); } @@ -192,10 +195,12 @@ library Utils { return jsonName.toSlice().until(".json".toSlice()).toString(); } else { revert( - string.concat( - "Contract name ", - contractName, - " must be in the format MyContract.sol:MyContract or MyContract.sol or out/MyContract.sol/MyContract.json" + string( + abi.encodePacked( + "Contract name ", + contractName, + " must be in the format MyContract.sol:MyContract or MyContract.sol or out/MyContract.sol/MyContract.json" + ) ) ); } @@ -210,9 +215,9 @@ library Utils { function toBashCommand(string[] memory inputs, string memory bashPath) internal pure returns (string[] memory) { string memory commandString; for (uint i = 0; i < inputs.length; i++) { - commandString = string.concat(commandString, inputs[i]); + commandString = string(abi.encodePacked(commandString, inputs[i])); if (i != inputs.length - 1) { - commandString = string.concat(commandString, " "); + commandString = string(abi.encodePacked(commandString, " ")); } } @@ -238,10 +243,12 @@ library Utils { if (result.exitCode != 0 && result.stdout.length == 0 && result.stderr.length == 0) { // On Windows, using the bash executable from WSL leads to a non-zero exit code and no output revert( - string.concat( - 'Failed to run bash command with "', - bashCommand[0], - '". 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, add the following line in the .env file of your project (using forward slashes):\nOPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash"' + string( + abi.encodePacked( + 'Failed to run bash command with "', + bashCommand[0], + '". 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, add the following line in the .env file of your project (using forward slashes):\nOPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash"' + ) ) ); } else { diff --git a/src/internal/Versions.sol b/src/internal/Versions.sol index 67cf4a95..1be0f660 100644 --- a/src/internal/Versions.sol +++ b/src/internal/Versions.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; library Versions { // TODO add a workflow to update this automatically based on package.json diff --git a/src/internal/interfaces/IProxyAdmin.sol b/src/internal/interfaces/IProxyAdmin.sol new file mode 100644 index 00000000..dc35a4a8 --- /dev/null +++ b/src/internal/interfaces/IProxyAdmin.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IProxyAdmin { + /** + * Upgrades a proxy to a new implementation without calling a function on the new implementation. + */ + function upgrade(address, address) external; + + /** + * Upgrades a proxy to a new implementation and calls a function on the new implementation. + * If UPGRADE_INTERFACE_VERSION is "5.0.0", bytes can be empty if no function should be called on the new implementation. + */ + function upgradeAndCall(address, address, bytes memory) external payable; +} diff --git a/src/internal/interfaces/IUpgradeableBeacon.sol b/src/internal/interfaces/IUpgradeableBeacon.sol new file mode 100644 index 00000000..9b68f0c2 --- /dev/null +++ b/src/internal/interfaces/IUpgradeableBeacon.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IUpgradeableBeacon { + /** + * Upgrades the beacon to a new implementation. + */ + function upgradeTo(address) external; +} diff --git a/src/internal/interfaces/IUpgradeableProxy.sol b/src/internal/interfaces/IUpgradeableProxy.sol new file mode 100644 index 00000000..13fba96c --- /dev/null +++ b/src/internal/interfaces/IUpgradeableProxy.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IUpgradeableProxy { + /** + * Upgrades the proxy to a new implementation without calling a function on the new implementation. + */ + function upgradeTo(address) external; + + /** + * Upgrades the proxy to a new implementation and calls a function on the new implementation. + * If UPGRADE_INTERFACE_VERSION is "5.0.0", bytes can be empty if no function should be called on the new implementation. + */ + function upgradeToAndCall(address, bytes memory) external payable; +} diff --git a/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/src/.gitkeep b/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.s.sol b/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.s.sol new file mode 100644 index 00000000..2f3f567e --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.s.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Script} from "forge-std/Script.sol"; + +import {Greeter} from "../../openzeppelin-contracts-v4/test/contracts/Greeter.sol"; +import {GreeterProxiable} from "../../openzeppelin-contracts-v4/test/contracts/GreeterProxiable.sol"; +import {GreeterV2} from "../../openzeppelin-contracts-v4/test/contracts/GreeterV2.sol"; +import {GreeterV2Proxiable} from "../../openzeppelin-contracts-v4/test/contracts/GreeterV2Proxiable.sol"; + +import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; + +/** + * @dev Sample script to deploy and upgrade contracts using transparent, UUPS, and beacon proxies. + */ +contract UpgradesScript is Script { + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + // example deployment and upgrade of a transparent proxy + address transparentProxy = Upgrades.deployTransparentProxy( + "Greeter.sol", + msg.sender, + abi.encodeCall(Greeter.initialize, ("hello")) + ); + Upgrades.upgradeProxy(transparentProxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); + + // example deployment and upgrade of a UUPS proxy + address uupsProxy = Upgrades.deployUUPSProxy( + "GreeterProxiable.sol", + abi.encodeCall(GreeterProxiable.initialize, ("hello")) + ); + Upgrades.upgradeProxy( + uupsProxy, + "GreeterV2Proxiable.sol", + abi.encodeCall(GreeterV2Proxiable.resetGreeting, ()) + ); + + // example deployment of a beacon proxy and upgrade of the beacon + address beacon = Upgrades.deployBeacon("Greeter.sol", msg.sender); + Upgrades.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); + Upgrades.upgradeBeacon(beacon, "GreeterV2.sol"); + + vm.stopBroadcast(); + } +} diff --git a/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.t.sol b/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.t.sol new file mode 100644 index 00000000..bfc8ddba --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4-with-v5-proxies/test/Upgrades.t.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; + +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; + +import {Greeter} from "../../openzeppelin-contracts-v4/test/contracts/Greeter.sol"; +import {GreeterProxiable} from "../../openzeppelin-contracts-v4/test/contracts/GreeterProxiable.sol"; +import {GreeterV2} from "../../openzeppelin-contracts-v4/test/contracts/GreeterV2.sol"; +import {GreeterV2Proxiable} from "../../openzeppelin-contracts-v4/test/contracts/GreeterV2Proxiable.sol"; + +/** + * @dev Tests for the Upgrades library. + */ +contract UpgradesTest is Test { + function testUUPS() public { + vm.startPrank(msg.sender); + address proxy = Upgrades.deployUUPSProxy( + "GreeterProxiable.sol", + abi.encodeCall(Greeter.initialize, ("hello")) + ); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeProxy( + proxy, + "GreeterV2Proxiable.sol", + abi.encodeCall(GreeterV2Proxiable.resetGreeting, ()), + msg.sender + ); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testTransparent() public { + vm.startPrank(msg.sender); + address proxy = Upgrades.deployTransparentProxy( + "Greeter.sol", + msg.sender, + abi.encodeCall(Greeter.initialize, ("hello")) + ); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + address adminAddress = Upgrades.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ()), msg.sender); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(Upgrades.getAdminAddress(proxy), adminAddress); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testBeacon() public { + address beacon = Upgrades.deployBeacon("Greeter.sol", msg.sender); + address implAddressV1 = IBeacon(beacon).implementation(); + + vm.startPrank(msg.sender); + address proxy = Upgrades.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + + assertEq(Upgrades.getBeaconAddress(proxy), beacon); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeBeacon(beacon, "GreeterV2.sol", msg.sender); + address implAddressV2 = IBeacon(beacon).implementation(); + + vm.prank(msg.sender); + GreeterV2(address(instance)).setGreeting("modified"); + + assertEq(instance.greeting(), "modified"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUpgradeProxyWithoutCaller() public { + vm.startPrank(msg.sender); + address proxy = Upgrades.deployUUPSProxy( + "GreeterProxiable.sol", + abi.encodeCall(GreeterProxiable.initialize, ("hello")) + ); + Upgrades.upgradeProxy(proxy, "GreeterV2Proxiable.sol", abi.encodeCall(GreeterV2Proxiable.resetGreeting, ())); + vm.stopPrank(); + } + + function testUpgradeBeaconWithoutCaller() public { + address beacon = Upgrades.deployBeacon("Greeter.sol", msg.sender); + + vm.startPrank(msg.sender); + Upgrades.upgradeBeacon(beacon, "GreeterV2.sol"); + vm.stopPrank(); + } +} diff --git a/test-profiles/openzeppelin-contracts-v4/src/.gitkeep b/test-profiles/openzeppelin-contracts-v4/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.s.sol b/test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.s.sol new file mode 100644 index 00000000..cc66d542 --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.s.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; + +import {Greeter} from "./contracts/Greeter.sol"; +import {GreeterProxiable} from "./contracts/GreeterProxiable.sol"; +import {GreeterV2} from "./contracts/GreeterV2.sol"; +import {GreeterV2Proxiable} from "./contracts/GreeterV2Proxiable.sol"; + +import {Upgrades, Options} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; + +/** + * @dev Sample script to upgrade OpenZeppelin Contracts v4 deployments using transparent, UUPS, and beacon proxies. + */ +contract LegacyUpgradesScript is Script { + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + // deploy initial implementations for testing only + address greeter = address(new Greeter()); + address greeterProxiable = address(new GreeterProxiable()); + + // deploy each type of proxy for testing only + address proxyAdmin = address(new ProxyAdmin()); + address transparentProxy = address(new TransparentUpgradeableProxy(greeter, proxyAdmin, abi.encodeWithSelector(Greeter.initialize.selector, ("hello")))); + + address uupsProxy = address(new ERC1967Proxy( + greeterProxiable, + abi.encodeWithSelector(GreeterProxiable.initialize.selector, ("hello")) + )); + + address beacon = address(new UpgradeableBeacon(greeter)); + new BeaconProxy(beacon, abi.encodeWithSelector(Greeter.initialize.selector, ("hello"))); + + // example upgrade of an existing transparent proxy + Upgrades.upgradeProxy(transparentProxy, "GreeterV2.sol", abi.encodeWithSelector(GreeterV2.resetGreeting.selector)); + + // example upgrade of an existing UUPS proxy + Upgrades.upgradeProxy( + uupsProxy, + "GreeterV2Proxiable.sol", + abi.encodeWithSelector(GreeterV2Proxiable.resetGreeting.selector) + ); + + // example upgrade of an existing beacon + Upgrades.upgradeBeacon(beacon, "GreeterV2.sol"); + + vm.stopBroadcast(); + } +} diff --git a/test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.t.sol b/test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.t.sol new file mode 100644 index 00000000..26842b78 --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/LegacyUpgrades.t.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Test} from "forge-std/Test.sol"; + +import {Upgrades} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; + +import {Greeter} from "./contracts/Greeter.sol"; +import {GreeterProxiable} from "./contracts/GreeterProxiable.sol"; +import {GreeterV2} from "./contracts/GreeterV2.sol"; +import {GreeterV2Proxiable} from "./contracts/GreeterV2Proxiable.sol"; + +/** + * @dev Tests for the Upgrades library in LegacyUpgrades. + */ +contract LegacyUpgradesTest is Test { + function testUUPS() public { + vm.startPrank(msg.sender); + address proxy = address(new ERC1967Proxy( + address(new GreeterProxiable()), + abi.encodeWithSelector(GreeterProxiable.initialize.selector, ("hello")) + )); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeProxy( + proxy, + "GreeterV2Proxiable.sol", + abi.encodeWithSelector(GreeterV2Proxiable.resetGreeting.selector), + msg.sender + ); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testTransparent() public { + vm.startPrank(msg.sender); + address proxyAdmin = address(new ProxyAdmin()); + address proxy = address(new TransparentUpgradeableProxy( + address(new Greeter()), + proxyAdmin, + abi.encodeWithSelector(Greeter.initialize.selector, ("hello")) + )); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + address adminAddress = Upgrades.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeWithSelector(GreeterV2.resetGreeting.selector), msg.sender); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(Upgrades.getAdminAddress(proxy), adminAddress); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testBeacon() public { + address implAddressV1 = address(new Greeter()); + + vm.startPrank(msg.sender); + address beacon = address(new UpgradeableBeacon(implAddressV1)); + address proxy = address(new BeaconProxy(beacon, abi.encodeWithSelector(Greeter.initialize.selector, ("hello")))); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + + assertEq(Upgrades.getBeaconAddress(proxy), beacon); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeBeacon(beacon, "GreeterV2.sol", msg.sender); + address implAddressV2 = IBeacon(beacon).implementation(); + + vm.prank(msg.sender); + GreeterV2(address(instance)).setGreeting("modified"); + + assertEq(instance.greeting(), "modified"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUpgradeProxyWithoutCaller() public { + vm.startPrank(msg.sender); + address proxy = address(new ERC1967Proxy( + address(new GreeterProxiable()), + abi.encodeWithSelector(GreeterProxiable.initialize.selector, ("hello")) + )); + Upgrades.upgradeProxy(proxy, "GreeterV2Proxiable.sol", abi.encodeWithSelector(GreeterV2Proxiable.resetGreeting.selector)); + vm.stopPrank(); + } + + function testUpgradeBeaconWithoutCaller() public { + vm.startPrank(msg.sender); + address beacon = address(new UpgradeableBeacon(address(new Greeter()))); + Upgrades.upgradeBeacon(beacon, "GreeterV2.sol"); + vm.stopPrank(); + } +} diff --git a/test-profiles/openzeppelin-contracts-v4/test/UnsafeLegacyUpgrades.t.sol b/test-profiles/openzeppelin-contracts-v4/test/UnsafeLegacyUpgrades.t.sol new file mode 100644 index 00000000..7113634a --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/UnsafeLegacyUpgrades.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Test} from "forge-std/Test.sol"; + +import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/LegacyUpgrades.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; + +import {Greeter} from "./contracts/Greeter.sol"; +import {GreeterProxiable} from "./contracts/GreeterProxiable.sol"; +import {GreeterV2} from "./contracts/GreeterV2.sol"; +import {GreeterV2Proxiable} from "./contracts/GreeterV2Proxiable.sol"; + +/** + * @dev Tests for the UnsafeUpgrades library in LegacyUpgrades. + */ +contract UnsafeLegacyUpgradesTest is Test { + function testUUPS() public { + vm.startPrank(msg.sender); + address proxy = address(new ERC1967Proxy( + address(new GreeterProxiable()), + abi.encodeWithSelector(Greeter.initialize.selector, ("hello")) + )); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + address implAddressV1 = UnsafeUpgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "hello"); + + UnsafeUpgrades.upgradeProxy( + proxy, + address(new GreeterV2Proxiable()), + abi.encodeWithSelector(GreeterV2Proxiable.resetGreeting.selector), + msg.sender + ); + address implAddressV2 = UnsafeUpgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testTransparent() public { + vm.startPrank(msg.sender); + address proxyAdmin = address(new ProxyAdmin()); + address proxy = address(new TransparentUpgradeableProxy( + address(new Greeter()), + proxyAdmin, + abi.encodeWithSelector(Greeter.initialize.selector, ("hello")) + )); + vm.stopPrank(); + + Greeter instance = Greeter(proxy); + address implAddressV1 = UnsafeUpgrades.getImplementationAddress(proxy); + address adminAddress = UnsafeUpgrades.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + assertEq(instance.greeting(), "hello"); + + UnsafeUpgrades.upgradeProxy( + proxy, + address(new GreeterV2()), + abi.encodeWithSelector(GreeterV2.resetGreeting.selector), + msg.sender + ); + address implAddressV2 = UnsafeUpgrades.getImplementationAddress(proxy); + + assertEq(UnsafeUpgrades.getAdminAddress(proxy), adminAddress); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testBeacon() public { + vm.startPrank(msg.sender); + address beacon = address(new UpgradeableBeacon(address(new Greeter()))); + vm.stopPrank(); + + address implAddressV1 = IBeacon(beacon).implementation(); + + address proxy = address(new BeaconProxy(beacon, abi.encodeWithSelector(Greeter.initialize.selector, ("hello")))); + Greeter instance = Greeter(proxy); + + assertEq(UnsafeUpgrades.getBeaconAddress(proxy), beacon); + + assertEq(instance.greeting(), "hello"); + + UnsafeUpgrades.upgradeBeacon(beacon, address(new GreeterV2()), msg.sender); + address implAddressV2 = IBeacon(beacon).implementation(); + + GreeterV2(address(instance)).resetGreeting(); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUpgradeProxyWithoutCaller() public { + vm.startPrank(msg.sender); + address proxy = address(new ERC1967Proxy( + address(new GreeterProxiable()), + abi.encodeWithSelector(GreeterProxiable.initialize.selector, ("hello")) + )); + + UnsafeUpgrades.upgradeProxy( + proxy, + address(new GreeterV2Proxiable()), + abi.encodeWithSelector(GreeterV2Proxiable.resetGreeting.selector) + ); + vm.stopPrank(); + } + + function testUpgradeBeaconWithoutCaller() public { + vm.startPrank(msg.sender); + address beacon = address(new UpgradeableBeacon(address(new Greeter()))); + + UnsafeUpgrades.upgradeBeacon(beacon, address(new GreeterV2())); + vm.stopPrank(); + } +} \ No newline at end of file diff --git a/test-profiles/openzeppelin-contracts-v4/test/contracts/Greeter.sol b/test-profiles/openzeppelin-contracts-v4/test/contracts/Greeter.sol new file mode 100644 index 00000000..22b95f69 --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/contracts/Greeter.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract Greeter is Initializable, OwnableUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(string memory _greeting) initializer public { + __Ownable_init(); + greeting = _greeting; + } +} diff --git a/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterProxiable.sol b/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterProxiable.sol new file mode 100644 index 00000000..a3cc69c9 --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterProxiable.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +contract GreeterProxiable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(string memory _greeting) initializer public { + __Ownable_init(); + __UUPSUpgradeable_init(); + greeting = _greeting; + } + + function _authorizeUpgrade(address newImplementation) + internal + onlyOwner + override + {} +} \ No newline at end of file diff --git a/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterV2.sol b/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterV2.sol new file mode 100644 index 00000000..ff098a94 --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterV2.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/// @custom:oz-upgrades-from Greeter +contract GreeterV2 is Initializable, OwnableUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(string memory _greeting) initializer public { + __Ownable_init(); + greeting = _greeting; + } + + function resetGreeting() public reinitializer(2) { + greeting = "resetted"; + } + + function setGreeting(string memory _greeting) public onlyOwner { + greeting = _greeting; + } +} diff --git a/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterV2Proxiable.sol b/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterV2Proxiable.sol new file mode 100644 index 00000000..6f150e9b --- /dev/null +++ b/test-profiles/openzeppelin-contracts-v4/test/contracts/GreeterV2Proxiable.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +// These contracts are for testing only, they are not safe for use in production. + +/// @custom:oz-upgrades-from GreeterProxiable +contract GreeterV2Proxiable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(string memory _greeting) initializer public { + __Ownable_init(); + __UUPSUpgradeable_init(); + greeting = _greeting; + } + + function _authorizeUpgrade(address newImplementation) + internal + onlyOwner + override + {} + + function resetGreeting() public reinitializer(2) { + greeting = "resetted"; + } +} diff --git a/test/UnsafeUpgrades.t.sol b/test/UnsafeUpgrades.t.sol new file mode 100644 index 00000000..6aa65586 --- /dev/null +++ b/test/UnsafeUpgrades.t.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; + +import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; + +import {Greeter} from "./contracts/Greeter.sol"; +import {GreeterProxiable} from "./contracts/GreeterProxiable.sol"; +import {GreeterV2} from "./contracts/GreeterV2.sol"; +import {GreeterV2Proxiable} from "./contracts/GreeterV2Proxiable.sol"; +import {WithConstructor, NoInitializer} from "./contracts/WithConstructor.sol"; + +/** + * @dev Tests for the UnsafeUpgrades library. + */ +contract UnsafeUpgradesTest is Test { + function testUUPS() public { + address proxy = UnsafeUpgrades.deployUUPSProxy( + address(new GreeterProxiable()), + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) + ); + Greeter instance = Greeter(proxy); + address implAddressV1 = UnsafeUpgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "hello"); + + UnsafeUpgrades.upgradeProxy( + proxy, + address(new GreeterV2Proxiable()), + abi.encodeCall(GreeterV2Proxiable.resetGreeting, ()), + msg.sender + ); + address implAddressV2 = UnsafeUpgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testTransparent() public { + address proxy = UnsafeUpgrades.deployTransparentProxy( + address(new Greeter()), + msg.sender, + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) + ); + Greeter instance = Greeter(proxy); + address implAddressV1 = UnsafeUpgrades.getImplementationAddress(proxy); + address adminAddress = UnsafeUpgrades.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + assertEq(instance.greeting(), "hello"); + + UnsafeUpgrades.upgradeProxy( + proxy, + address(new GreeterV2()), + abi.encodeCall(GreeterV2.resetGreeting, ()), + msg.sender + ); + address implAddressV2 = UnsafeUpgrades.getImplementationAddress(proxy); + + assertEq(UnsafeUpgrades.getAdminAddress(proxy), adminAddress); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testBeacon() public { + address beacon = UnsafeUpgrades.deployBeacon(address(new Greeter()), msg.sender); + address implAddressV1 = IBeacon(beacon).implementation(); + + address proxy = UnsafeUpgrades.deployBeaconProxy( + beacon, + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) + ); + Greeter instance = Greeter(proxy); + + assertEq(UnsafeUpgrades.getBeaconAddress(proxy), beacon); + + assertEq(instance.greeting(), "hello"); + + UnsafeUpgrades.upgradeBeacon(beacon, address(new GreeterV2()), msg.sender); + address implAddressV2 = IBeacon(beacon).implementation(); + + GreeterV2(address(instance)).resetGreeting(); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUpgradeProxyWithoutCaller() public { + address proxy = UnsafeUpgrades.deployUUPSProxy( + address(new GreeterProxiable()), + abi.encodeCall(GreeterProxiable.initialize, (msg.sender, "hello")) + ); + + vm.startPrank(msg.sender); + UnsafeUpgrades.upgradeProxy( + proxy, + address(new GreeterV2Proxiable()), + abi.encodeCall(GreeterV2Proxiable.resetGreeting, ()) + ); + vm.stopPrank(); + } + + function testUpgradeBeaconWithoutCaller() public { + address beacon = UnsafeUpgrades.deployBeacon(address(new Greeter()), msg.sender); + + vm.startPrank(msg.sender); + UnsafeUpgrades.upgradeBeacon(beacon, address(new GreeterV2())); + vm.stopPrank(); + } + + function testWithConstructor() public { + address proxy = UnsafeUpgrades.deployTransparentProxy( + address(new WithConstructor(123)), + msg.sender, + abi.encodeCall(WithConstructor.initialize, (456)) + ); + assertEq(WithConstructor(proxy).a(), 123); + assertEq(WithConstructor(proxy).b(), 456); + } + + function testNoInitializer() public { + address proxy = UnsafeUpgrades.deployTransparentProxy(address(new WithConstructor(123)), msg.sender, ""); + assertEq(WithConstructor(proxy).a(), 123); + } +} diff --git a/test/Upgrades.s.sol b/test/Upgrades.s.sol index 37d1f866..cacc344f 100644 --- a/test/Upgrades.s.sol +++ b/test/Upgrades.s.sol @@ -23,14 +23,14 @@ contract UpgradesScript is Script { address transparentProxy = Upgrades.deployTransparentProxy( "Greeter.sol", msg.sender, - abi.encodeCall(Greeter.initialize, ("hello")) + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) ); Upgrades.upgradeProxy(transparentProxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); // example deployment and upgrade of a UUPS proxy address uupsProxy = Upgrades.deployUUPSProxy( "GreeterProxiable.sol", - abi.encodeCall(GreeterProxiable.initialize, ("hello")) + abi.encodeCall(GreeterProxiable.initialize, (msg.sender, "hello")) ); Upgrades.upgradeProxy( uupsProxy, @@ -40,7 +40,7 @@ contract UpgradesScript is Script { // example deployment of a beacon proxy and upgrade of the beacon address beacon = Upgrades.deployBeacon("Greeter.sol", msg.sender); - Upgrades.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); + Upgrades.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, (msg.sender, "hello"))); Upgrades.upgradeBeacon(beacon, "GreeterV2.sol"); vm.stopBroadcast(); diff --git a/test/Upgrades.t.sol b/test/Upgrades.t.sol index 6ea75886..f784a8c5 100644 --- a/test/Upgrades.t.sol +++ b/test/Upgrades.t.sol @@ -2,11 +2,9 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import {Greeter} from "./contracts/Greeter.sol"; @@ -16,17 +14,17 @@ import {GreeterV2Proxiable} from "./contracts/GreeterV2Proxiable.sol"; import {WithConstructor, NoInitializer} from "./contracts/WithConstructor.sol"; // Import additional contracts to include them for compilation -import {MyContractName} from "./contracts/MyContractFile.sol"; import "./contracts/Validations.sol"; /** * @dev Tests for the Upgrades library. */ contract UpgradesTest is Test { - address constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - function testUUPS() public { - address proxy = Upgrades.deployUUPSProxy("GreeterProxiable.sol", abi.encodeCall(Greeter.initialize, ("hello"))); + address proxy = Upgrades.deployUUPSProxy( + "GreeterProxiable.sol", + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) + ); Greeter instance = Greeter(proxy); address implAddressV1 = Upgrades.getImplementationAddress(proxy); @@ -48,7 +46,7 @@ contract UpgradesTest is Test { address proxy = Upgrades.deployTransparentProxy( "Greeter.sol", msg.sender, - abi.encodeCall(Greeter.initialize, ("hello")) + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) ); Greeter instance = Greeter(proxy); address implAddressV1 = Upgrades.getImplementationAddress(proxy); @@ -71,7 +69,7 @@ contract UpgradesTest is Test { address beacon = Upgrades.deployBeacon("Greeter.sol", msg.sender); address implAddressV1 = IBeacon(beacon).implementation(); - address proxy = Upgrades.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); + address proxy = Upgrades.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, (msg.sender, "hello"))); Greeter instance = Greeter(proxy); assertEq(Upgrades.getBeaconAddress(proxy), beacon); @@ -81,19 +79,19 @@ contract UpgradesTest is Test { Upgrades.upgradeBeacon(beacon, "GreeterV2.sol", msg.sender); address implAddressV2 = IBeacon(beacon).implementation(); - GreeterV2(address(instance)).resetGreeting(); + vm.prank(msg.sender); + GreeterV2(address(instance)).setGreeting("modified"); - assertEq(instance.greeting(), "resetted"); + assertEq(instance.greeting(), "modified"); assertFalse(implAddressV2 == implAddressV1); } function testUpgradeProxyWithoutCaller() public { address proxy = Upgrades.deployUUPSProxy( "GreeterProxiable.sol", - abi.encodeCall(GreeterProxiable.initialize, ("hello")) + abi.encodeCall(GreeterProxiable.initialize, (msg.sender, "hello")) ); - Vm vm = Vm(CHEATCODE_ADDRESS); vm.startPrank(msg.sender); Upgrades.upgradeProxy(proxy, "GreeterV2Proxiable.sol", abi.encodeCall(GreeterV2Proxiable.resetGreeting, ())); vm.stopPrank(); @@ -102,7 +100,6 @@ contract UpgradesTest is Test { function testUpgradeBeaconWithoutCaller() public { address beacon = Upgrades.deployBeacon("Greeter.sol", msg.sender); - Vm vm = Vm(CHEATCODE_ADDRESS); vm.startPrank(msg.sender); Upgrades.upgradeBeacon(beacon, "GreeterV2.sol"); vm.stopPrank(); diff --git a/test/UpgradesUseDefenderDeploy.t.sol b/test/UpgradesUseDefenderDeploy.t.sol index c5aa4fd6..847076d2 100644 --- a/test/UpgradesUseDefenderDeploy.t.sol +++ b/test/UpgradesUseDefenderDeploy.t.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; @@ -18,8 +17,6 @@ import {strings} from "solidity-stringutils/src/strings.sol"; * These do not perform any actual deployments, but just checks that the Defender CLI is invoked and catches its error message since we are using a dev network. */ contract UpgradesUseDefenderDeployTest is Test { - address constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - using strings for *; Deployer d; @@ -40,7 +37,13 @@ contract UpgradesUseDefenderDeployTest is Test { Options memory opts; opts.defender.useDefenderDeploy = true; - try d.deployUUPSProxy("GreeterProxiable.sol", abi.encodeCall(GreeterProxiable.initialize, ("hello")), opts) { + try + d.deployUUPSProxy( + "GreeterProxiable.sol", + abi.encodeCall(GreeterProxiable.initialize, (msg.sender, "hello")), + opts + ) + { fail(); } catch Error(string memory reason) { strings.slice memory slice = reason.toSlice(); @@ -53,7 +56,14 @@ contract UpgradesUseDefenderDeployTest is Test { Options memory opts; opts.defender.useDefenderDeploy = true; - try d.deployTransparentProxy("Greeter.sol", msg.sender, abi.encodeCall(Greeter.initialize, ("hello")), opts) { + try + d.deployTransparentProxy( + "Greeter.sol", + msg.sender, + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")), + opts + ) + { fail(); } catch Error(string memory reason) { strings.slice memory slice = reason.toSlice(); @@ -63,7 +73,10 @@ contract UpgradesUseDefenderDeployTest is Test { } function testUpgradeProxy() public { - address proxy = Upgrades.deployUUPSProxy("GreeterProxiable.sol", abi.encodeCall(Greeter.initialize, ("hello"))); + address proxy = Upgrades.deployUUPSProxy( + "GreeterProxiable.sol", + abi.encodeCall(Greeter.initialize, (msg.sender, "hello")) + ); Options memory opts; opts.defender.useDefenderDeploy = true; @@ -98,7 +111,7 @@ contract UpgradesUseDefenderDeployTest is Test { Options memory opts; opts.defender.useDefenderDeploy = true; - try d.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello")), opts) { + try d.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, (msg.sender, "hello")), opts) { fail(); } catch Error(string memory reason) { strings.slice memory slice = reason.toSlice(); diff --git a/test/contracts/Greeter.sol b/test/contracts/Greeter.sol index 23fcb8fb..24b6fb0d 100644 --- a/test/contracts/Greeter.sol +++ b/test/contracts/Greeter.sol @@ -1,12 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -// These contracts are for testing only, they are not safe for use in production. +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract Greeter is Initializable, OwnableUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } -contract Greeter { string public greeting; - function initialize(string memory _greeting) public { + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); greeting = _greeting; } } diff --git a/test/contracts/GreeterProxiable.sol b/test/contracts/GreeterProxiable.sol index 5ed02f7f..693768d5 100644 --- a/test/contracts/GreeterProxiable.sol +++ b/test/contracts/GreeterProxiable.sol @@ -1,14 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {Proxiable} from "./Proxiable.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -// These contracts are for testing only, they are not safe for use in production. +contract GreeterProxiable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } -contract GreeterProxiable is Proxiable { string public greeting; - function initialize(string memory _greeting) public { + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); greeting = _greeting; } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } diff --git a/test/contracts/GreeterV2.sol b/test/contracts/GreeterV2.sol index b41be7b2..91fc699b 100644 --- a/test/contracts/GreeterV2.sol +++ b/test/contracts/GreeterV2.sol @@ -1,17 +1,28 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -// These contracts are for testing only, they are not safe for use in production. +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; /// @custom:oz-upgrades-from Greeter -contract GreeterV2 { +contract GreeterV2 is Initializable, OwnableUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + string public greeting; - function initialize(string memory _greeting) public { + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); greeting = _greeting; } - function resetGreeting() public { + function resetGreeting() public reinitializer(2) { greeting = "resetted"; } + + function setGreeting(string memory _greeting) public onlyOwner { + greeting = _greeting; + } } diff --git a/test/contracts/GreeterV2Proxiable.sol b/test/contracts/GreeterV2Proxiable.sol index 6763d2db..a5bd5baa 100644 --- a/test/contracts/GreeterV2Proxiable.sol +++ b/test/contracts/GreeterV2Proxiable.sol @@ -1,19 +1,30 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {Proxiable} from "./Proxiable.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; // These contracts are for testing only, they are not safe for use in production. /// @custom:oz-upgrades-from GreeterProxiable -contract GreeterV2Proxiable is Proxiable { +contract GreeterV2Proxiable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + string public greeting; - function initialize(string memory _greeting) public { + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); greeting = _greeting; } - function resetGreeting() public { + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + + function resetGreeting() public reinitializer(2) { greeting = "resetted"; } } diff --git a/test/contracts/Proxiable.sol b/test/contracts/Proxiable.sol deleted file mode 100644 index fb246b9c..00000000 --- a/test/contracts/Proxiable.sol +++ /dev/null @@ -1,55 +0,0 @@ -pragma solidity ^0.8.20; - -// These contracts are for testing only, they are not safe for use in production. - -interface IERC1822Proxiable { - function proxiableUUID() external view returns (bytes32); -} - -contract Proxiable { - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; - - function upgradeToAndCall(address newImplementation, bytes calldata data) external { - try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - if (slot != _IMPLEMENTATION_SLOT) { - revert("slot is unsupported as a uuid"); - } - _setImplementation(newImplementation); - if (data.length > 0) { - /** - * Note that using delegate call can make your implementation contract vulnerable if this function - * is not protected with the `onlyProxy` modifier. Again, this contract is for testing only, it is - * not safe for use in production. Instead, use the `UUPSUpgradeable` contract available in - * @openzeppelin/contracts-upgradeable - */ - /// @custom:oz-upgrades-unsafe-allow delegatecall - (bool success, ) = newImplementation.delegatecall(data); - require(success, "upgrade call reverted"); - } else { - _checkNonPayable(); - } - } catch { - revert("the implementation is not UUPS"); - } - } - - function proxiableUUID() external view virtual returns (bytes32) { - return _IMPLEMENTATION_SLOT; - } - - function _checkNonPayable() private { - if (msg.value > 0) { - revert("non-payable upgrade call"); - } - } - - function _setImplementation(address newImplementation) private { - bytes32 slot = _IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, newImplementation) - } - } -} diff --git a/test/internal/DefenderDeploy.t.sol b/test/internal/DefenderDeploy.t.sol index 68d82933..bd45c8ad 100644 --- a/test/internal/DefenderDeploy.t.sol +++ b/test/internal/DefenderDeploy.t.sol @@ -12,6 +12,7 @@ import {WithConstructor} from "../contracts/WithConstructor.sol"; import {UnrecognizedLicense} from "../contracts/UnrecognizedLicense.sol"; import {NoLicense} from "../contracts/NoLicense.sol"; import {Unlicensed} from "../contracts/Unlicensed.sol"; +import {MyContractName} from "../contracts/MyContractFile.sol"; /** * @dev Tests the DefenderDeploy internal library. diff --git a/test/internal/Utils.t.sol b/test/internal/Utils.t.sol index 1a11f096..6ae89217 100644 --- a/test/internal/Utils.t.sol +++ b/test/internal/Utils.t.sol @@ -6,6 +6,8 @@ import {strings} from "solidity-stringutils/src/strings.sol"; import {Utils, ContractInfo} from "openzeppelin-foundry-upgrades/internal/Utils.sol"; +import {MyContractName} from "../contracts/MyContractFile.sol"; + /** * @dev Tests the Utils internal library. */ @@ -17,7 +19,7 @@ contract UtilsTest is Test { assertEq(info.contractPath, "test/contracts/Greeter.sol"); assertEq(info.license, "MIT"); - assertEq(info.sourceCodeHash, "0xf9875b1fd90da13f5f990d5ba7e66481f4b7e13e4a8f57fa9145fe90a1cb9324"); // source code hash of Greeter.sol + assertEq(info.sourceCodeHash, "0x9564e0245350d0eb5e42a8fed97d87518dbfbddf7668ed383f97a8558b2a9c39"); // source code hash of Greeter.sol } function testGetContractInfo_from_fileAndName() public view { diff --git a/yarn.lock b/yarn.lock index 0d2d3789..35bc205c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -423,6 +423,21 @@ "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" +"@openzeppelin/contracts-upgradeable-v4@npm:@openzeppelin/contracts-upgradeable@^v4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz#38b21708a719da647de4bb0e4802ee235a0d24df" + integrity sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA== + +"@openzeppelin/contracts-upgradeable@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.2.tgz#3e5321a2ecdd0b206064356798c21225b6ec7105" + integrity sha512-0MmkHSHiW2NRFiT9/r5Lu4eJq5UJ4/tzlOgYXNAIj/ONkQTVnz22pLxDvp4C4uZ9he7ZFvGn3Driptn1/iU7tQ== + +"@openzeppelin/contracts-v4@npm:@openzeppelin/contracts@^v4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" + integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA== + "@openzeppelin/contracts@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210"