Skip to content

Commit a2d73c0

Browse files
authored
Merge branch 'OpenZeppelin:master' into master
2 parents 881e65c + bf69b60 commit a2d73c0

File tree

88 files changed

+2577
-518
lines changed

Some content is hidden

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

88 files changed

+2577
-518
lines changed

Diff for: .changeset/brave-islands-sparkle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`GovernorSequentialProposalId`: Adds a `Governor` extension that sequentially numbers proposal ids instead of using the hash.

Diff for: .changeset/cyan-taxis-travel.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Address`: bubble up revert data on `sendValue` failed call

Diff for: .changeset/long-walls-draw.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`IERC6909`: Add the interface for ERC-6909.

Diff for: .changeset/proud-planes-arrive.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'openzeppelin-solidity': minor
33
---
44

5-
`Bytes`: Add a library of common operation that operate on `bytes` objects.
5+
`Bytes`: Add a library of common operations that operate on `bytes` objects.

Diff for: .changeset/seven-insects-taste.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': patch
3+
---
4+
5+
`ERC7579Utils`: Add ABI decoding checks on calldata bounds within `decodeBatch`

Diff for: .changeset/ten-fishes-fold.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`IGovernor`: Add the `getProposalId` function to the governor interface.

Diff for: .changeset/thin-eels-cross.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': patch
3+
---
4+
5+
`ERC4626`: Use the `asset` getter in `totalAssets`, `_deposit` and `_withdraw`.

Diff for: .githooks/pre-push

-8
This file was deleted.

Diff for: .github/workflows/checks.yml

+1-5
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
uses: ./.github/actions/setup
9898
- name: Run coverage
9999
run: npm run coverage
100-
- uses: codecov/codecov-action@v4
100+
- uses: codecov/codecov-action@v5
101101
env:
102102
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
103103

@@ -118,11 +118,7 @@ jobs:
118118
- uses: actions/checkout@v4
119119
- name: Set up environment
120120
uses: ./.github/actions/setup
121-
- run: rm foundry.toml
122121
- uses: crytic/[email protected]
123-
with:
124-
node-version: 18.15
125-
slither-version: 0.10.1
126122

127123
codespell:
128124
runs-on: ubuntu-latest

Diff for: .github/workflows/formal-verification.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -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@v3
55+
uses: actions/setup-java@v4
5656
with:
5757
distribution: temurin
5858
java-version: ${{ env.JAVA_VERSION }}

Diff for: .husky/pre-commit

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
npm run test:generation
2+
npx lint-staged

Diff for: GUIDELINES.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ External contributions must be reviewed separately by multiple maintainers.
5555

5656
Automation should be used as much as possible to reduce the possibility of human error and forgetfulness.
5757

58-
Automations that make use of sensitive credentials must use secure secret management, and must be strengthened against attacks such as [those on GitHub Actions worklows](https://github.com/nikitastupin/pwnhub).
58+
Automations that make use of sensitive credentials must use secure secret management, and must be strengthened against attacks such as [those on GitHub Actions workflows](https://github.com/nikitastupin/pwnhub).
5959

6060
Some other examples of automation are:
6161

Diff for: audits/2024-12-v5.2.pdf

237 KB
Binary file not shown.

Diff for: audits/README.md

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# Audits
22

3-
| Date | Version | Commit | Auditor | Scope | Links |
4-
| ------------ | ------- | --------- | ------------ | -------------------- | ----------------------------------------------------------- |
5-
| October 2024 | v5.1.0 | TBD | OpenZeppelin | v5.1 Changes | [🔗](./2024-10-v5.1.pdf) |
6-
| October 2023 | v5.0.0 | `b5a3e69` | OpenZeppelin | v5.0 Changes | [🔗](./2023-10-v5.0.pdf) |
7-
| May 2023 | v4.9.0 | `91df66c` | OpenZeppelin | v4.9 Changes | [🔗](./2023-05-v4.9.pdf) |
8-
| October 2022 | v4.8.0 | `14f98db` | OpenZeppelin | ERC4626, Checkpoints | [🔗](./2022-10-ERC4626.pdf) [🔗](./2022-10-Checkpoints.pdf) |
9-
| October 2018 | v2.0.0 | `dac5bcc` | LevelK | Everything | [🔗](./2018-10.pdf) |
10-
| March 2017 | v1.0.4 | `9c5975a` | New Alchemy | Everything | [🔗](./2017-03.md) |
3+
| Date | Version | Commit | Auditor | Scope | Links |
4+
| ------------- | ------- | -------------------------------------------------------------------------------- | ------------ | -------------------- | ----------------------------------------------------------- |
5+
| December 2024 | v5.2.0 | [`98d28f9`](https://github.com/openzeppelin/openzeppelin-contracts/tree/98d28f9) | OpenZeppelin | v5.2 Changes | [🔗](./2024-12-v5.2.pdf) |
6+
| October 2024 | v5.1.0 | [`aba9ff6`](https://github.com/openzeppelin/openzeppelin-contracts/tree/aba9ff6) | OpenZeppelin | v5.1 Changes | [🔗](./2024-10-v5.1.pdf) |
7+
| October 2023 | v5.0.0 | [`b5a3e69`](https://github.com/openzeppelin/openzeppelin-contracts/tree/b5a3e69) | OpenZeppelin | v5.0 Changes | [🔗](./2023-10-v5.0.pdf) |
8+
| May 2023 | v4.9.0 | [`91df66c`](https://github.com/openzeppelin/openzeppelin-contracts/tree/91df66c) | OpenZeppelin | v4.9 Changes | [🔗](./2023-05-v4.9.pdf) |
9+
| October 2022 | v4.8.0 | [`14f98db`](https://github.com/openzeppelin/openzeppelin-contracts/tree/14f98db) | OpenZeppelin | ERC4626, Checkpoints | [🔗](./2022-10-ERC4626.pdf) [🔗](./2022-10-Checkpoints.pdf) |
10+
| October 2018 | v2.0.0 | [`dac5bcc`](https://github.com/openzeppelin/openzeppelin-contracts/tree/dac5bcc) | LevelK | Everything | [🔗](./2018-10.pdf) |
11+
| March 2017 | v1.0.4 | [`9c5975a`](https://github.com/openzeppelin/openzeppelin-contracts/tree/9c5975a) | New Alchemy | Everything | [🔗](./2017-03.md) |
1112

1213
# Formal Verification
1314

Diff for: contracts/account/utils/draft-ERC4337Utils.sol

+39-20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
pragma solidity ^0.8.20;
44

5-
import {IEntryPoint, PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
5+
import {PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
66
import {Math} from "../../utils/math/Math.sol";
77
import {Packing} from "../../utils/Packing.sol";
88

@@ -24,9 +24,9 @@ library ERC4337Utils {
2424
function parseValidationData(
2525
uint256 validationData
2626
) internal pure returns (address aggregator, uint48 validAfter, uint48 validUntil) {
27-
validAfter = uint48(bytes32(validationData).extract_32_6(0x00));
28-
validUntil = uint48(bytes32(validationData).extract_32_6(0x06));
29-
aggregator = address(bytes32(validationData).extract_32_20(0x0c));
27+
validAfter = uint48(bytes32(validationData).extract_32_6(0));
28+
validUntil = uint48(bytes32(validationData).extract_32_6(6));
29+
aggregator = address(bytes32(validationData).extract_32_20(12));
3030
if (validUntil == 0) validUntil = type(uint48).max;
3131
}
3232

@@ -59,7 +59,8 @@ library ERC4337Utils {
5959
(address aggregator1, uint48 validAfter1, uint48 validUntil1) = parseValidationData(validationData1);
6060
(address aggregator2, uint48 validAfter2, uint48 validUntil2) = parseValidationData(validationData2);
6161

62-
bool success = aggregator1 == address(0) && aggregator2 == address(0);
62+
bool success = aggregator1 == address(uint160(SIG_VALIDATION_SUCCESS)) &&
63+
aggregator2 == address(uint160(SIG_VALIDATION_SUCCESS));
6364
uint48 validAfter = uint48(Math.max(validAfter1, validAfter2));
6465
uint48 validUntil = uint48(Math.min(validUntil1, validUntil2));
6566
return packValidationData(success, validAfter, validUntil);
@@ -71,12 +72,7 @@ library ERC4337Utils {
7172
return (aggregator_, block.timestamp < validAfter || validUntil < block.timestamp);
7273
}
7374

74-
/// @dev Computes the hash of a user operation with the current entrypoint and chainid.
75-
function hash(PackedUserOperation calldata self) internal view returns (bytes32) {
76-
return hash(self, address(this), block.chainid);
77-
}
78-
79-
/// @dev Sames as {hash}, but with a custom entrypoint and chainid.
75+
/// @dev Computes the hash of a user operation for a given entrypoint and chainid.
8076
function hash(
8177
PackedUserOperation calldata self,
8278
address entrypoint,
@@ -103,24 +99,34 @@ library ERC4337Utils {
10399
return result;
104100
}
105101

102+
/// @dev Returns `factory` from the {PackedUserOperation}, or address(0) if the initCode is empty or not properly formatted.
103+
function factory(PackedUserOperation calldata self) internal pure returns (address) {
104+
return self.initCode.length < 20 ? address(0) : address(bytes20(self.initCode[0:20]));
105+
}
106+
107+
/// @dev Returns `factoryData` from the {PackedUserOperation}, or empty bytes if the initCode is empty or not properly formatted.
108+
function factoryData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
109+
return self.initCode.length < 20 ? _emptyCalldataBytes() : self.initCode[20:];
110+
}
111+
106112
/// @dev Returns `verificationGasLimit` from the {PackedUserOperation}.
107113
function verificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
108-
return uint128(self.accountGasLimits.extract_32_16(0x00));
114+
return uint128(self.accountGasLimits.extract_32_16(0));
109115
}
110116

111-
/// @dev Returns `accountGasLimits` from the {PackedUserOperation}.
117+
/// @dev Returns `callGasLimit` from the {PackedUserOperation}.
112118
function callGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
113-
return uint128(self.accountGasLimits.extract_32_16(0x10));
119+
return uint128(self.accountGasLimits.extract_32_16(16));
114120
}
115121

116122
/// @dev Returns the first section of `gasFees` from the {PackedUserOperation}.
117123
function maxPriorityFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
118-
return uint128(self.gasFees.extract_32_16(0x00));
124+
return uint128(self.gasFees.extract_32_16(0));
119125
}
120126

121127
/// @dev Returns the second section of `gasFees` from the {PackedUserOperation}.
122128
function maxFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
123-
return uint128(self.gasFees.extract_32_16(0x10));
129+
return uint128(self.gasFees.extract_32_16(16));
124130
}
125131

126132
/// @dev Returns the total gas price for the {PackedUserOperation} (ie. `maxFeePerGas` or `maxPriorityFeePerGas + basefee`).
@@ -129,22 +135,35 @@ library ERC4337Utils {
129135
// Following values are "per gas"
130136
uint256 maxPriorityFee = maxPriorityFeePerGas(self);
131137
uint256 maxFee = maxFeePerGas(self);
132-
return Math.ternary(maxFee == maxPriorityFee, maxFee, Math.min(maxFee, maxPriorityFee + block.basefee));
138+
return Math.min(maxFee, maxPriorityFee + block.basefee);
133139
}
134140
}
135141

136142
/// @dev Returns the first section of `paymasterAndData` from the {PackedUserOperation}.
137143
function paymaster(PackedUserOperation calldata self) internal pure returns (address) {
138-
return address(bytes20(self.paymasterAndData[0:20]));
144+
return self.paymasterAndData.length < 52 ? address(0) : address(bytes20(self.paymasterAndData[0:20]));
139145
}
140146

141147
/// @dev Returns the second section of `paymasterAndData` from the {PackedUserOperation}.
142148
function paymasterVerificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
143-
return uint128(bytes16(self.paymasterAndData[20:36]));
149+
return self.paymasterAndData.length < 52 ? 0 : uint128(bytes16(self.paymasterAndData[20:36]));
144150
}
145151

146152
/// @dev Returns the third section of `paymasterAndData` from the {PackedUserOperation}.
147153
function paymasterPostOpGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
148-
return uint128(bytes16(self.paymasterAndData[36:52]));
154+
return self.paymasterAndData.length < 52 ? 0 : uint128(bytes16(self.paymasterAndData[36:52]));
155+
}
156+
157+
/// @dev Returns the fourth section of `paymasterAndData` from the {PackedUserOperation}.
158+
function paymasterData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
159+
return self.paymasterAndData.length < 52 ? _emptyCalldataBytes() : self.paymasterAndData[52:];
160+
}
161+
162+
// slither-disable-next-line write-after-write
163+
function _emptyCalldataBytes() private pure returns (bytes calldata result) {
164+
assembly ("memory-safe") {
165+
result.offset := 0
166+
result.length := 0
167+
}
149168
}
150169
}

Diff for: contracts/account/utils/draft-ERC7579Utils.sol

+53-18
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,26 @@ library ERC7579Utils {
2222
using Packing for *;
2323

2424
/// @dev A single `call` execution.
25-
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
25+
CallType internal constant CALLTYPE_SINGLE = CallType.wrap(0x00);
2626

2727
/// @dev A batch of `call` executions.
28-
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
28+
CallType internal constant CALLTYPE_BATCH = CallType.wrap(0x01);
2929

3030
/// @dev A `delegatecall` execution.
31-
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
31+
CallType internal constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
3232

3333
/// @dev Default execution type that reverts on failure.
34-
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
34+
ExecType internal constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
3535

3636
/// @dev Execution type that does not revert on failure.
37-
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
37+
ExecType internal constant EXECTYPE_TRY = ExecType.wrap(0x01);
3838

39-
/// @dev Emits when an {EXECTYPE_TRY} execution fails.
40-
event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes result);
39+
/**
40+
* @dev Emits when an {EXECTYPE_TRY} execution fails.
41+
* @param batchExecutionIndex The index of the failed call in the execution batch.
42+
* @param returndata The returned data from the failed call.
43+
*/
44+
event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes returndata);
4145

4246
/// @dev The provided {CallType} is not supported.
4347
error ERC7579UnsupportedCallType(CallType callType);
@@ -57,10 +61,13 @@ library ERC7579Utils {
5761
/// @dev The module type is not supported.
5862
error ERC7579UnsupportedModuleType(uint256 moduleTypeId);
5963

64+
/// @dev Input calldata not properly formatted and possibly malicious.
65+
error ERC7579DecodingError();
66+
6067
/// @dev Executes a single call.
6168
function execSingle(
62-
ExecType execType,
63-
bytes calldata executionCalldata
69+
bytes calldata executionCalldata,
70+
ExecType execType
6471
) internal returns (bytes[] memory returnData) {
6572
(address target, uint256 value, bytes calldata callData) = decodeSingle(executionCalldata);
6673
returnData = new bytes[](1);
@@ -69,8 +76,8 @@ library ERC7579Utils {
6976

7077
/// @dev Executes a batch of calls.
7178
function execBatch(
72-
ExecType execType,
73-
bytes calldata executionCalldata
79+
bytes calldata executionCalldata,
80+
ExecType execType
7481
) internal returns (bytes[] memory returnData) {
7582
Execution[] calldata executionBatch = decodeBatch(executionCalldata);
7683
returnData = new bytes[](executionBatch.length);
@@ -87,8 +94,8 @@ library ERC7579Utils {
8794

8895
/// @dev Executes a delegate call.
8996
function execDelegateCall(
90-
ExecType execType,
91-
bytes calldata executionCalldata
97+
bytes calldata executionCalldata,
98+
ExecType execType
9299
) internal returns (bytes[] memory returnData) {
93100
(address target, bytes calldata callData) = decodeDelegate(executionCalldata);
94101
returnData = new bytes[](1);
@@ -165,12 +172,40 @@ library ERC7579Utils {
165172
}
166173

167174
/// @dev Decodes a batch of executions. See {encodeBatch}.
175+
///
176+
/// NOTE: This function runs some checks and will throw a {ERC7579DecodingError} if the input is not properly formatted.
168177
function decodeBatch(bytes calldata executionCalldata) internal pure returns (Execution[] calldata executionBatch) {
169-
assembly ("memory-safe") {
170-
let ptr := add(executionCalldata.offset, calldataload(executionCalldata.offset))
171-
// Extract the ERC7579 Executions
172-
executionBatch.offset := add(ptr, 32)
173-
executionBatch.length := calldataload(ptr)
178+
unchecked {
179+
uint256 bufferLength = executionCalldata.length;
180+
181+
// Check executionCalldata is not empty.
182+
if (bufferLength < 32) revert ERC7579DecodingError();
183+
184+
// Get the offset of the array (pointer to the array length).
185+
uint256 arrayLengthOffset = uint256(bytes32(executionCalldata[0:32]));
186+
187+
// The array length (at arrayLengthOffset) should be 32 bytes long. We check that this is within the
188+
// buffer bounds. Since we know bufferLength is at least 32, we can subtract with no overflow risk.
189+
if (arrayLengthOffset > bufferLength - 32) revert ERC7579DecodingError();
190+
191+
// Get the array length. arrayLengthOffset + 32 is bounded by bufferLength so it does not overflow.
192+
uint256 arrayLength = uint256(bytes32(executionCalldata[arrayLengthOffset:arrayLengthOffset + 32]));
193+
194+
// Check that the buffer is long enough to store the array elements as "offset pointer":
195+
// - each element of the array is an "offset pointer" to the data.
196+
// - each "offset pointer" (to an array element) takes 32 bytes.
197+
// - validity of the calldata at that location is checked when the array element is accessed, so we only
198+
// need to check that the buffer is large enough to hold the pointers.
199+
//
200+
// Since we know bufferLength is at least arrayLengthOffset + 32, we can subtract with no overflow risk.
201+
// Solidity limits length of such arrays to 2**64-1, this guarantees `arrayLength * 32` does not overflow.
202+
if (arrayLength > type(uint64).max || bufferLength - arrayLengthOffset - 32 < arrayLength * 32)
203+
revert ERC7579DecodingError();
204+
205+
assembly ("memory-safe") {
206+
executionBatch.offset := add(add(executionCalldata.offset, arrayLengthOffset), 32)
207+
executionBatch.length := arrayLength
208+
}
174209
}
175210
}
176211

Diff for: contracts/finance/VestingWallet.sol

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ import {Ownable} from "../access/Ownable.sol";
2626
*
2727
* NOTE: When using this contract with any token whose balance is adjusted automatically (i.e. a rebase token), make
2828
* sure to account the supply/balance adjustment in the vesting schedule to ensure the vested amount is as intended.
29+
*
30+
* NOTE: Chains with support for native ERC20s may allow the vesting wallet to withdraw the underlying asset as both an
31+
* ERC20 and as native currency. For example, if chain C supports token A and the wallet gets deposited 100 A, then
32+
* at 50% of the vesting period, the beneficiary can withdraw 50 A as ERC20 and 25 A as native currency (totaling 75 A).
33+
* Consider disabling one of the withdrawal methods.
2934
*/
3035
contract VestingWallet is Context, Ownable {
3136
event EtherReleased(uint256 amount);

0 commit comments

Comments
 (0)