Skip to content

Commit 6c1a634

Browse files
Amxxfrangio
andauthored
Add Governor contracts (#2672)
Co-authored-by: Francisco Giordano <[email protected]>
1 parent f88e555 commit 6c1a634

39 files changed

+5302
-377
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* `ERC2771Context`: use private variable from storage to store the forwarder address. Fixes issues where `_msgSender()` was not callable from constructors. ([#2754](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2754))
66
* `EnumerableSet`: add `values()` functions that returns an array containing all values in a single call. ([#2768](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2768))
7+
* `Governor`: added a modular system of `Governor` contracts based on `GovernorAlpha` and `GovernorBravo`. ([#2672](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2672))
78

89
## 4.2.0 (2021-06-30)
910

@@ -18,7 +19,7 @@
1819
* `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593))
1920
* `BitMaps`: add a new `BitMaps` library that provides a storage efficient datastructure for `uint256` to `bool` mapping with contiguous keys. ([#2710](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2710))
2021

21-
### Breaking Changes
22+
### Breaking Changes
2223

2324
* `ERC20FlashMint` is no longer a Draft ERC. ([#2673](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2673)))
2425

contracts/governance/Governor.sol

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "../utils/cryptography/ECDSA.sol";
6+
import "../utils/cryptography/draft-EIP712.sol";
7+
import "../utils/introspection/ERC165.sol";
8+
import "../utils/math/SafeCast.sol";
9+
import "../utils/Address.sol";
10+
import "../utils/Context.sol";
11+
import "../utils/Timers.sol";
12+
import "./IGovernor.sol";
13+
14+
/**
15+
* @dev Core of the governance system, designed to be extended though various modules.
16+
*
17+
* This contract is abstract and requiers several function to be implemented in various modules:
18+
*
19+
* - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
20+
* - A voting module must implement {getVotes}
21+
* - Additionanly, the {votingPeriod} must also be implemented
22+
*
23+
* _Available since v4.3._
24+
*/
25+
abstract contract Governor is Context, ERC165, EIP712, IGovernor {
26+
using SafeCast for uint256;
27+
using Timers for Timers.BlockNumber;
28+
29+
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
30+
31+
struct ProposalCore {
32+
Timers.BlockNumber voteStart;
33+
Timers.BlockNumber voteEnd;
34+
bool executed;
35+
bool canceled;
36+
}
37+
38+
string private _name;
39+
40+
mapping(uint256 => ProposalCore) private _proposals;
41+
42+
/**
43+
* @dev Restrict access to governor executing address. Some module might override the _executor function to make
44+
* sure this modifier is consistant with the execution model.
45+
*/
46+
modifier onlyGovernance() {
47+
require(_msgSender() == _executor(), "Governor: onlyGovernance");
48+
_;
49+
}
50+
51+
/**
52+
* @dev Sets the value for {name} and {version}
53+
*/
54+
constructor(string memory name_) EIP712(name_, version()) {
55+
_name = name_;
56+
}
57+
58+
/**
59+
* @dev See {IERC165-supportsInterface}.
60+
*/
61+
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
62+
return interfaceId == type(IGovernor).interfaceId || super.supportsInterface(interfaceId);
63+
}
64+
65+
/**
66+
* @dev See {IGovernor-name}.
67+
*/
68+
function name() public view virtual override returns (string memory) {
69+
return _name;
70+
}
71+
72+
/**
73+
* @dev See {IGovernor-version}.
74+
*/
75+
function version() public view virtual override returns (string memory) {
76+
return "1";
77+
}
78+
79+
/**
80+
* @dev See {IGovernor-hashProposal}.
81+
*
82+
* The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array
83+
* and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id
84+
* can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in
85+
* advance, before the proposal is submitted.
86+
*
87+
* Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the
88+
* same proposal (with same operation and same description) will have the same id if submitted on multiple governors
89+
* accross multiple networks. This also means that in order to execute the same operation twice (on the same
90+
* governor) the proposer will have to change the description in order to avoid proposal id conflicts.
91+
*/
92+
function hashProposal(
93+
address[] memory targets,
94+
uint256[] memory values,
95+
bytes[] memory calldatas,
96+
bytes32 descriptionHash
97+
) public pure virtual override returns (uint256) {
98+
return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
99+
}
100+
101+
/**
102+
* @dev See {IGovernor-state}.
103+
*/
104+
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
105+
ProposalCore memory proposal = _proposals[proposalId];
106+
107+
if (proposal.executed) {
108+
return ProposalState.Executed;
109+
} else if (proposal.canceled) {
110+
return ProposalState.Canceled;
111+
} else if (proposal.voteStart.isPending()) {
112+
return ProposalState.Pending;
113+
} else if (proposal.voteEnd.isPending()) {
114+
return ProposalState.Active;
115+
} else if (proposal.voteEnd.isExpired()) {
116+
return
117+
_quorumReached(proposalId) && _voteSucceeded(proposalId)
118+
? ProposalState.Succeeded
119+
: ProposalState.Defeated;
120+
} else {
121+
revert("Governor: unknown proposal id");
122+
}
123+
}
124+
125+
/**
126+
* @dev See {IGovernor-proposalSnapshot}.
127+
*/
128+
function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) {
129+
return _proposals[proposalId].voteStart.getDeadline();
130+
}
131+
132+
/**
133+
* @dev See {IGovernor-proposalDeadline}.
134+
*/
135+
function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
136+
return _proposals[proposalId].voteEnd.getDeadline();
137+
}
138+
139+
/**
140+
* @dev See {IGovernor-votingDelay}
141+
*/
142+
function votingDelay() public view virtual override returns (uint256);
143+
144+
/**
145+
* @dev See {IGovernor-votingPeriod}
146+
*/
147+
function votingPeriod() public view virtual override returns (uint256);
148+
149+
/**
150+
* @dev See {IGovernor-quorum}
151+
*/
152+
function quorum(uint256 blockNumber) public view virtual override returns (uint256);
153+
154+
/**
155+
* @dev See {IGovernor-getVotes}
156+
*/
157+
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256);
158+
159+
/**
160+
* @dev Amount of votes already casted passes the threshold limit.
161+
*/
162+
function _quorumReached(uint256 proposalId) internal view virtual returns (bool);
163+
164+
/**
165+
* @dev Is the proposal successful or not.
166+
*/
167+
function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool);
168+
169+
/**
170+
* @dev Register a vote with a given support and voting weight.
171+
*
172+
* Note: Support is generic and can represent various things depending on the voting system used.
173+
*/
174+
function _countVote(
175+
uint256 proposalId,
176+
address account,
177+
uint8 support,
178+
uint256 weight
179+
) internal virtual;
180+
181+
/**
182+
* @dev See {IGovernor-propose}.
183+
*/
184+
function propose(
185+
address[] memory targets,
186+
uint256[] memory values,
187+
bytes[] memory calldatas,
188+
string memory description
189+
) public virtual override returns (uint256) {
190+
uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
191+
192+
require(targets.length == values.length, "Governor: invalid proposal length");
193+
require(targets.length == calldatas.length, "Governor: invalid proposal length");
194+
require(targets.length > 0, "Governor: empty proposal");
195+
196+
ProposalCore storage proposal = _proposals[proposalId];
197+
require(proposal.voteStart.isUnset(), "Governor: proposal already exists");
198+
199+
uint64 snapshot = block.number.toUint64() + votingDelay().toUint64();
200+
uint64 deadline = snapshot + votingPeriod().toUint64();
201+
202+
proposal.voteStart.setDeadline(snapshot);
203+
proposal.voteEnd.setDeadline(deadline);
204+
205+
emit ProposalCreated(
206+
proposalId,
207+
_msgSender(),
208+
targets,
209+
values,
210+
new string[](targets.length),
211+
calldatas,
212+
snapshot,
213+
deadline,
214+
description
215+
);
216+
217+
return proposalId;
218+
}
219+
220+
/**
221+
* @dev See {IGovernor-execute}.
222+
*/
223+
function execute(
224+
address[] memory targets,
225+
uint256[] memory values,
226+
bytes[] memory calldatas,
227+
bytes32 descriptionHash
228+
) public payable virtual override returns (uint256) {
229+
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
230+
231+
ProposalState status = state(proposalId);
232+
require(
233+
status == ProposalState.Succeeded || status == ProposalState.Queued,
234+
"Governor: proposal not successful"
235+
);
236+
_proposals[proposalId].executed = true;
237+
238+
emit ProposalExecuted(proposalId);
239+
240+
_execute(proposalId, targets, values, calldatas, descriptionHash);
241+
242+
return proposalId;
243+
}
244+
245+
/**
246+
* @dev Internal execution mechanism. Can be overriden to implement different execution mechanism
247+
*/
248+
function _execute(
249+
uint256, /* proposalId */
250+
address[] memory targets,
251+
uint256[] memory values,
252+
bytes[] memory calldatas,
253+
bytes32 /*descriptionHash*/
254+
) internal virtual {
255+
string memory errorMessage = "Governor: call reverted without message";
256+
for (uint256 i = 0; i < targets.length; ++i) {
257+
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
258+
Address.verifyCallResult(success, returndata, errorMessage);
259+
}
260+
}
261+
262+
/**
263+
* @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as
264+
* canceled to allow distinguishing it from executed proposals.
265+
*
266+
* Emits a {IGovernor-ProposalCanceled} event.
267+
*/
268+
function _cancel(
269+
address[] memory targets,
270+
uint256[] memory values,
271+
bytes[] memory calldatas,
272+
bytes32 descriptionHash
273+
) internal virtual returns (uint256) {
274+
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
275+
ProposalState status = state(proposalId);
276+
277+
require(
278+
status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed,
279+
"Governor: proposal not active"
280+
);
281+
_proposals[proposalId].canceled = true;
282+
283+
emit ProposalCanceled(proposalId);
284+
285+
return proposalId;
286+
}
287+
288+
/**
289+
* @dev See {IGovernor-castVote}.
290+
*/
291+
function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) {
292+
address voter = _msgSender();
293+
return _castVote(proposalId, voter, support, "");
294+
}
295+
296+
/**
297+
* @dev See {IGovernor-castVoteWithReason}.
298+
*/
299+
function castVoteWithReason(
300+
uint256 proposalId,
301+
uint8 support,
302+
string calldata reason
303+
) public virtual override returns (uint256) {
304+
address voter = _msgSender();
305+
return _castVote(proposalId, voter, support, reason);
306+
}
307+
308+
/**
309+
* @dev See {IGovernor-castVoteBySig}.
310+
*/
311+
function castVoteBySig(
312+
uint256 proposalId,
313+
uint8 support,
314+
uint8 v,
315+
bytes32 r,
316+
bytes32 s
317+
) public virtual override returns (uint256) {
318+
address voter = ECDSA.recover(
319+
_hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))),
320+
v,
321+
r,
322+
s
323+
);
324+
return _castVote(proposalId, voter, support, "");
325+
}
326+
327+
/**
328+
* @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been casted yet, retrieve
329+
* voting weight using {IGovernor-getVotes} and call the {_countVote} internal function.
330+
*
331+
* Emits a {IGovernor-VoteCast} event.
332+
*/
333+
function _castVote(
334+
uint256 proposalId,
335+
address account,
336+
uint8 support,
337+
string memory reason
338+
) internal virtual returns (uint256) {
339+
ProposalCore storage proposal = _proposals[proposalId];
340+
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
341+
342+
uint256 weight = getVotes(account, proposal.voteStart.getDeadline());
343+
_countVote(proposalId, account, support, weight);
344+
345+
emit VoteCast(account, proposalId, support, weight, reason);
346+
347+
return weight;
348+
}
349+
350+
/**
351+
* @dev Address through which the governor executes action. Will be overloaded by module that execute actions
352+
* through another contract such as a timelock.
353+
*/
354+
function _executor() internal view virtual returns (address) {
355+
return address(this);
356+
}
357+
}

0 commit comments

Comments
 (0)