From d18634adfc8cf0a810dadd7eebbd6df8a26d489b Mon Sep 17 00:00:00 2001 From: borj404 Date: Tue, 19 Aug 2025 18:56:36 +0200 Subject: [PATCH 1/2] perf: optimize ReentrancyGuardTransient with pre-computed BooleanSlot --- contracts/utils/ReentrancyGuardTransient.sol | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/contracts/utils/ReentrancyGuardTransient.sol b/contracts/utils/ReentrancyGuardTransient.sol index a1318c86f3c..20f044d0443 100644 --- a/contracts/utils/ReentrancyGuardTransient.sol +++ b/contracts/utils/ReentrancyGuardTransient.sol @@ -19,6 +19,17 @@ abstract contract ReentrancyGuardTransient { bytes32 private constant REENTRANCY_GUARD_STORAGE = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + /** + * @dev Pre-computed BooleanSlot type wrapper for the reentrancy guard storage slot. + * This optimization avoids the overhead of repeatedly calling asBoolean() which performs + * a bytes32.wrap() operation on each invocation. By pre-computing the wrapped type at compile-time, + * this eliminates redundant type casting operations in _nonReentrantBefore(), _nonReentrantAfter(), + * and _reentrancyGuardEntered(), reducing gas consumption by 10 gas per call + * to asBoolean() (30 gas total per nonReentrant modifier execution). + */ + TransientSlot.BooleanSlot private constant REENTRANCY_SLOT = + TransientSlot.BooleanSlot.wrap(REENTRANCY_GUARD_STORAGE); + /** * @dev Unauthorized reentrant call. */ @@ -38,17 +49,17 @@ abstract contract ReentrancyGuardTransient { } function _nonReentrantBefore() private { - // On the first call to nonReentrant, REENTRANCY_GUARD_STORAGE.asBoolean().tload() will be false + // On the first call to nonReentrant, REENTRANCY_SLOT.tload() will be false if (_reentrancyGuardEntered()) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail - REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true); + REENTRANCY_SLOT.tstore(true); } function _nonReentrantAfter() private { - REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false); + REENTRANCY_SLOT.tstore(false); } /** @@ -56,6 +67,6 @@ abstract contract ReentrancyGuardTransient { * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { - return REENTRANCY_GUARD_STORAGE.asBoolean().tload(); + return REENTRANCY_SLOT.tload(); } } From c07b8d2c1990eb019a3049462e1d839fcdf03bed Mon Sep 17 00:00:00 2001 From: borj404 Date: Tue, 19 Aug 2025 19:36:15 +0200 Subject: [PATCH 2/2] chore: add changeset --- .changeset/thick-goats-travel.md | 5 ++++ contracts/utils/ReentrancyGuardTransient.sol | 26 ++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 .changeset/thick-goats-travel.md diff --git a/.changeset/thick-goats-travel.md b/.changeset/thick-goats-travel.md new file mode 100644 index 00000000000..c06f8b5322a --- /dev/null +++ b/.changeset/thick-goats-travel.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': patch +--- + +Optimize ReentrancyGuardTransient by pre-computing BooleanSlot wrapper to save 30 gas per call diff --git a/contracts/utils/ReentrancyGuardTransient.sol b/contracts/utils/ReentrancyGuardTransient.sol index 20f044d0443..4da45ed9de5 100644 --- a/contracts/utils/ReentrancyGuardTransient.sol +++ b/contracts/utils/ReentrancyGuardTransient.sol @@ -23,9 +23,8 @@ abstract contract ReentrancyGuardTransient { * @dev Pre-computed BooleanSlot type wrapper for the reentrancy guard storage slot. * This optimization avoids the overhead of repeatedly calling asBoolean() which performs * a bytes32.wrap() operation on each invocation. By pre-computing the wrapped type at compile-time, - * this eliminates redundant type casting operations in _nonReentrantBefore(), _nonReentrantAfter(), - * and _reentrancyGuardEntered(), reducing gas consumption by 10 gas per call - * to asBoolean() (30 gas total per nonReentrant modifier execution). + * this eliminates 3 redundant type casting operations per nonReentrant modifier execution, + * reducing gas consumption by 19 gas. */ TransientSlot.BooleanSlot private constant REENTRANCY_SLOT = TransientSlot.BooleanSlot.wrap(REENTRANCY_GUARD_STORAGE); @@ -48,11 +47,28 @@ abstract contract ReentrancyGuardTransient { _nonReentrantAfter(); } - function _nonReentrantBefore() private { - // On the first call to nonReentrant, REENTRANCY_SLOT.tload() will be false + /** + * @dev A `view` only version of {nonReentrant}. Use to block view functions + * from being called, preventing reading from inconsistent contract state. + * + * CAUTION: This is a "view" modifier and does not change the reentrancy + * status. Use it only on view functions. For payable or non-payable functions, + * use the standard {nonReentrant} modifier instead. + */ + modifier nonReentrantView() { + _nonReentrantBeforeView(); + _; + } + + function _nonReentrantBeforeView() private view { if (_reentrancyGuardEntered()) { revert ReentrancyGuardReentrantCall(); } + } + + function _nonReentrantBefore() private { + // On the first call to nonReentrant, REENTRANCY_SLOT.tload() will be false + _nonReentrantBeforeView(); // Any calls to nonReentrant after this point will fail REENTRANCY_SLOT.tstore(true);