From 5b81ca3da1fd018cb193c1571afab2e525bb4aaa Mon Sep 17 00:00:00 2001 From: Eric Forgy Date: Thu, 15 May 2025 22:21:09 -0700 Subject: [PATCH 1/7] Added: _reentrancyGuardStorageSlot() --- contracts/utils/ReentrancyGuardTransient.sol | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contracts/utils/ReentrancyGuardTransient.sol b/contracts/utils/ReentrancyGuardTransient.sol index a1318c86f3c..9863b333f16 100644 --- a/contracts/utils/ReentrancyGuardTransient.sol +++ b/contracts/utils/ReentrancyGuardTransient.sol @@ -44,11 +44,11 @@ abstract contract ReentrancyGuardTransient { } // Any calls to nonReentrant after this point will fail - REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true); + _reentrancyGuardStorageSlot().asBoolean().tstore(true); } function _nonReentrantAfter() private { - REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false); + _reentrancyGuardStorageSlot().asBoolean().tstore(false); } /** @@ -56,6 +56,10 @@ abstract contract ReentrancyGuardTransient { * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { - return REENTRANCY_GUARD_STORAGE.asBoolean().tload(); + return _reentrancyGuardStorageSlot().asBoolean().tload(); + } + + function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) { + return REENTRANCY_GUARD_STORAGE; } } From 750a60b92d62ecbb87f58a95062cc08bc4e22b67 Mon Sep 17 00:00:00 2001 From: Eric Forgy Date: Thu, 15 May 2025 22:47:00 -0700 Subject: [PATCH 2/7] Add changeset --- .changeset/three-parents-argue.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/three-parents-argue.md diff --git a/.changeset/three-parents-argue.md b/.changeset/three-parents-argue.md new file mode 100644 index 00000000000..95517262fb9 --- /dev/null +++ b/.changeset/three-parents-argue.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': patch +--- + +Add `_reentrancyGuardStorageSlot()` to `ReentrancyGuardTransient` as a `pure virtual` function to allow slot overrides for diamond-compatible modular usage. Related to #5681. From 4c3c847a5706fff0a1ab2b7769b8ec6df099d33b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 27 Aug 2025 22:29:59 +0200 Subject: [PATCH 3/7] Add _reentrancyGuardStorageSlot() to ReentrancyGuard (non-transient) --- contracts/utils/ReentrancyGuard.sol | 31 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/contracts/utils/ReentrancyGuard.sol b/contracts/utils/ReentrancyGuard.sol index a95fb512f31..6c5e33d6232 100644 --- a/contracts/utils/ReentrancyGuard.sol +++ b/contracts/utils/ReentrancyGuard.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.20; +import {StorageSlot} from "./StorageSlot.sol"; + /** * @dev Contract module that helps prevent reentrant calls to a function. * @@ -21,8 +23,19 @@ pragma solidity ^0.8.20; * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + * + * As of v5.5, this storage-based version of the reentrancy guard is DEPRECATED. In the + * next major release (v6.0), {ReentrancyGuardTransient} will be removed, and its logic + * moved to this contract, making transient storage the standard storage option for + * reentrancy guards. */ abstract contract ReentrancyGuard { + using StorageSlot for bytes32; + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant REENTRANCY_GUARD_STORAGE = + 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write @@ -37,15 +50,13 @@ abstract contract ReentrancyGuard { uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; - uint256 private _status; - /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { - _status = NOT_ENTERED; + _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED; } /** @@ -62,19 +73,19 @@ abstract contract ReentrancyGuard { } function _nonReentrantBefore() private { - // On the first call to nonReentrant, _status will be NOT_ENTERED - if (_status == ENTERED) { + // On the first call to nonReentrant, REENTRANCY_GUARD_STORAGE.getUint256Slot().value will be NOT_ENTERED + if (_reentrancyGuardEntered()) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail - _status = ENTERED; + _reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) - _status = NOT_ENTERED; + _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED; } /** @@ -82,6 +93,10 @@ abstract contract ReentrancyGuard { * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { - return _status == ENTERED; + return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED; + } + + function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) { + return REENTRANCY_GUARD_STORAGE; } } From f8a43dc5aab593a567ce535808b279c741478742 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 27 Aug 2025 22:31:25 +0200 Subject: [PATCH 4/7] update changeset --- .changeset/three-parents-argue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/three-parents-argue.md b/.changeset/three-parents-argue.md index 95517262fb9..6c62c89ab35 100644 --- a/.changeset/three-parents-argue.md +++ b/.changeset/three-parents-argue.md @@ -2,4 +2,4 @@ 'openzeppelin-solidity': patch --- -Add `_reentrancyGuardStorageSlot()` to `ReentrancyGuardTransient` as a `pure virtual` function to allow slot overrides for diamond-compatible modular usage. Related to #5681. +`ReentrancyGuard` and `ReentrancyGuardTransient`: Add `_reentrancyGuardStorageSlot()`, a `pure virtual` function, that can be overriden to allow slot customization. From 58f3ed5407137d68f0bedbf6ad26ce7a28ee5e7d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 2 Sep 2025 18:00:47 +0200 Subject: [PATCH 5/7] Update .changeset/three-parents-argue.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernesto García --- .changeset/three-parents-argue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/three-parents-argue.md b/.changeset/three-parents-argue.md index 6c62c89ab35..b4986fc6a21 100644 --- a/.changeset/three-parents-argue.md +++ b/.changeset/three-parents-argue.md @@ -2,4 +2,4 @@ 'openzeppelin-solidity': patch --- -`ReentrancyGuard` and `ReentrancyGuardTransient`: Add `_reentrancyGuardStorageSlot()`, a `pure virtual` function, that can be overriden to allow slot customization. +`ReentrancyGuard`, `ReentrancyGuardTransient`: Add an internal `_reentrancyGuardStorageSlot` function allowing slot customization via override. From 4b5ce785b0a5c1a237480a74315ff3c76ea71078 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 2 Sep 2025 18:01:36 +0200 Subject: [PATCH 6/7] Update contracts/utils/ReentrancyGuard.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernesto García --- contracts/utils/ReentrancyGuard.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/utils/ReentrancyGuard.sol b/contracts/utils/ReentrancyGuard.sol index f958d5e8fe1..be35f0d4ab5 100644 --- a/contracts/utils/ReentrancyGuard.sol +++ b/contracts/utils/ReentrancyGuard.sol @@ -24,10 +24,8 @@ import {StorageSlot} from "./StorageSlot.sol"; * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. * - * As of v5.5, this storage-based version of the reentrancy guard is DEPRECATED. In the - * next major release (v6.0), {ReentrancyGuardTransient} will be removed, and its logic - * moved to this contract, making transient storage the standard storage option for - * reentrancy guards. + * IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced + * by the {ReentrancyGuardTransient} variant in v6.0. */ abstract contract ReentrancyGuard { using StorageSlot for bytes32; From e813756712c60024dfdce18c264386e5d103e51d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 2 Sep 2025 18:17:34 +0200 Subject: [PATCH 7/7] Keep unused upgradeable structure to avoid upgrade plugin errors --- scripts/upgradeable/upgradeable.patch | 42 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/scripts/upgradeable/upgradeable.patch b/scripts/upgradeable/upgradeable.patch index 0fd5caa052b..ad3ed680894 100644 --- a/scripts/upgradeable/upgradeable.patch +++ b/scripts/upgradeable/upgradeable.patch @@ -59,10 +59,10 @@ index ff596b0c3..000000000 - - diff --git a/README.md b/README.md -index 60d0a430a..0e4f91a6d 100644 +index 2f92281b3..a0e46695d 100644 --- a/README.md +++ b/README.md -@@ -19,6 +19,9 @@ +@@ -20,6 +20,9 @@ > [!IMPORTANT] > OpenZeppelin Contracts uses semantic versioning to communicate backwards compatibility of its API and storage layout. For upgradeable contracts, the storage layout of different major versions should be assumed incompatible, for example, it is unsafe to upgrade from 4.9.3 to 5.0.0. Learn more at [Backwards Compatibility](https://docs.openzeppelin.com/contracts/backwards-compatibility). @@ -72,7 +72,7 @@ index 60d0a430a..0e4f91a6d 100644 ## Overview ### Installation -@@ -26,7 +29,7 @@ +@@ -27,7 +30,7 @@ #### Hardhat (npm) ``` @@ -81,7 +81,7 @@ index 60d0a430a..0e4f91a6d 100644 ``` #### Foundry (git) -@@ -38,10 +41,10 @@ $ npm install @openzeppelin/contracts +@@ -39,10 +42,10 @@ $ npm install @openzeppelin/contracts > Foundry installs the latest version initially, but subsequent `forge update` commands will use the `master` branch. ``` @@ -94,7 +94,7 @@ index 60d0a430a..0e4f91a6d 100644 ### Usage -@@ -50,10 +53,11 @@ Once installed, you can use the contracts in the library by importing them: +@@ -51,10 +54,11 @@ Once installed, you can use the contracts in the library by importing them: ```solidity pragma solidity ^0.8.20; @@ -110,7 +110,7 @@ index 60d0a430a..0e4f91a6d 100644 } ``` diff --git a/contracts/package.json b/contracts/package.json -index 70ae73bc2..ef659873f 100644 +index 8ccb9465e..509cd7f05 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,5 +1,5 @@ @@ -118,7 +118,7 @@ index 70ae73bc2..ef659873f 100644 - "name": "@openzeppelin/contracts", + "name": "@openzeppelin/contracts-upgradeable", "description": "Secure Smart Contract library for Solidity", - "version": "5.3.0", + "version": "5.4.0", "files": [ @@ -13,7 +13,7 @@ }, @@ -139,12 +139,28 @@ index 70ae73bc2..ef659873f 100644 + "@openzeppelin/contracts": "" + } } +diff --git a/contracts/utils/ReentrancyGuard.sol b/contracts/utils/ReentrancyGuard.sol +index f958d5e8f..845209bc8 100644 +--- a/contracts/utils/ReentrancyGuard.sol ++++ b/contracts/utils/ReentrancyGuard.sol +@@ -36,6 +36,11 @@ abstract contract ReentrancyGuard { + bytes32 private constant REENTRANCY_GUARD_STORAGE = + 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + ++ /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard ++ struct ReentrancyGuardStorage { ++ uint256 _status; ++ } ++ + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol -index c39954e35..fe681f87a 100644 +index 0eaef9d27..01f1b5f58 100644 --- a/contracts/utils/cryptography/EIP712.sol +++ b/contracts/utils/cryptography/EIP712.sol @@ -4,7 +4,6 @@ - pragma solidity ^0.8.20; + pragma solidity ^0.8.24; import {MessageHashUtils} from "./MessageHashUtils.sol"; -import {ShortStrings, ShortString} from "../ShortStrings.sol"; @@ -314,10 +330,10 @@ index c39954e35..fe681f87a 100644 } } diff --git a/package.json b/package.json -index eeeaf0bcd..65581c544 100644 +index 285a074ed..d48cc8e4b 100644 --- a/package.json +++ b/package.json -@@ -34,7 +34,7 @@ +@@ -35,7 +35,7 @@ }, "repository": { "type": "git", @@ -335,7 +351,7 @@ index 304d1386a..a1cd63bee 100644 +@openzeppelin/contracts-upgradeable/=contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ diff --git a/test/account/AccountERC7702.test.js b/test/account/AccountERC7702.test.js -index d08a52209..7a44bccfe 100644 +index f442d49af..8f22dc926 100644 --- a/test/account/AccountERC7702.test.js +++ b/test/account/AccountERC7702.test.js @@ -26,8 +26,8 @@ async function fixture() { @@ -350,7 +366,7 @@ index d08a52209..7a44bccfe 100644 verifyingContract: mock.address, }; diff --git a/test/account/examples/AccountERC7702WithModulesMock.test.js b/test/account/examples/AccountERC7702WithModulesMock.test.js -index 9ee5f9177..f6106bcc7 100644 +index 8ceab19d1..c3f4194a6 100644 --- a/test/account/examples/AccountERC7702WithModulesMock.test.js +++ b/test/account/examples/AccountERC7702WithModulesMock.test.js @@ -36,8 +36,8 @@ async function fixture() {