Skip to content

Commit 4e35b0b

Browse files
AmxxEricForgyernestognw
authored
Add _reentrancyGuardStorageSlot() (#5892)
Co-authored-by: Eric Forgy <[email protected]> Co-authored-by: Ernesto García <[email protected]>
1 parent 7b9d0ce commit 4e35b0b

File tree

4 files changed

+61
-23
lines changed

4 files changed

+61
-23
lines changed

.changeset/three-parents-argue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': patch
3+
---
4+
5+
`ReentrancyGuard`, `ReentrancyGuardTransient`: Add an internal `_reentrancyGuardStorageSlot` function allowing slot customization via override.

contracts/utils/ReentrancyGuard.sol

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
pragma solidity ^0.8.20;
55

6+
import {StorageSlot} from "./StorageSlot.sol";
7+
68
/**
79
* @dev Contract module that helps prevent reentrant calls to a function.
810
*
@@ -21,8 +23,17 @@ pragma solidity ^0.8.20;
2123
* TIP: If you would like to learn more about reentrancy and alternative ways
2224
* to protect against it, check out our blog post
2325
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
26+
*
27+
* IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced
28+
* by the {ReentrancyGuardTransient} variant in v6.0.
2429
*/
2530
abstract contract ReentrancyGuard {
31+
using StorageSlot for bytes32;
32+
33+
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
34+
bytes32 private constant REENTRANCY_GUARD_STORAGE =
35+
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
36+
2637
// Booleans are more expensive than uint256 or any type that takes up a full
2738
// word because each write operation emits an extra SLOAD to first read the
2839
// slot's contents, replace the bits taken up by the boolean, and then write
@@ -37,15 +48,13 @@ abstract contract ReentrancyGuard {
3748
uint256 private constant NOT_ENTERED = 1;
3849
uint256 private constant ENTERED = 2;
3950

40-
uint256 private _status;
41-
4251
/**
4352
* @dev Unauthorized reentrant call.
4453
*/
4554
error ReentrancyGuardReentrantCall();
4655

4756
constructor() {
48-
_status = NOT_ENTERED;
57+
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
4958
}
5059

5160
/**
@@ -75,7 +84,7 @@ abstract contract ReentrancyGuard {
7584
}
7685

7786
function _nonReentrantBeforeView() private view {
78-
if (_status == ENTERED) {
87+
if (_reentrancyGuardEntered()) {
7988
revert ReentrancyGuardReentrantCall();
8089
}
8190
}
@@ -85,20 +94,24 @@ abstract contract ReentrancyGuard {
8594
_nonReentrantBeforeView();
8695

8796
// Any calls to nonReentrant after this point will fail
88-
_status = ENTERED;
97+
_reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
8998
}
9099

91100
function _nonReentrantAfter() private {
92101
// By storing the original value once again, a refund is triggered (see
93102
// https://eips.ethereum.org/EIPS/eip-2200)
94-
_status = NOT_ENTERED;
103+
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
95104
}
96105

97106
/**
98107
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
99108
* `nonReentrant` function in the call stack.
100109
*/
101110
function _reentrancyGuardEntered() internal view returns (bool) {
102-
return _status == ENTERED;
111+
return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
112+
}
113+
114+
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
115+
return REENTRANCY_GUARD_STORAGE;
103116
}
104117
}

contracts/utils/ReentrancyGuardTransient.sol

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,22 @@ abstract contract ReentrancyGuardTransient {
6161
_nonReentrantBeforeView();
6262

6363
// Any calls to nonReentrant after this point will fail
64-
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
64+
_reentrancyGuardStorageSlot().asBoolean().tstore(true);
6565
}
6666

6767
function _nonReentrantAfter() private {
68-
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
68+
_reentrancyGuardStorageSlot().asBoolean().tstore(false);
6969
}
7070

7171
/**
7272
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
7373
* `nonReentrant` function in the call stack.
7474
*/
7575
function _reentrancyGuardEntered() internal view returns (bool) {
76-
return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
76+
return _reentrancyGuardStorageSlot().asBoolean().tload();
77+
}
78+
79+
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
80+
return REENTRANCY_GUARD_STORAGE;
7781
}
7882
}

scripts/upgradeable/upgradeable.patch

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ index ff596b0c3..000000000
5959
-<!-- Make sure that you have reviewed the OpenZeppelin Contracts Contributor Guidelines. -->
6060
-<!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md -->
6161
diff --git a/README.md b/README.md
62-
index 60d0a430a..0e4f91a6d 100644
62+
index 2f92281b3..a0e46695d 100644
6363
--- a/README.md
6464
+++ b/README.md
65-
@@ -19,6 +19,9 @@
65+
@@ -20,6 +20,9 @@
6666
> [!IMPORTANT]
6767
> 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).
6868

@@ -72,7 +72,7 @@ index 60d0a430a..0e4f91a6d 100644
7272
## Overview
7373

7474
### Installation
75-
@@ -26,7 +29,7 @@
75+
@@ -27,7 +30,7 @@
7676
#### Hardhat (npm)
7777

7878
```
@@ -81,7 +81,7 @@ index 60d0a430a..0e4f91a6d 100644
8181
```
8282

8383
#### Foundry (git)
84-
@@ -38,10 +41,10 @@ $ npm install @openzeppelin/contracts
84+
@@ -39,10 +42,10 @@ $ npm install @openzeppelin/contracts
8585
> Foundry installs the latest version initially, but subsequent `forge update` commands will use the `master` branch.
8686

8787
```
@@ -94,7 +94,7 @@ index 60d0a430a..0e4f91a6d 100644
9494

9595
### Usage
9696

97-
@@ -50,10 +53,11 @@ Once installed, you can use the contracts in the library by importing them:
97+
@@ -51,10 +54,11 @@ Once installed, you can use the contracts in the library by importing them:
9898
```solidity
9999
pragma solidity ^0.8.20;
100100

@@ -110,15 +110,15 @@ index 60d0a430a..0e4f91a6d 100644
110110
}
111111
```
112112
diff --git a/contracts/package.json b/contracts/package.json
113-
index 70ae73bc2..ef659873f 100644
113+
index 8ccb9465e..509cd7f05 100644
114114
--- a/contracts/package.json
115115
+++ b/contracts/package.json
116116
@@ -1,5 +1,5 @@
117117
{
118118
- "name": "@openzeppelin/contracts",
119119
+ "name": "@openzeppelin/contracts-upgradeable",
120120
"description": "Secure Smart Contract library for Solidity",
121-
"version": "5.3.0",
121+
"version": "5.4.0",
122122
"files": [
123123
@@ -13,7 +13,7 @@
124124
},
@@ -139,12 +139,28 @@ index 70ae73bc2..ef659873f 100644
139139
+ "@openzeppelin/contracts": "<package-version>"
140140
+ }
141141
}
142+
diff --git a/contracts/utils/ReentrancyGuard.sol b/contracts/utils/ReentrancyGuard.sol
143+
index f958d5e8f..845209bc8 100644
144+
--- a/contracts/utils/ReentrancyGuard.sol
145+
+++ b/contracts/utils/ReentrancyGuard.sol
146+
@@ -36,6 +36,11 @@ abstract contract ReentrancyGuard {
147+
bytes32 private constant REENTRANCY_GUARD_STORAGE =
148+
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
149+
150+
+ /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
151+
+ struct ReentrancyGuardStorage {
152+
+ uint256 _status;
153+
+ }
154+
+
155+
// Booleans are more expensive than uint256 or any type that takes up a full
156+
// word because each write operation emits an extra SLOAD to first read the
157+
// slot's contents, replace the bits taken up by the boolean, and then write
142158
diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol
143-
index c39954e35..fe681f87a 100644
159+
index 0eaef9d27..01f1b5f58 100644
144160
--- a/contracts/utils/cryptography/EIP712.sol
145161
+++ b/contracts/utils/cryptography/EIP712.sol
146162
@@ -4,7 +4,6 @@
147-
pragma solidity ^0.8.20;
163+
pragma solidity ^0.8.24;
148164

149165
import {MessageHashUtils} from "./MessageHashUtils.sol";
150166
-import {ShortStrings, ShortString} from "../ShortStrings.sol";
@@ -314,10 +330,10 @@ index c39954e35..fe681f87a 100644
314330
}
315331
}
316332
diff --git a/package.json b/package.json
317-
index eeeaf0bcd..65581c544 100644
333+
index 285a074ed..d48cc8e4b 100644
318334
--- a/package.json
319335
+++ b/package.json
320-
@@ -34,7 +34,7 @@
336+
@@ -35,7 +35,7 @@
321337
},
322338
"repository": {
323339
"type": "git",
@@ -335,7 +351,7 @@ index 304d1386a..a1cd63bee 100644
335351
+@openzeppelin/contracts-upgradeable/=contracts/
336352
+@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
337353
diff --git a/test/account/AccountERC7702.test.js b/test/account/AccountERC7702.test.js
338-
index d08a52209..7a44bccfe 100644
354+
index f442d49af..8f22dc926 100644
339355
--- a/test/account/AccountERC7702.test.js
340356
+++ b/test/account/AccountERC7702.test.js
341357
@@ -26,8 +26,8 @@ async function fixture() {
@@ -350,7 +366,7 @@ index d08a52209..7a44bccfe 100644
350366
verifyingContract: mock.address,
351367
};
352368
diff --git a/test/account/examples/AccountERC7702WithModulesMock.test.js b/test/account/examples/AccountERC7702WithModulesMock.test.js
353-
index 9ee5f9177..f6106bcc7 100644
369+
index 8ceab19d1..c3f4194a6 100644
354370
--- a/test/account/examples/AccountERC7702WithModulesMock.test.js
355371
+++ b/test/account/examples/AccountERC7702WithModulesMock.test.js
356372
@@ -36,8 +36,8 @@ async function fixture() {

0 commit comments

Comments
 (0)