Skip to content

Commit

Permalink
Merge branch 'master' into upgradeToAndCallHardhat
Browse files Browse the repository at this point in the history
  • Loading branch information
ericglau authored Sep 27, 2023
2 parents 69ce9a1 + 754f203 commit 8d376e4
Show file tree
Hide file tree
Showing 72 changed files with 3,480 additions and 62 deletions.
4 changes: 2 additions & 2 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
* xref:index.adoc[Overview]
* xref:truffle-upgrades.adoc[Using with Truffle]
* xref:hardhat-upgrades.adoc[Using with Hardhat]
** xref:defender-deploy.adoc[OpenZeppelin Defender integration]
* xref:truffle-upgrades.adoc[Using with Truffle]
* xref:writing-upgradeable.adoc[Writing Upgradeable Contracts]
* xref:proxies.adoc[Proxy Upgrade Pattern]
* xref:network-files.adoc[Network Files]
* xref:faq.adoc[Frequently Asked Questions]
.API Reference
* xref:api-truffle-upgrades.adoc[Truffle Upgrades]
* xref:api-hardhat-upgrades.adoc[Hardhat Upgrades]
* xref:api-truffle-upgrades.adoc[Truffle Upgrades]
* xref:api-core.adoc[Upgrades Core & CLI]
5 changes: 5 additions & 0 deletions docs/modules/ROOT/pages/api-core.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ If any errors are found, the command will exit with a non-zero exit code and pri
* `--unsafeAllowRenames` - Configure storage layout check to allow variable renaming.
* `--unsafeSkipStorageCheck` - Skips checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort.

[[high-level-api]]
== High-Level API

The high-level API is a programmatic equivalent to the <<validate-command, validate command>>. Use this API if you want to validate all of your project's upgradeable contracts from a JavaScript or TypeScript environment.
Expand Down Expand Up @@ -193,6 +194,8 @@ An object that represents the result of upgrade safety checks and storage layout

== Low-Level API

NOTE: This low-level API is deprecated. Use the <<high-level-api>> instead.

The low-level API works with https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description[Solidity input and output JSON objects] and lets you perform upgrade safety checks and storage layout comparisons on individual contracts. Use this API if you want to validate specific contracts rather than a whole project.

=== Prerequisites
Expand Down Expand Up @@ -229,6 +232,7 @@ constructor UpgradeableContract(
unsafeSkipStorageCheck?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
},
solcVersion?: string,
): UpgradeableContract
----

Expand All @@ -244,6 +248,7 @@ Creates a new instance of `UpgradeableContract`.
** `unsafeAllow`
** `unsafeAllowRenames`
** `unsafeSkipStorageCheck`
* `solcVersion` - the Solidity version used to compile the implementation contract.

TIP: In Hardhat, `solcInput` and `solcOutput` can be obtained from the Build Info file, which itself can be retrieved with `hre.artifacts.getBuildInfo`.

Expand Down
2 changes: 2 additions & 0 deletions docs/modules/ROOT/pages/api-truffle-upgrades.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
= OpenZeppelin Truffle Upgrades API

WARNING: This package is deprecated. The recommended alternative is to use https://hardhat.org/[Hardhat] along with the xref:hardhat-upgrades.adoc[Hardhat Upgrades] plugin.

Both `deployProxy` and `upgradeProxy` functions will return instances of https://www.trufflesuite.com/docs/truffle/reference/contract-abstractions[Truffle contracts], and require Truffle contract classes (retrieved via `artifacts.require`) as arguments. For https://docs.openzeppelin.com/contracts/4.x/api/proxy#beacon[beacons], `deployBeacon` and `upgradeBeacon` will both return an upgradable beacon instance that can be used with a beacon proxy. All deploy and upgrade functions validate that the implementation contract is upgrade-safe, and will fail otherwise.

[[common-options]]
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/ROOT/pages/truffle-upgrades.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
= Using with Truffle

WARNING: This package is deprecated. The recommended alternative is to use https://hardhat.org/[Hardhat] along with the xref:hardhat-upgrades.adoc[Hardhat Upgrades] plugin.

This package adds functions to your Truffle migrations and tests so you can deploy and upgrade proxies for your contracts.

NOTE: Usage from https://www.trufflesuite.com/docs/truffle/getting-started/writing-external-scripts[Truffle external scripts] is not yet supported.
Expand Down
11 changes: 11 additions & 0 deletions docs/modules/ROOT/pages/writing-upgradeable.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -454,3 +454,14 @@ contract Base {
----

To help determine the proper storage gap size in the new version of your contract, you can simply attempt an upgrade using `upgradeProxy` or just run the validations with `validateUpgrade` (see docs for xref:api-hardhat-upgrades.adoc[Hardhat] or xref:api-truffle-upgrades.adoc[Truffle]). If a storage gap is not being reduced properly, you will see an error message indicating the expected size of the storage gap.

[[namespaced-storage-layout]]
=== Namespaced Storage Layout

https://eips.ethereum.org/EIPS/eip-7201[ERC-7201: Namespaced Storage Layout] is another convention that can be used to avoid storage layout errors when modifying base contracts or when changing the inheritance order of contracts. This convention is used in the upgradeable variant of OpenZeppelin Contracts starting with version 5.0.

This convention involves placing all storage variables of a contract into one or more structs and annotating those structs with `@custom:storage-location erc7201:<NAMESPACE_ID>`. A namespace id is a string that uniquely identifies each namespace in a contract, so the same id must not be defined more than once in a contract or any of its base contracts.

When using namespaced storage layouts, the OpenZeppelin Upgrades plugins will automatically detect the namespace ids and validate that each change within a namespace during an upgrade is safe according to the same rules as described in <<modifying-your-contracts>>.

NOTE: Solidity version 0.8.20 or higher is required in order to use the Upgrades plugins with namespaced storage layouts. The plugins will give an error if they detect `@custom:storage-location` annotations with an older version of Solidity, because older versions of the compiler do not produce sufficient information for validation of namespaced storage layouts.
2 changes: 2 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## Unreleased

- Support new upgrade interface in OpenZeppelin Contracts 5.0. ([883](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/883))
- Deprecate low-level API. Use [CLI or high-level API](https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core) instead.
- Add validations for namespaced storage layout. ([#876](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/876))

## 1.29.0 (2023-09-19)

Expand Down
181 changes: 181 additions & 0 deletions packages/core/contracts/test/Namespaced.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Example {
/// @custom:storage-location erc7201:example.main
struct MainStorage {
uint256 x;
uint256 y;
}

// keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant MAIN_STORAGE_LOCATION =
0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500;

function _getMainStorage() private pure returns (MainStorage storage $) {
assembly {
$.slot := MAIN_STORAGE_LOCATION
}
}

function _getXTimesY() internal view returns (uint256) {
MainStorage storage $ = _getMainStorage();
return $.x * $.y;
}
}

contract MultipleNamespaces {
/// @custom:storage-location erc7201:one
struct S1 {
uint256 a;
}

/// @custom:storage-location erc7201:two
struct S2 {
uint128 a;
}
}

contract ExampleV2_Ok {
/// @custom:storage-location erc7201:example.main
struct MainStorage {
uint256 x;
uint256 y;
uint256 z;
}

// keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant MAIN_STORAGE_LOCATION =
0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500;

function _getMainStorage() private pure returns (MainStorage storage $) {
assembly {
$.slot := MAIN_STORAGE_LOCATION
}
}

function _getXTimesYPlusZ() internal view returns (uint256) {
MainStorage storage $ = _getMainStorage();
return $.x * $.y + $.z;
}
}

contract ExampleV2_Bad {
/// @custom:storage-location erc7201:example.main
struct MainStorage {
uint256 y;
}

// keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant MAIN_STORAGE_LOCATION =
0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500;

function _getMainStorage() private pure returns (MainStorage storage $) {
assembly {
$.slot := MAIN_STORAGE_LOCATION
}
}

function _getYSquared() internal view returns (uint256) {
MainStorage storage $ = _getMainStorage();
return $.y * $.y;
}
}

contract RecursiveStruct {
struct MyStruct {
uint128 a;
uint256 b;
}

/// @custom:storage-location erc7201:example.main
struct MainStorage {
MyStruct s;
uint256 y;
}
}

contract RecursiveStructV2_Outer_Ok {
struct MyStruct {
uint128 a;
uint256 b;
}

/// @custom:storage-location erc7201:example.main
struct MainStorage {
MyStruct s;
uint256 y;
uint256 z;
}
}

contract RecursiveStructV2_Bad {
struct MyStruct {
uint128 a;
uint256 b;
uint256 c;
}

/// @custom:storage-location erc7201:example.main
struct MainStorage {
MyStruct s;
uint256 y;
}
}

contract MultipleNamespacesAndRegularVariables {
/// @custom:storage-location erc7201:one
struct S1 {
uint128 a;
uint256 b;
}

/// @custom:storage-location erc7201:two
struct S2 {
uint128 a;
uint256 b;
}

uint128 public a;
uint256 public b;
}

contract MultipleNamespacesAndRegularVariablesV2_Ok {
/// @custom:storage-location erc7201:one
struct S1 {
uint128 a;
uint256 b;
uint256 c;
}

/// @custom:storage-location erc7201:two
struct S2 {
uint128 a;
uint256 b;
uint256 c;
}

uint128 public a;
uint256 public b;
uint256 public c;
}

contract MultipleNamespacesAndRegularVariablesV2_Bad {
/// @custom:storage-location erc7201:one
struct S1 {
uint256 c;
uint128 a;
uint256 b;
}

/// @custom:storage-location erc7201:two
struct S2 {
uint256 c;
uint128 a;
uint256 b;
}

uint256 public c;
uint128 public a;
uint256 public b;
}
59 changes: 59 additions & 0 deletions packages/core/contracts/test/NamespacedConflicts.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DuplicateNamespace {
function foo() public pure returns (uint256) {
return 0;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting1 {
uint256 b;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting2 {
uint256 c;
}

function foo2() public pure returns (uint256) {
return 0;
}
}

contract Parent {
function foo5() public pure returns (uint256) {
return 0;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting0 {
uint256 a;
}

function foo6() public pure returns (uint256) {
return 0;
}
}

contract ConflictsWithParent is Parent {
function foo3() public pure returns (uint256) {
return 0;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting {
uint256 a;
}

function foo4() public pure returns (uint256) {
return 0;
}
}

contract ConflictsInBothParents is DuplicateNamespace, ConflictsWithParent {
uint256 a;
}

contract InheritsDuplicate is DuplicateNamespace {
}
35 changes: 35 additions & 0 deletions packages/core/contracts/test/NamespacedConflictsLayout.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DuplicateNamespace {
/// @custom:storage-location erc7201:conflicting
struct Conflicting1 {
uint256 b;
} Conflicting1 $Conflicting1;

/// @custom:storage-location erc7201:conflicting
struct Conflicting2 {
uint256 c;
} Conflicting2 $Conflicting2;
}

contract Parent {
/// @custom:storage-location erc7201:conflicting
struct Conflicting0 {
uint256 a;
} Conflicting0 $Conflicting0;
}

contract ConflictsWithParent is Parent {
/// @custom:storage-location erc7201:conflicting
struct Conflicting {
uint256 a;
} Conflicting $Conflicting;
}

contract ConflictsInBothParents is DuplicateNamespace, ConflictsWithParent {
uint256 a;
}

contract InheritsDuplicate is DuplicateNamespace {
}
16 changes: 16 additions & 0 deletions packages/core/contracts/test/NamespacedLayout.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Example {
MainStorage $MainStorage;

/// @custom:storage-location erc7201:example.main
struct MainStorage {
uint256 x;
uint256 y;
}

// keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant MAIN_STORAGE_LOCATION =
0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500;
}
Loading

0 comments on commit 8d376e4

Please sign in to comment.