Skip to content

Commit 5b77380

Browse files
authored
Merge branch 'master' into perf/reentrancy-guard-precompute-booleanslot
2 parents 9847b3a + efdc7cd commit 5b77380

File tree

66 files changed

+1384
-542
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1384
-542
lines changed

.changeset/funny-donuts-follow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`IERC7751`: Add the interface for custom error wrapping of bubbled up reverts.

.changeset/quick-pianos-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`ReentrancyGuard` and `ReentrancyGuardTransient`: Add `nonReentrantView`, a read-only version of the `nonReentrant` modifier.

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
certora/specs/*.spec linguist-language=Solidity
2+
certora/specs/*.conf linguist-detectable
3+
certora/specs/*.conf linguist-language=JSON5

.github/workflows/formal-verification.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ on:
1212
env:
1313
PIP_VERSION: '3.11'
1414
JAVA_VERSION: '11'
15-
SOLC_VERSION: '0.8.20'
15+
SOLC_VERSION: '0.8.27'
1616

1717
concurrency: ${{ github.workflow }}-${{ github.ref }}
1818

@@ -52,7 +52,7 @@ jobs:
5252
- name: Install python packages
5353
run: pip install -r fv-requirements.txt
5454
- name: Install java
55-
uses: actions/setup-java@v4
55+
uses: actions/setup-java@v5
5656
with:
5757
distribution: temurin
5858
java-version: ${{ env.JAVA_VERSION }}
@@ -64,7 +64,7 @@ jobs:
6464
- name: Verify specification
6565
run: |
6666
make -C certora apply
67-
node certora/run.js ${{ steps.arguments.outputs.result }} >> "$GITHUB_STEP_SUMMARY"
67+
node certora/run.js ${{ steps.arguments.outputs.result }} -p 1 -v >> "$GITHUB_STEP_SUMMARY"
6868
env:
6969
CERTORAKEY: ${{ secrets.CERTORAKEY }}
7070

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
--- access/manager/AccessManager.sol 2023-10-05 12:17:09.694051809 -0300
2-
+++ access/manager/AccessManager.sol 2023-10-05 12:26:18.498688718 -0300
3-
@@ -6,7 +6,6 @@
1+
--- access/manager/AccessManager.sol 2025-08-16 15:15:34
2+
+++ access/manager/AccessManager.sol 2025-08-16 15:17:51
3+
@@ -7,7 +7,6 @@
44
import {IAccessManaged} from "./IAccessManaged.sol";
55
import {Address} from "../../utils/Address.sol";
66
import {Context} from "../../utils/Context.sol";
77
-import {Multicall} from "../../utils/Multicall.sol";
88
import {Math} from "../../utils/math/Math.sol";
99
import {Time} from "../../utils/types/Time.sol";
10-
11-
@@ -57,7 +56,8 @@
12-
* mindful of the danger associated with functions such as {{Ownable-renounceOwnership}} or
13-
* {{AccessControl-renounceRole}}.
10+
import {Hashes} from "../../utils/cryptography/Hashes.sol";
11+
@@ -59,7 +58,8 @@
12+
* mindful of the danger associated with functions such as {Ownable-renounceOwnership} or
13+
* {AccessControl-renounceRole}.
1414
*/
1515
-contract AccessManager is Context, Multicall, IAccessManager {
1616
+// NOTE: The FV version of this contract doesn't include Multicall because CVL HAVOCs on any `delegatecall`.
1717
+contract AccessManager is Context, IAccessManager {
1818
using Time for *;
1919

2020
// Structure that stores the details for a target contract.
21-
@@ -105,7 +105,7 @@
21+
@@ -115,7 +115,7 @@
2222

2323
// Used to identify operations that are currently being executed via {execute}.
2424
// This should be transient storage when supported by the EVM.
2525
- bytes32 private _executionId;
2626
+ bytes32 internal _executionId; // private → internal for FV
2727

2828
/**
29-
* @dev Check that the caller is authorized to perform the operation, following the restrictions encoded in
30-
@@ -253,6 +253,11 @@
29+
* @dev Check that the caller is authorized to perform the operation.
30+
@@ -263,6 +263,11 @@
3131
_setGrantDelay(roleId, newDelay);
3232
}
3333

@@ -39,28 +39,28 @@
3939
/**
4040
* @dev Internal version of {grantRole} without access control. Returns true if the role was newly granted.
4141
*
42-
@@ -287,6 +292,11 @@
43-
return newMember;
44-
}
42+
@@ -295,6 +300,11 @@
4543

44+
emit RoleGranted(roleId, account, executionDelay, since, newMember);
45+
return newMember;
46+
+ }
47+
+
4648
+ // Exposed for FV
4749
+ function _getRoleGrantDelayFull(uint64 roleId) internal view virtual returns (uint32, uint32, uint48) {
4850
+ return _roles[roleId].grantDelay.getFull();
49-
+ }
50-
+
51-
/**
52-
* @dev Internal version of {revokeRole} without access control. This logic is also used by {renounceRole}.
53-
* Returns true if the role was previously granted.
54-
@@ -586,7 +596,7 @@
51+
}
52+
5553
/**
56-
* @dev Check if the current call is authorized according to admin logic.
54+
@@ -596,7 +606,7 @@
55+
*
56+
* WARNING: Carefully review the considerations of {AccessManaged-restricted} since they apply to this modifier.
5757
*/
5858
- function _checkAuthorized() private {
5959
+ function _checkAuthorized() internal virtual { // private → internal virtual for FV
6060
address caller = _msgSender();
6161
(bool immediate, uint32 delay) = _canCallSelf(caller, _msgData());
6262
if (!immediate) {
63-
@@ -609,7 +619,7 @@
63+
@@ -619,7 +629,7 @@
6464
*/
6565
function _getAdminRestrictions(
6666
bytes calldata data
@@ -69,7 +69,7 @@
6969
if (data.length < 4) {
7070
return (false, 0, 0);
7171
}
72-
@@ -662,7 +672,7 @@
72+
@@ -672,7 +682,7 @@
7373
address caller,
7474
address target,
7575
bytes calldata data
@@ -78,7 +78,7 @@
7878
if (target == address(this)) {
7979
return _canCallSelf(caller, data);
8080
} else {
81-
@@ -716,14 +726,14 @@
81+
@@ -728,14 +738,14 @@
8282
/**
8383
* @dev Extracts the selector from calldata. Panics if data is not at least 4 bytes
8484
*/
@@ -92,6 +92,6 @@
9292
*/
9393
- function _hashExecutionId(address target, bytes4 selector) private pure returns (bytes32) {
9494
+ function _hashExecutionId(address target, bytes4 selector) internal pure returns (bytes32) { // private → internal for FV
95-
return keccak256(abi.encode(target, selector));
95+
return Hashes.efficientKeccak256(bytes32(uint256(uint160(target))), selector);
9696
}
9797
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--- account/extensions/draft-AccountERC7579.sol 2025-07-22 11:18:42.944504471 +0200
2+
+++ account/extensions/draft-AccountERC7579.sol 2025-07-22 13:57:03.670277084 +0200
3+
@@ -60,8 +60,8 @@
4+
using EnumerableSet for *;
5+
using Packing for bytes32;
6+
7+
- EnumerableSet.AddressSet private _validators;
8+
- EnumerableSet.AddressSet private _executors;
9+
+ EnumerableSet.AddressSet internal _validators; // private → internal for FV
10+
+ EnumerableSet.AddressSet internal _executors; // private → internal for FV
11+
mapping(bytes4 selector => address) private _fallbacks;
12+
13+
/// @dev The account's {fallback} was called with a selector that doesn't have an installed handler.
14+
@@ -308,8 +308,9 @@
15+
* the ERC-2771 format.
16+
*/
17+
function _fallback() internal virtual returns (bytes memory) {
18+
- address handler = _fallbackHandler(msg.sig);
19+
- require(handler != address(0), ERC7579MissingFallbackHandler(msg.sig));
20+
+ bytes4 selector = bytes4(msg.data[0:4]);
21+
+ address handler = _fallbackHandler(selector);
22+
+ require(handler != address(0), ERC7579MissingFallbackHandler(selector));
23+
24+
// From https://eips.ethereum.org/EIPS/eip-7579#fallback[ERC-7579 specifications]:
25+
// - MUST utilize ERC-2771 to add the original msg.sender to the calldata sent to the fallback handler
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- token/ERC721/ERC721.sol 2025-08-19 17:23:33.436823903 +0200
2+
+++ token/ERC721/ERC721.sol 2025-08-19 17:23:07.328925282 +0200
3+
@@ -27,7 +27,7 @@
4+
5+
mapping(uint256 tokenId => address) private _owners;
6+
7+
- mapping(address owner => uint256) private _balances;
8+
+ mapping(address owner => uint256) internal _balances; // private → internal for FV
9+
10+
mapping(uint256 tokenId => address) private _tokenApprovals;
11+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import {AccountERC7702WithModulesMock} from "../patched/mocks/account/AccountMock.sol";
5+
import {EIP712} from "../patched/utils/cryptography/EIP712.sol";
6+
import {EnumerableSet} from "../patched/utils/structs/EnumerableSet.sol";
7+
8+
contract AccountHarness is AccountERC7702WithModulesMock {
9+
using EnumerableSet for EnumerableSet.AddressSet;
10+
11+
constructor(string memory name, string memory version) EIP712(name, version) {}
12+
13+
function getFallbackHandler(bytes4 selector) external view returns (address) {
14+
return _fallbackHandler(selector);
15+
}
16+
17+
function getDataSelector(bytes memory data) external pure returns (bytes4) {
18+
return bytes4(data);
19+
}
20+
21+
function _validatorContains(address module) external view returns (bool) {
22+
return _validators.contains(module);
23+
}
24+
25+
function _validatorLength() external view returns (uint256) {
26+
return _validators.length();
27+
}
28+
29+
function _validatorAt(uint256 index) external view returns (address) {
30+
return _validators.at(index);
31+
}
32+
33+
function _validatorAtFull(uint256 index) external view returns (bytes32) {
34+
return _validators._inner._values[index];
35+
}
36+
37+
function _validatorPositionOf(address module) external view returns (uint256) {
38+
return _validators._inner._positions[bytes32(uint256(uint160(module)))];
39+
}
40+
41+
function _executorContains(address module) external view returns (bool) {
42+
return _executors.contains(module);
43+
}
44+
45+
function _executorLength() external view returns (uint256) {
46+
return _executors.length();
47+
}
48+
49+
function _executorAt(uint256 index) external view returns (address) {
50+
return _executors.at(index);
51+
}
52+
53+
function _executorAtFull(uint256 index) external view returns (bytes32) {
54+
return _executors._inner._values[index];
55+
}
56+
57+
function _executorPositionOf(address module) external view returns (uint256) {
58+
return _executors._inner._positions[bytes32(uint256(uint160(module)))];
59+
}
60+
}

certora/harnesses/ERC20WrapperHarness.sol

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,6 @@ contract ERC20WrapperHarness is ERC20Permit, ERC20Wrapper {
1212
string memory _symbol
1313
) ERC20(_name, _symbol) ERC20Permit(_name) ERC20Wrapper(_underlying) {}
1414

15-
function underlyingTotalSupply() public view returns (uint256) {
16-
return underlying().totalSupply();
17-
}
18-
19-
function underlyingBalanceOf(address account) public view returns (uint256) {
20-
return underlying().balanceOf(account);
21-
}
22-
23-
function underlyingAllowanceToThis(address account) public view returns (uint256) {
24-
return underlying().allowance(account, address(this));
25-
}
26-
2715
function recover(address account) public returns (uint256) {
2816
return _recover(account);
2917
}

certora/harnesses/ERC721Harness.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ contract ERC721Harness is ERC721 {
2323
_burn(tokenId);
2424
}
2525

26+
function unsafeBalanceOf(address owner) public view returns (uint256) {
27+
return _balances[owner];
28+
}
29+
2630
function unsafeOwnerOf(uint256 tokenId) external view returns (address) {
2731
return _ownerOf(tokenId);
2832
}

0 commit comments

Comments
 (0)