Skip to content

Commit 8876971

Browse files
Amxxernestognwarr00voronorStackOverflowExcept1on
authored
Release v5.3 cherrypick #2 (#5526)
Signed-off-by: Hadrien Croubois <[email protected]> Co-authored-by: Ernesto García <[email protected]> Co-authored-by: Arr00 <[email protected]> Co-authored-by: Voronor <[email protected]> Co-authored-by: StackOverflowExcept1on <[email protected]> Co-authored-by: Michalis Kargakis <[email protected]>
1 parent 77a459e commit 8876971

34 files changed

+1641
-262
lines changed

.changeset/blue-nails-give.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Math`: Add `add512`, `mul512` and `mulShr`.

.changeset/fair-pumpkins-compete.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Math`: Add saturating arithmetic operations `saturatingAdd`, `saturatingSub` and `saturatingMul`.

.changeset/fast-coats-try.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Initializable`: Add `_initializableStorageSlot` function that returns a pointer to the storage struct. The function allows customizing with a custom storage slot with an `override`.

.changeset/fuzzy-crews-poke.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`GovernorSuperQuorum`: Add a governance extension to support a super quorum. Proposals that meet the super quorum (and have a majority of for votes) advance to the `Succeeded` state before the proposal deadline.

.changeset/good-zebras-ring.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`MerkleTree`: Add an update function that replaces a previously inserted leaf with a new value, updating the tree root along the way.

.changeset/nice-cherries-reply.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Strings`: Add `espaceJSON` that escapes special characters in JSON strings.

.changeset/ninety-rings-suffer.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`GovernorVotesSuperQuorumFraction`: Add a variant of the `GovernorSuperQuorum` extensions where the super quorum is expressed as a fraction of the total supply.

.changeset/quiet-shrimps-kiss.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openzeppelin-solidity": patch
3+
---
4+
5+
`MessageHashUtils`: Add `toDataWithIntendedValidatorHash(address, bytes32)`.

contracts/governance/Governor.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import {IGovernor, IERC6372} from "./IGovernor.sol";
2121
*
2222
* This contract is abstract and requires several functions to be implemented in various modules:
2323
*
24-
* - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
24+
* - A counting module must implement {_quorumReached}, {_voteSucceeded} and {_countVote}
2525
* - A voting module must implement {_getVotes}
26-
* - Additionally, {votingPeriod} must also be implemented
26+
* - Additionally, {votingPeriod}, {votingDelay}, and {quorum} must also be implemented
2727
*/
2828
abstract contract Governor is Context, ERC165, EIP712, Nonces, IGovernor, IERC721Receiver, IERC1155Receiver {
2929
using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque;

contracts/governance/README.adoc

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Votes modules determine the source of voting power, and sometimes quorum number.
2424

2525
* {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply.
2626

27+
* {GovernorVotesSuperQuorumFraction}: Combines `GovernorSuperQuorum` with `GovernorVotesQuorumFraction` to set the super quorum as a fraction of the total token supply.
28+
2729
Counting modules determine valid voting options.
2830

2931
* {GovernorCountingSimple}: Simple voting mechanism with 3 voting options: Against, For and Abstain.
@@ -50,6 +52,8 @@ Other extensions can customize the behavior or interface in multiple ways.
5052

5153
* {GovernorProposalGuardian}: Adds a proposal guardian that can cancel proposals at any stage in their lifecycle--this permission is passed on to the proposers if the guardian is not set.
5254

55+
* {GovernorSuperQuorum}: Extension of {Governor} with a super quorum. Proposals that meet the super quorum (and have a majority of for votes) advance to the `Succeeded` state before the proposal deadline.
56+
5357
In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications:
5458

5559
* <<Governor-votingDelay-,`votingDelay()`>>: Delay (in ERC-6372 clock) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes.
@@ -76,6 +80,8 @@ NOTE: Functions of the `Governor` contract do not include access control. If you
7680

7781
{{GovernorVotesQuorumFraction}}
7882

83+
{{GovernorVotesSuperQuorumFraction}}
84+
7985
=== Extensions
8086

8187
{{GovernorTimelockAccess}}
@@ -92,6 +98,8 @@ NOTE: Functions of the `Governor` contract do not include access control. If you
9298

9399
{{GovernorProposalGuardian}}
94100

101+
{{GovernorSuperQuorum}}
102+
95103
== Utils
96104

97105
{{Votes}}

contracts/governance/extensions/GovernorCountingFractional.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {Math} from "../../utils/math/Math.sol";
2727
* * Voting from an L2 with tokens held by a bridge
2828
* * Voting privately from a shielded pool using zero knowledge proofs.
2929
*
30-
* Based on ScopeLift's GovernorCountingFractional[https://github.com/ScopeLift/flexible-voting/blob/e5de2efd1368387b840931f19f3c184c85842761/src/GovernorCountingFractional.sol]
30+
* Based on ScopeLift's https://github.com/ScopeLift/flexible-voting/blob/e5de2efd1368387b840931f19f3c184c85842761/src/GovernorCountingFractional.sol[`GovernorCountingFractional`]
3131
*
3232
* _Available since v5.1._
3333
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import {Governor} from "../Governor.sol";
5+
import {SafeCast} from "../../utils/math/SafeCast.sol";
6+
import {Checkpoints} from "../../utils/structs/Checkpoints.sol";
7+
8+
/**
9+
* @dev Extension of {Governor} with a super quorum. Proposals that meet the super quorum (and have a majority of for
10+
* votes) advance to the `Succeeded` state before the proposal deadline. Counting modules that want to use this
11+
* extension must implement {proposalVotes}.
12+
*/
13+
abstract contract GovernorSuperQuorum is Governor {
14+
/**
15+
* @dev Minimum number of cast votes required for a proposal to reach super quorum. Only FOR votes are counted
16+
* towards the super quorum. Once the super quorum is reached, an active proposal can proceed to the next state
17+
* without waiting for the proposal deadline.
18+
*
19+
* NOTE: The `timepoint` parameter corresponds to the snapshot used for counting the vote. This enables scaling of the
20+
* quorum depending on values such as the `totalSupply` of a token at this timepoint (see {ERC20Votes}).
21+
*
22+
* NOTE: Make sure the value specified for the super quorum is greater than {quorum}, otherwise, it may be
23+
* possible to pass a proposal with less votes than the default quorum.
24+
*/
25+
function superQuorum(uint256 timepoint) public view virtual returns (uint256);
26+
27+
/**
28+
* @dev Accessor to the internal vote counts. This must be implemented by the counting module. Counting modules
29+
* that don't implement this function are incompatible with this module
30+
*/
31+
function proposalVotes(
32+
uint256 proposalId
33+
) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes);
34+
35+
/**
36+
* @dev Overridden version of the {Governor-state} function that checks if the proposal has reached the super
37+
* quorum.
38+
*
39+
* NOTE: If the proposal reaches super quorum but {_voteSucceeded} returns false, eg, assuming the super quorum
40+
* has been set low enough that both FOR and AGAINST votes have exceeded it and AGAINST votes exceed FOR votes,
41+
* the proposal continues to be active until {_voteSucceeded} returns true or the proposal deadline is reached.
42+
* This means that with a low super quorum it is also possible that a vote can succeed prematurely before enough
43+
* AGAINST voters have a chance to vote. Hence, it is recommended to set a high enough super quorum to avoid these
44+
* types of scenarios.
45+
*/
46+
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
47+
ProposalState currentState = super.state(proposalId);
48+
if (currentState != ProposalState.Active) return currentState;
49+
50+
(, uint256 forVotes, ) = proposalVotes(proposalId);
51+
if (forVotes < superQuorum(proposalSnapshot(proposalId)) || !_voteSucceeded(proposalId)) {
52+
return ProposalState.Active;
53+
} else if (proposalEta(proposalId) == 0) {
54+
return ProposalState.Succeeded;
55+
} else {
56+
return ProposalState.Queued;
57+
}
58+
}
59+
}

contracts/governance/extensions/GovernorVotesQuorumFraction.sol

+16-13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
pragma solidity ^0.8.20;
55

66
import {GovernorVotes} from "./GovernorVotes.sol";
7+
import {Math} from "../../utils/math/Math.sol";
78
import {SafeCast} from "../../utils/math/SafeCast.sol";
89
import {Checkpoints} from "../../utils/structs/Checkpoints.sol";
910

@@ -45,18 +46,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
4546
* @dev Returns the quorum numerator at a specific timepoint. See {quorumDenominator}.
4647
*/
4748
function quorumNumerator(uint256 timepoint) public view virtual returns (uint256) {
48-
uint256 length = _quorumNumeratorHistory._checkpoints.length;
49-
50-
// Optimistic search, check the latest checkpoint
51-
Checkpoints.Checkpoint208 storage latest = _quorumNumeratorHistory._checkpoints[length - 1];
52-
uint48 latestKey = latest._key;
53-
uint208 latestValue = latest._value;
54-
if (latestKey <= timepoint) {
55-
return latestValue;
56-
}
57-
58-
// Otherwise, do the binary search
59-
return _quorumNumeratorHistory.upperLookupRecent(SafeCast.toUint48(timepoint));
49+
return _optimisticUpperLookupRecent(_quorumNumeratorHistory, timepoint);
6050
}
6151

6252
/**
@@ -70,7 +60,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
7060
* @dev Returns the quorum for a timepoint, in terms of number of votes: `supply * numerator / denominator`.
7161
*/
7262
function quorum(uint256 timepoint) public view virtual override returns (uint256) {
73-
return (token().getPastTotalSupply(timepoint) * quorumNumerator(timepoint)) / quorumDenominator();
63+
return Math.mulDiv(token().getPastTotalSupply(timepoint), quorumNumerator(timepoint), quorumDenominator());
7464
}
7565

7666
/**
@@ -107,4 +97,17 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
10797

10898
emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator);
10999
}
100+
101+
/**
102+
* @dev Returns the numerator at a specific timepoint.
103+
*/
104+
function _optimisticUpperLookupRecent(
105+
Checkpoints.Trace208 storage ckpts,
106+
uint256 timepoint
107+
) internal view returns (uint256) {
108+
// If trace is empty, key and value are both equal to 0.
109+
// In that case `key <= timepoint` is true, and it is ok to return 0.
110+
(, uint48 key, uint208 value) = ckpts.latestCheckpoint();
111+
return key <= timepoint ? value : ckpts.upperLookupRecent(SafeCast.toUint48(timepoint));
112+
}
110113
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import {Governor} from "../Governor.sol";
5+
import {GovernorSuperQuorum} from "./GovernorSuperQuorum.sol";
6+
import {GovernorVotesQuorumFraction} from "./GovernorVotesQuorumFraction.sol";
7+
import {Math} from "../../utils/math/Math.sol";
8+
import {SafeCast} from "../../utils/math/SafeCast.sol";
9+
import {Checkpoints} from "../../utils/structs/Checkpoints.sol";
10+
11+
/**
12+
* @dev Extension of {GovernorVotesQuorumFraction} with a super quorum expressed as a
13+
* fraction of the total supply. Proposals that meet the super quorum (and have a majority of for votes) advance to
14+
* the `Succeeded` state before the proposal deadline.
15+
*/
16+
abstract contract GovernorVotesSuperQuorumFraction is GovernorVotesQuorumFraction, GovernorSuperQuorum {
17+
using Checkpoints for Checkpoints.Trace208;
18+
19+
Checkpoints.Trace208 private _superQuorumNumeratorHistory;
20+
21+
event SuperQuorumNumeratorUpdated(uint256 oldSuperQuorumNumerator, uint256 newSuperQuorumNumerator);
22+
23+
/**
24+
* @dev The super quorum set is not valid as it exceeds the quorum denominator.
25+
*/
26+
error GovernorInvalidSuperQuorumFraction(uint256 superQuorumNumerator, uint256 denominator);
27+
28+
/**
29+
* @dev The super quorum set is not valid as it is smaller or equal to the quorum.
30+
*/
31+
error GovernorInvalidSuperQuorumTooSmall(uint256 superQuorumNumerator, uint256 quorumNumerator);
32+
33+
/**
34+
* @dev The quorum set is not valid as it exceeds the super quorum.
35+
*/
36+
error GovernorInvalidQuorumTooLarge(uint256 quorumNumerator, uint256 superQuorumNumerator);
37+
38+
/**
39+
* @dev Initialize super quorum as a fraction of the token's total supply.
40+
*
41+
* The super quorum is specified as a fraction of the token's total supply and has to
42+
* be greater than the quorum.
43+
*/
44+
constructor(uint256 superQuorumNumeratorValue) {
45+
_updateSuperQuorumNumerator(superQuorumNumeratorValue);
46+
}
47+
48+
/**
49+
* @dev Returns the current super quorum numerator.
50+
*/
51+
function superQuorumNumerator() public view virtual returns (uint256) {
52+
return _superQuorumNumeratorHistory.latest();
53+
}
54+
55+
/**
56+
* @dev Returns the super quorum numerator at a specific `timepoint`.
57+
*/
58+
function superQuorumNumerator(uint256 timepoint) public view virtual returns (uint256) {
59+
return _optimisticUpperLookupRecent(_superQuorumNumeratorHistory, timepoint);
60+
}
61+
62+
/**
63+
* @dev Returns the super quorum for a `timepoint`, in terms of number of votes: `supply * numerator / denominator`.
64+
*/
65+
function superQuorum(uint256 timepoint) public view virtual override returns (uint256) {
66+
return Math.mulDiv(token().getPastTotalSupply(timepoint), superQuorumNumerator(timepoint), quorumDenominator());
67+
}
68+
69+
/**
70+
* @dev Changes the super quorum numerator.
71+
*
72+
* Emits a {SuperQuorumNumeratorUpdated} event.
73+
*
74+
* Requirements:
75+
*
76+
* - Must be called through a governance proposal.
77+
* - New super quorum numerator must be smaller or equal to the denominator.
78+
* - New super quorum numerator must be greater than or equal to the quorum numerator.
79+
*/
80+
function updateSuperQuorumNumerator(uint256 newSuperQuorumNumerator) public virtual onlyGovernance {
81+
_updateSuperQuorumNumerator(newSuperQuorumNumerator);
82+
}
83+
84+
/**
85+
* @dev Changes the super quorum numerator.
86+
*
87+
* Emits a {SuperQuorumNumeratorUpdated} event.
88+
*
89+
* Requirements:
90+
*
91+
* - New super quorum numerator must be smaller or equal to the denominator.
92+
* - New super quorum numerator must be greater than or equal to the quorum numerator.
93+
*/
94+
function _updateSuperQuorumNumerator(uint256 newSuperQuorumNumerator) internal virtual {
95+
uint256 denominator = quorumDenominator();
96+
if (newSuperQuorumNumerator > denominator) {
97+
revert GovernorInvalidSuperQuorumFraction(newSuperQuorumNumerator, denominator);
98+
}
99+
100+
uint256 quorumNumerator = quorumNumerator();
101+
if (newSuperQuorumNumerator < quorumNumerator) {
102+
revert GovernorInvalidSuperQuorumTooSmall(newSuperQuorumNumerator, quorumNumerator);
103+
}
104+
105+
uint256 oldSuperQuorumNumerator = _superQuorumNumeratorHistory.latest();
106+
_superQuorumNumeratorHistory.push(clock(), SafeCast.toUint208(newSuperQuorumNumerator));
107+
108+
emit SuperQuorumNumeratorUpdated(oldSuperQuorumNumerator, newSuperQuorumNumerator);
109+
}
110+
111+
/**
112+
* @dev Overrides {GovernorVotesQuorumFraction-_updateQuorumNumerator} to ensure the super
113+
* quorum numerator is greater than or equal to the quorum numerator.
114+
*/
115+
function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual override {
116+
// Ignoring check when the superQuorum was never set (construction sets quorum before superQuorum)
117+
if (_superQuorumNumeratorHistory.length() > 0) {
118+
uint256 superQuorumNumerator_ = superQuorumNumerator();
119+
if (newQuorumNumerator > superQuorumNumerator_) {
120+
revert GovernorInvalidQuorumTooLarge(newQuorumNumerator, superQuorumNumerator_);
121+
}
122+
}
123+
super._updateQuorumNumerator(newQuorumNumerator);
124+
}
125+
126+
/// @inheritdoc GovernorSuperQuorum
127+
function state(
128+
uint256 proposalId
129+
) public view virtual override(Governor, GovernorSuperQuorum) returns (ProposalState) {
130+
return super.state(proposalId);
131+
}
132+
}

contracts/mocks/MerkleTreeMock.sol

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ contract MerkleTreeMock {
1414
bytes32 public root;
1515

1616
event LeafInserted(bytes32 leaf, uint256 index, bytes32 root);
17+
event LeafUpdated(bytes32 oldLeaf, bytes32 newLeaf, uint256 index, bytes32 root);
1718

1819
function setup(uint8 _depth, bytes32 _zero) public {
1920
root = _tree.setup(_depth, _zero);
@@ -25,6 +26,13 @@ contract MerkleTreeMock {
2526
root = currentRoot;
2627
}
2728

29+
function update(uint256 index, bytes32 oldValue, bytes32 newValue, bytes32[] memory proof) public {
30+
(bytes32 oldRoot, bytes32 newRoot) = _tree.update(index, oldValue, newValue, proof);
31+
if (oldRoot != root) revert MerkleTree.MerkleTreeUpdateInvalidProof();
32+
emit LeafUpdated(oldValue, newValue, index, newRoot);
33+
root = newRoot;
34+
}
35+
2836
function depth() public view returns (uint256) {
2937
return _tree.depth();
3038
}

0 commit comments

Comments
 (0)