Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Deploy proxy contracts from OpenZeppelin Contracts 5.0 #919

Merged
merged 31 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a91d2f7
Deploy proxies from OpenZeppelin Contracts 5.0
ericglau Nov 9, 2023
d84f89d
Only include artifact jsons
ericglau Nov 9, 2023
d1b300e
Run tests from packages
ericglau Nov 9, 2023
6cfcb65
Fix ci
ericglau Nov 10, 2023
3025241
Remove ProxyAdmin from defender deploy
ericglau Nov 10, 2023
2a1bfd8
Support etherscan verify of ProxyAdmin v5
ericglau Nov 10, 2023
20967a6
Fallback if owner function not found in admin
ericglau Nov 10, 2023
79c5d60
Lint
ericglau Nov 10, 2023
ca24455
Rename function, update comment
ericglau Nov 10, 2023
aba6bdb
Merge branch 'master' into 5.0proxies
ericglau Nov 13, 2023
d4a6803
replace if-elseif-else with switch
Amxx Nov 21, 2023
efcbc26
Update upgrade-proxy.ts
Amxx Nov 21, 2023
956edd2
Merge branch 'master' into 5.0proxies
ericglau Nov 21, 2023
c692e21
add misisng }
Amxx Nov 21, 2023
9d2d5c5
fix lint
Amxx Nov 21, 2023
ca9547a
Merge branch 'master' into 5.0proxies
ericglau Nov 22, 2023
8657d5a
Merge remote-tracking branch 'origin/5.0proxies' into 5.0proxies
ericglau Nov 22, 2023
7060df9
Clarify broken contract in tests
ericglau Nov 22, 2023
94f783f
Update packages/core/scripts/copy-build-info-v5.js
ericglau Nov 22, 2023
65683be
Rename to copy-legacy-artifacts.sh, add more checks
ericglau Nov 22, 2023
385d3a4
Use jq in version check
ericglau Nov 22, 2023
6bb0b3c
Inline encodeFunctionData
ericglau Nov 22, 2023
e0ac42f
Verify proxy admin using owner that was broadcast during construction
ericglau Nov 23, 2023
407c1b6
Temporary logging
ericglau Nov 23, 2023
24000f2
Remove logging, replace existing v5 artifacts dir
ericglau Nov 23, 2023
31c2199
Merge branch 'master' into 5.0proxies
ericglau Nov 28, 2023
e457755
Prepare alpha release versions
ericglau Nov 28, 2023
f5d2ba3
Fix merge from master
ericglau Nov 28, 2023
9347f85
Apply suggestions from code review
ericglau Nov 30, 2023
dfd6508
Update upgrades-core, use ethers.provider.getSigner
ericglau Nov 30, 2023
ea3c4e3
Use constants
ericglau Nov 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,15 @@ it('works before and after upgrading', async function () {

## How do the plugins work?

Both plugins provide functions which take care of managing upgradeable deployments of your contracts.
The plugins provide functions which take care of managing upgradeable deployments of your contracts.

For example, `deployProxy` does the following:

1. Validate that the implementation is [upgrade safe](https://docs.openzeppelin.com/upgrades-plugins/faq#what-does-it-mean-for-a-contract-to-be-upgrade-safe)

2. Deploy a [proxy admin](https://docs.openzeppelin.com/upgrades-plugins/faq#what-is-a-proxy-admin) for your project (if needed)

3. Check if there is an [implementation contract](https://docs.openzeppelin.com/upgrades-plugins/faq#what-is-an-implementation-contract) deployed with the same bytecode, and deploy one if not
2. Check if there is an [implementation contract](https://docs.openzeppelin.com/upgrades-plugins/faq#what-is-an-implementation-contract) deployed with the same bytecode, and deploy one if not

4. Create and initialize the proxy contract
3. Create and initialize the proxy contract, along with a [proxy admin](https://docs.openzeppelin.com/upgrades-plugins/faq#what-is-a-proxy-admin) (if needed)

And when you call `upgradeProxy`:

Expand All @@ -104,7 +102,7 @@ And when you call `upgradeProxy`:

3. Upgrade the proxy to use the new implementation contract

The plugins will keep track of all the implementation contracts you have deployed in an `.openzeppelin` folder in the project root, as well as the proxy admin. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).
The plugins will keep track of all the implementation contracts you have deployed in an `.openzeppelin` folder in the project root. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).

> Note: the format of the files within the `.openzeppelin` folder is not compatible with those of the [OpenZeppelin CLI](https://docs.openzeppelin.com/cli). If you want to use these plugins for an existing OpenZeppelin CLI project, we will be sharing soon a guide on how to migrate.

Expand All @@ -116,9 +114,9 @@ For UUPS and transparent proxies, use `deployProxy` and `upgradeProxy` as shown

## Managing ownership

Transparent proxies define an _admin_ address which has the rights to upgrade them. By default, the admin is a [proxy admin contract](https://docs.openzeppelin.com/upgrades-plugins/faq#what-is-a-proxy-admin) deployed behind the scenes. You can change the admin of a proxy by calling the `admin.changeProxyAdmin` function in the plugin. Keep in mind that the _admin_ of a proxy can only upgrade it, but not interact with the implementation contract. Read [here](https://docs.openzeppelin.com/upgrades-plugins/proxies#transparent-proxies-and-function-clashes) for more info on this restriction.
Transparent proxies have an _admin_ address which has the rights to upgrade them. By default, the admin is a [proxy admin contract](https://docs.openzeppelin.com/upgrades-plugins/faq#what-is-a-proxy-admin) deployed behind the scenes. Keep in mind that the _admin_ of a proxy can only upgrade it, but not interact with the implementation contract. Read [here](https://docs.openzeppelin.com/upgrades-plugins/proxies#transparent-proxies-and-function-clashes) for more info on this restriction.

The proxy admin contract also defines an _owner_ address which has the rights to operate it. By default, this address is the externally owned account used during deployment. You can change the proxy admin owner by calling the `admin.transferProxyAdminOwnership` function in the plugin. Note that changing the proxy admin owner effectively transfers the power to upgrade any proxy in your whole project to the new owner, so use with care. Refer to each plugin documentation for more details on the `admin` functions.
The proxy admin contract also defines an _owner_ address which has the rights to operate it. By default, this address is the `initialOwner` address used during deployment of the transparent proxy if provided, otherwise it is the externally owned account used during deployment. You can change the proxy admin owner by calling the `admin.transferProxyAdminOwnership` function in the plugin. Refer to each plugin documentation for more details on the `admin` functions.

UUPS and beacon proxies do not use admin addresses. UUPS proxies rely on an [`_authorizeUpgrade`](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable-_authorizeUpgrade-address-) function to be overridden to include access restriction to the upgrade mechanism, whereas beacon proxies are upgradable only by the owner of their corresponding beacon.

Expand Down
59 changes: 20 additions & 39 deletions docs/modules/ROOT/pages/api-hardhat-upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ The following options are common to some functions.
* `unsafeAllowRenames`: (`boolean`) Configure storage layout check to allow variable renaming.
* `unsafeSkipStorageCheck`: (`boolean`) upgrades the proxy or beacon without first checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort.
* `constructorArgs`: (`unknown[]`) Provide arguments for the constructor of 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.
* `timeout`: (`number`) Timeout in milliseconds to wait for the transaction confirmation when deploying an implementation contract or proxy admin contract. Defaults to `60000`. Use `0` to wait indefinitely.
* `pollingInterval`: (`number`) Polling interval in milliseconds between checks for the transaction confirmation when deploying an implementation contract or proxy admin contract. Defaults to `5000`.
* `initialOwner`: (`string`) the address to set as the initial owner of a transparent proxy's admin or initial owner of a beacon. Defaults to the externally owned account that is deploying the transparent proxy or beacon. Not supported for UUPS proxies.
** *Since:* `@openzeppelin/hardhat-upgrades@3.0.0`
* `timeout`: (`number`) Timeout in milliseconds to wait for the transaction confirmation when deploying an implementation contract. Defaults to `60000`. Use `0` to wait indefinitely.
* `pollingInterval`: (`number`) Polling interval in milliseconds between checks for the transaction confirmation when deploying an implementation contract. Defaults to `5000`.
* `redeployImplementation`: (`"always" | "never" | "onchange"`) Determines whether the implementation contract will be redeployed. Defaults to `"onchange"`.
** If set to `"always"`, the implementation contract is always redeployed even if it was previously deployed with the same bytecode. This can be used with the `salt` option when deploying a proxy through OpenZeppelin Defender to ensure that the implementation contract is deployed with the same salt as the proxy.
** If set to `"never"`, the implementation contract is never redeployed. If the implementation contract was not previously deployed or is not found in the network file, an error will be thrown.
Expand Down Expand Up @@ -51,6 +53,7 @@ async function deployProxy(
initializer?: string | false,
unsafeAllow?: ValidationError[],
constructorArgs?: unknown[],
initialOwner?: string,
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
Expand Down Expand Up @@ -124,6 +127,7 @@ async function deployBeacon(
opts?: {
unsafeAllow?: ValidationError[],
constructorArgs?: unknown[],
initialOwner?: string,
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
Expand Down Expand Up @@ -568,37 +572,6 @@ Similar to `prepareUpgrade`. This method validates and deploys the new implement

* an object with the URL of the Defender proposal, and the ethers transaction response corresponding to the deployment of the new implementation contract. Note that if the new implementation contract was originally imported as a result of `forceImport`, the ethers transaction response will be undefined.

[[deploy-proxy-admin]]
== deployProxyAdmin

[source,ts]
----
async function deployProxyAdmin(
signer?: ethers.Signer,
opts?: {
timeout?: number,
pollingInterval?: number,
txOverrides?: ethers.Overrides,
},
): Promise<string>
----

Deploys a https://docs.openzeppelin.com/contracts/4.x/api/proxy#ProxyAdmin[proxy admin] contract and returns its address if one was not already deployed on the current network, or just returns the address of the proxy admin if one was already deployed. Note that this plugin currently only supports using one proxy admin per network.

*Parameters:*

* `signer` - the signer to use for deployment.
* `opts` - an object with options:
** additional options as described in <<common-options>>.

*Returns:*

* the address of the proxy admin.

*Since:*

* `@openzeppelin/hardhat-upgrades@1.20.0`

[[admin-change-proxy-admin]]
== admin.changeProxyAdmin

Expand Down Expand Up @@ -632,23 +605,31 @@ NOTE: This function is not supported with admins or proxies from OpenZeppelin Co
[source,ts]
----
async function transferProxyAdminOwnership(
newAdmin: string,
proxyAddress: string,
newOwner: string,
signer?: ethers.Signer,
opts?: {
txOverrides?: ethers.Overrides,
}
): Promise<void>
----

Changes the owner of the proxy admin contract, which is the default admin for upgrade rights over all proxies.
Changes the owner of the proxy admin contract for a specific proxy.

NOTE: The `proxyAddress` parameter is required since `@openzeppelin/hardhat-upgrades@3.0.0`

*Parameters:*

* `newAdmin` - the new admin address.
* `proxyAddress` - the address of the proxy whose admin ownership is to be transferred.
* `newOwner` - the new owner address for the proxy admin contract.
* `signer` - the signer to use for the transaction.
* `opts` - an object with options:
** additional options as described in <<common-options>>.

*Since:*

* `@openzeppelin/hardhat-upgrades@3.0.0`

[[erc1967]]
== erc1967

Expand Down Expand Up @@ -712,9 +693,9 @@ The arguments are the same as for hardhat-verify's `verify` task. If the provid
The following contracts will be verified when you run this task on your proxy address:

* Your implementation contract
* https://docs.openzeppelin.com/contracts/4.x/api/proxy#ERC1967Proxy[ERC1967Proxy] or https://docs.openzeppelin.com/contracts/4.x/api/proxy#TransparentUpgradeableProxy[TransparentUpgradeableProxy] or https://docs.openzeppelin.com/contracts/4.x/api/proxy#BeaconProxy[BeaconProxy] (for UUPS, transparent, or beacon proxies, respectively)
* https://docs.openzeppelin.com/contracts/4.x/api/proxy#ProxyAdmin[ProxyAdmin] (with transparent proxies)
* https://docs.openzeppelin.com/contracts/4.x/api/proxy#UpgradeableBeacon[UpgradeableBeacon] (with beacon proxies)
* https://docs.openzeppelin.com/contracts/api/proxy#ERC1967Proxy[ERC1967Proxy] or https://docs.openzeppelin.com/contracts/api/proxy#TransparentUpgradeableProxy[TransparentUpgradeableProxy] or https://docs.openzeppelin.com/contracts/api/proxy#BeaconProxy[BeaconProxy] (for UUPS, transparent, or beacon proxies, respectively)
* https://docs.openzeppelin.com/contracts/api/proxy#ProxyAdmin[ProxyAdmin] (with transparent proxies)
* https://docs.openzeppelin.com/contracts/api/proxy#UpgradeableBeacon[UpgradeableBeacon] (with beacon proxies)

*Since:*

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/faq.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ You can read more about how to make storage-compatible changes to an implementat
[[what-is-a-proxy-admin]]
== What is a proxy admin?

A `ProxyAdmin` is a contract that acts as the owner of all your proxies. Only one per network gets deployed. When you start your project, the `ProxyAdmin` is owned by the deployer address, but you can transfer ownership of it by calling xref:contracts:api:access.adoc#Ownable-transferOwnership-address-[`transferOwnership`].
A `ProxyAdmin` is an intermediary contract that acts as the upgrader of a transparent proxy. Each `ProxyAdmin` is owned by the deployer address, or by the `initialOwner` address when deploying a transparent proxy from OpenZeppelin Contracts 5.0 or above. You can transfer the ownership of a proxy admin by calling xref:contracts:api:access.adoc#Ownable-transferOwnership-address-[`transferOwnership`].

[[what-is-an-implementation-contract]]
== What is an implementation contract?
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/hardhat-upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ async function main() {
main();
----

This will automatically check that the `Box` contract is upgrade-safe, set up a proxy admin (if needed), deploy an implementation contract for the `Box` contract (unless there is one already from a previous deployment), create a proxy, and initialize it by calling `initialize(42)`.
This will automatically check that the `Box` contract is upgrade-safe, deploy an implementation contract for the `Box` contract (unless there is one already from a previous deployment), create a proxy (along with a proxy admin if needed), and initialize it by calling `initialize(42)`.

Then, in another script, you can use the `upgradeProxy` function to upgrade the deployed instance to a new version. The new version can be a different contract (such as `BoxV2`), or you can just modify the existing `Box` contract and recompile it - the plugin will note it changed.

Expand Down
8 changes: 3 additions & 5 deletions docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,15 @@ it('works before and after upgrading', async function () {
[[how-plugins-work]]
== How the plugins work

Both plugins provide functions which take care of managing upgradeable deployments of your contracts.
The plugins provide functions which take care of managing upgradeable deployments of your contracts.

For example, `deployProxy` does the following:

1. Validate that the implementation is xref:faq.adoc#what-does-it-mean-for-a-contract-to-be-upgrade-safe[upgrade safe].

2. Deploy a xref:faq.adoc#what-is-a-proxy-admin[proxy admin] for your project (if needed).
2. Deploy the xref:faq.adoc#what-is-an-implementation-contract[implementation contract].

3. Deploy the xref:faq.adoc#what-is-an-implementation-contract[implementation contract].

4. Create and initialize the proxy contract.
3. Create and initialize the proxy contract, along with a xref:faq.adoc#what-is-a-proxy-admin[proxy admin] (if needed).

And when you call `upgradeProxy`:

Expand Down
6 changes: 1 addition & 5 deletions docs/modules/ROOT/pages/network-files.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= Network Files

OpenZeppelin Upgrades keep track of all the contract versions you have deployed in an `.openzeppelin` folder in the project root, as well as the proxy admin. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).
OpenZeppelin Upgrades keep track of all the contract versions you have deployed in an `.openzeppelin` folder in the project root. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).

NOTE: The format of the files within the `.openzeppelin` folder is not compatible with those of the xref:cli::index.adoc[OpenZeppelin CLI]. If you want to use these plugins for an existing OpenZeppelin CLI project, you have to migrate it first. See xref:migrate-from-cli.adoc[Migrate from CLI] for instructions.

Expand Down Expand Up @@ -32,10 +32,6 @@ OpenZeppelin Upgrades will generate a file for each of the networks you work on
"types": {...}
}
}
},
"admin": {
"address": "...",
"txHash": "..."
}
}
----
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/proxies.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ Assuming a proxy with an `owner()` and an `upgradeTo()` function, that delegates
|Other |returns erc20.owner() |fails |returns erc20.transfer()
|==============================================================

Fortunately, OpenZeppelin Upgrades accounts for this situation, and creates an intermediary ProxyAdmin contract that is in charge of all the proxies you create via the Upgrades plugins. Even if you call the `deploy` command from your node's default account, the ProxyAdmin contract will be the actual admin of all your proxies. This means that you will be able to interact with the proxies from any of your node's accounts, without having to worry about the nuances of the transparent proxy pattern. Only advanced users that create proxies from Solidity need to be aware of the transparent proxies pattern.
Fortunately, OpenZeppelin Upgrades accounts for this situation, and uses an intermediary ProxyAdmin contract for each transparent proxy. Even if you call the `deploy` command from your node's default account, the ProxyAdmin contracts will be the actual admins of your transparent proxies. This means that you will be able to interact with the proxies from any of your node's accounts, without having to worry about the nuances of the transparent proxy pattern. Only advanced users that create proxies from Solidity need to be aware of the transparent proxies pattern.

[[summary]]
== Summary
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"scripts": {
"docs": "oz-docs",
"docs:watch": "oz-docs watch",
"prepare": "wsrun -ms compile:contracts && tsc -b",
"prepare": "wsrun -ms prepare && tsc -b",
"lint": "yarn lint:path .",
"lint:path": "eslint --ignore-path .gitignore --max-warnings 0",
"test": "tsc -b && wsrun -ms test",
"test": "wsrun -ms test",
"coverage": "nyc yarn test"
},
"devDependencies": {
Expand All @@ -19,7 +19,7 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-unicorn": "^49.0.0",
"ethers": "^6.6.0",
"ethers": "^6.8.1",
"lerna": "^5.0.0",
"nyc": "^15.1.0",
"prettier": "^3.0.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Support deploying proxies from OpenZeppelin Contracts 5.0.

## 1.31.3 (2023-11-28)

- Fix Hardhat compile errors when contracts have overloaded functions or standalone NatSpec documentation. ([#918](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/918))
Expand Down
7 changes: 1 addition & 6 deletions packages/core/contracts/import.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";

// Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
}
16 changes: 12 additions & 4 deletions packages/core/contracts/test/Proxiable.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
// These contracts are for testing only, they are not safe for use in production.

abstract contract Proxiable is UUPSUpgradeable {
function _authorizeUpgrade(address newImplementation) internal override {
abstract contract BrokenProxiable {
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

// NOT SAFE FOR PRODUCTION USE.
// This does NOT actually perform any upgrade, but is only for tests to check that this function exists.
function upgradeToAndCall(address newImplementation, bytes calldata data) external {
_authorizeUpgrade(newImplementation);
}

function _authorizeUpgrade(address newImplementation) internal {
_beforeUpgrade(newImplementation);
}

function _beforeUpgrade(address newImplementation) internal virtual;
}

contract ChildOfProxiable is Proxiable {
contract ChildOfProxiable is BrokenProxiable {
function _beforeUpgrade(address newImplementation) internal virtual override {}
}
Loading