Skip to content

Commit

Permalink
Update "Using with Upgrades" page for 5.0 (#4659)
Browse files Browse the repository at this point in the history
(cherry picked from commit 0f89a7e)
  • Loading branch information
ericglau committed Oct 5, 2023
1 parent bd25a0a commit 6a0cfce
Showing 1 changed file with 12 additions and 8 deletions.
20 changes: 12 additions & 8 deletions docs/modules/ROOT/pages/upgradeable.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

If your contract is going to be deployed with upgradeability, such as using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins], you will need to use the Upgradeable variant of OpenZeppelin Contracts.

This variant is available as a separate package called `@openzeppelin/contracts-upgradeable`, which is hosted in the repository https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[OpenZeppelin/openzeppelin-contracts-upgradeable].
This variant is available as a separate package called `@openzeppelin/contracts-upgradeable`, which is hosted in the repository https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[OpenZeppelin/openzeppelin-contracts-upgradeable]. It uses `@openzeppelin/contracts` as a peer dependency.

It follows all of the rules for xref:upgrades-plugins::writing-upgradeable.adoc[Writing Upgradeable Contracts]: constructors are replaced by initializer functions, state variables are initialized in initializer functions, and we additionally check for storage incompatibilities across minor versions.

Expand All @@ -13,12 +13,12 @@ TIP: OpenZeppelin provides a full suite of tools for deploying and securing upgr
=== Installation

```console
$ npm install @openzeppelin/contracts-upgradeable
$ npm install @openzeppelin/contracts-upgradeable @openzeppelin/contracts
```

=== Usage

The package replicates the structure of the main OpenZeppelin Contracts package, but every file and contract has the suffix `Upgradeable`.
The Upgradeable package replicates the structure of the main OpenZeppelin Contracts package, but every file and contract has the suffix `Upgradeable`.

```diff
-import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
Expand All @@ -28,6 +28,8 @@ The package replicates the structure of the main OpenZeppelin Contracts package,
+contract MyCollectible is ERC721Upgradeable {
```

NOTE: Interfaces and libraries are not included in the Upgradeable package, but are instead imported from the main OpenZeppelin Contracts package.

Constructors are replaced by internal initializer functions following the naming convention `+__{ContractName}_init+`. Since these are internal, you must always define your own public initializer function and call the parent initializer of the contract you extend.

```diff
Expand All @@ -50,8 +52,8 @@ async function main() {

const mc = await upgrades.deployProxy(MyCollectible);

await mc.deployed();
console.log("MyCollectible deployed to:", mc.address);
await mc.waitForDeployment();
console.log("MyCollectible deployed to:", await mc.getAddress());
}

main();
Expand All @@ -66,8 +68,10 @@ Initializer functions are not linearized by the compiler like constructors. Beca

The function `+__{ContractName}_init_unchained+` found in every contract is the initializer function minus the calls to parent initializers, and can be used to avoid the double initialization problem, but doing this manually is not recommended. We hope to be able to implement safety checks for this in future versions of the Upgrades Plugins.

=== Storage Gaps
=== Namespaced Storage

You may notice that contracts use a struct with the `@custom:storage-location erc7201:<NAMESPACE_ID>` annotation to store the contract's state variables. This follows the https://eips.ethereum.org/EIPS/eip-7201[ERC-7201: Namespaced Storage Layout] pattern, where each contract has its own storage layout in a namespace that is separate from other contracts in the inheritance chain.

You may notice that every contract includes a state variable named `+__gap+`. This is empty reserved space in storage that is put in place in Upgradeable contracts. It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments.
Without namespaced storage, it isn't safe to simply add a state variable because it "shifts down" all of the state variables below in the inheritance chain. This makes the storage layouts incompatible, as explained in xref:upgrades-plugins::writing-upgradeable.adoc#modifying-your-contracts[Writing Upgradeable Contracts].

It isn't safe to simply add a state variable because it "shifts down" all of the state variables below in the inheritance chain. This makes the storage layouts incompatible, as explained in xref:upgrades-plugins::writing-upgradeable.adoc#modifying-your-contracts[Writing Upgradeable Contracts]. The size of the `+__gap+` array is calculated so that the amount of storage used by a contract always adds up to the same number (in this case 50 storage slots).
The namespaced storage pattern used in the Upgradeable package allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments. It also allows changing the inheritance order with no impact on the resulting storage layout, as long as all inherited contracts use namespaced storage.

0 comments on commit 6a0cfce

Please sign in to comment.