Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove GovernorCompatibilyBravo and add simpler GovernorStorage #4360

Merged
merged 69 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
ef52cdc
Remove the GovernorCompatibilyBravo module in favor of the simpler Go…
Amxx Jun 16, 2023
37f33a8
Update contracts/governance/README.adoc
Amxx Jun 16, 2023
0eb90ca
Apply suggestions from code review
Amxx Jun 20, 2023
fb35e92
wip
Amxx Jun 28, 2023
841c80d
Merge remote-tracking branch 'upstream' into feature/Governor-storage
Amxx Jun 28, 2023
4b97eb4
refactor state checking
Amxx Jun 28, 2023
0f89b6a
update docs
Amxx Jun 28, 2023
1d7e28c
fix lint
Amxx Jun 28, 2023
79ad539
_queue returns bool
Amxx Jun 29, 2023
c1a6eee
add changeset
Amxx Jun 29, 2023
51b37ec
bool → eta
Amxx Jun 29, 2023
f527b69
minimize change
Amxx Jun 29, 2023
1d60a50
split propose into public + internal
Amxx Jun 29, 2023
67f808a
split internals function
Amxx Jun 29, 2023
9b76c86
Update contracts/governance/extensions/GovernorStorage.sol
Amxx Jun 29, 2023
caf1fe8
wip
Amxx Jun 30, 2023
30eb76c
Merge remote-tracking branch 'upstream' into feature/Governor-storage
Amxx Jun 30, 2023
44e23cc
fix lint
Amxx Jul 3, 2023
18c1e35
test GovernorStorage
Amxx Jul 3, 2023
35d6359
coverage
Amxx Jul 3, 2023
aeae17e
lint
Amxx Jul 3, 2023
d4f858e
Merge remote-tracking branch 'upstream' into feature/Governor-storage
Amxx Jul 3, 2023
ce87721
move proposalEta to core
Amxx Jul 3, 2023
57cad90
Merge branch 'master' into feature/Governor-storage
Amxx Jul 5, 2023
8b7788b
fix lint
Amxx Jul 5, 2023
e7d2123
Merge remote-tracking branch 'upstream' into feature/Governor-storage
Amxx Jul 7, 2023
f2d55b0
Merge branch 'master' into feature/Governor-storage
ernestognw Jul 19, 2023
a67ce0e
remove proposalId from the internal function. It is now computed inte…
Amxx Jul 25, 2023
c8717b6
Merge branch 'master' into feature/Governor-storage
Amxx Jul 27, 2023
1b7a395
improve docs
frangio Jul 28, 2023
b97a4b7
rename implemented -> queued
frangio Jul 28, 2023
b828ced
rename _{queue,execute}Calls to _do{Queue,Execute}
frangio Jul 28, 2023
899b14b
typo
frangio Jul 28, 2023
edd9c35
edit docs section on GovStorage
frangio Jul 28, 2023
869836f
fix timelock id
frangio Jul 28, 2023
0091b82
remove proposalId and add descriptionHash to return values
frangio Jul 28, 2023
331f8e1
revert on unknown proposalId
frangio Jul 28, 2023
d44e090
lint
frangio Jul 29, 2023
ef1c20f
remove GovernorCompatibilityBravo.test.js
frangio Jul 29, 2023
11ca422
rename getters
frangio Jul 29, 2023
977083a
update changeset
frangio Jul 29, 2023
8084aa3
update changeset
frangio Jul 29, 2023
5645ec8
fix test helpers & remove clock from the governor interface id
Amxx Jul 31, 2023
9d2ac8b
_doQueue returns eta only
Amxx Jul 31, 2023
ccd0d47
Update docs/modules/ROOT/pages/governance.adoc
Amxx Jul 31, 2023
d3d2396
merge _proposals and _proposalsExtra mappings
Amxx Jul 31, 2023
440cedc
Apply suggestions from code review
frangio Aug 1, 2023
bdddeda
switch to custom error and add tests
frangio Aug 1, 2023
b2445f8
Batch GovernorStorage transactions setup
ernestognw Aug 1, 2023
2a8dfb3
improve _cancel docs
frangio Aug 1, 2023
02e43ff
add proposalThreshold in IGovernor
frangio Aug 1, 2023
92589ff
improve definition of ETA
frangio Aug 1, 2023
e313b71
remove redundant docs
frangio Aug 1, 2023
caf04b2
edit docs for queue
frangio Aug 1, 2023
b01dad5
add note on possible revert in _queue
frangio Aug 1, 2023
ec55fe1
Merge remote-tracking branch 'upstream/master' into feature/Governor-…
frangio Aug 1, 2023
6b352c4
add missing await
frangio Aug 1, 2023
6f99bc9
fix variable
frangio Aug 1, 2023
62f6fd6
Revert "add missing await"
frangio Aug 1, 2023
037831e
rename _do{Queue,Execute} to _{queue,execute}Operations
frangio Aug 1, 2023
5e74069
lint
frangio Aug 1, 2023
8cb38ed
remove internal _queue and _execute
frangio Aug 3, 2023
d861ec1
make governor into interface
frangio Aug 1, 2023
f9240de
turn IGovernor into interface
frangio Aug 3, 2023
9e7f04e
lint
frangio Aug 3, 2023
d1d8e83
lint
frangio Aug 3, 2023
ae58b64
fix docs
frangio Aug 3, 2023
d2e54af
Remove clock and CLOCK_Mode from IGovernor
Amxx Aug 3, 2023
1853702
document memory/storage insonsistency has beeing the result of gas co…
Amxx Aug 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-lobsters-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': major
---

`Governor`: Add a public `queue` and an internal `_queue` function in the governor core. Module that implement queuing are expected to override the internal one.
5 changes: 5 additions & 0 deletions .changeset/wild-beds-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': major
---

`GovernorStorage`: new governor extensions that stores the proposal details onchain. This replaces the old GovernorCompatibilityBravo module for proposal enumerability.
285 changes: 173 additions & 112 deletions contracts/governance/Governor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,8 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
bytes4 governorCancelId = this.cancel.selector ^ this.proposalProposer.selector;

bytes4 governorParamsId = this.castVoteWithReasonAndParams.selector ^
this.castVoteWithReasonAndParamsBySig.selector ^
this.getVotesWithParams.selector;

// The original interface id in v4.3.
bytes4 governor43Id = type(IGovernor).interfaceId ^
type(IERC6372).interfaceId ^
governorCancelId ^
governorParamsId;

// An updated interface id in v4.6, with params added.
bytes4 governor46Id = type(IGovernor).interfaceId ^ type(IERC6372).interfaceId ^ governorCancelId;

// For the updated interface id in v4.9, we use governorCancelId directly.

return
interfaceId == governor43Id ||
interfaceId == governor46Id ||
interfaceId == governorCancelId ||
interfaceId == type(IGovernor).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
super.supportsInterface(interfaceId);
}
Expand Down Expand Up @@ -274,19 +255,86 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
string memory description
) public virtual override returns (uint256) {
address proposer = _msgSender();

// check: description restriction
require(_isValidDescriptionForProposer(proposer, description), "Governor: proposer restricted");
frangio marked this conversation as resolved.
Show resolved Hide resolved

uint256 currentTimepoint = clock();
// check proposal threshold
uint256 proposerVotes = getVotes(proposer, clock() - 1);
uint256 votesThreshold = proposalThreshold();
if (proposerVotes < votesThreshold) {
revert GovernorInsufficientProposerVotes(proposer, proposerVotes, votesThreshold);
}

// Avoid stack too deep
{
uint256 proposerVotes = getVotes(proposer, currentTimepoint - 1);
uint256 votesThreshold = proposalThreshold();
if (proposerVotes < votesThreshold) {
revert GovernorInsufficientProposerVotes(proposer, proposerVotes, votesThreshold);
}
return _propose(proposer, targets, values, calldatas, description);
frangio marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @dev See {IGovernor-queue}.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);

_queue(proposalId, targets, values, calldatas, descriptionHash);

return proposalId;
}

/**
* @dev See {IGovernor-execute}.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);

_execute(proposalId, targets, values, calldatas, descriptionHash);

return proposalId;
}

/**
* @dev See {IGovernor-cancel}.
*/
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);

// public cancel restrictions (on top of existing _cancel restrictions).
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Pending));
if (_msgSender() != proposalProposer(proposalId)) {
revert GovernorOnlyProposer(_msgSender());
}

_cancel(proposalId, targets, values, calldatas, descriptionHash);

return proposalId;
}

/**
* @dev Internal propose mechanism. Can be overridden to add more logic on proposal creation.
*
* Emits a {IGovernor-ProposalCreated} event.
*/
function _propose(
address proposer,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) internal virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));

if (targets.length != values.length || targets.length != calldatas.length || targets.length == 0) {
Expand All @@ -296,7 +344,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
revert GovernorUnexpectedProposalState(proposalId, state(proposalId), bytes32(0));
}

uint256 snapshot = currentTimepoint + votingDelay();
uint256 snapshot = clock() + votingDelay();
uint256 duration = votingPeriod();

_proposals[proposalId] = ProposalCore({
Expand All @@ -323,59 +371,55 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
}

/**
* @dev See {IGovernor-execute}.
* @dev Internal execution mechanism. Can be overridden (with a super call) to add logic before or after the
* execution.
*
* Emits a {IGovernor-ProposalExecuted} event.
*/
function execute(
function _execute(
uint256 proposalId,
frangio marked this conversation as resolved.
Show resolved Hide resolved
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
) internal virtual {
_validateStateBitmap(
proposalId,
_encodeStateBitmap(ProposalState.Succeeded) | _encodeStateBitmap(ProposalState.Queued)
);

ProposalState currentState = state(proposalId);
if (currentState != ProposalState.Succeeded && currentState != ProposalState.Queued) {
revert GovernorUnexpectedProposalState(
proposalId,
currentState,
_encodeStateBitmap(ProposalState.Succeeded) | _encodeStateBitmap(ProposalState.Queued)
);
}
// mark as executed before calls to avoid reentrancy
_proposals[proposalId].executed = true;

emit ProposalExecuted(proposalId);

_beforeExecute(proposalId, targets, values, calldatas, descriptionHash);
_execute(proposalId, targets, values, calldatas, descriptionHash);
_afterExecute(proposalId, targets, values, calldatas, descriptionHash);
// before execute: register governance call in queue.
if (_executor() != address(this)) {
for (uint256 i = 0; i < targets.length; ++i) {
if (targets[i] == address(this)) {
_governanceCall.pushBack(keccak256(calldatas[i]));
}
}
}

return proposalId;
}
_executeCalls(proposalId, targets, values, calldatas, descriptionHash);

/**
* @dev See {IGovernor-cancel}.
*/
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
ProposalState currentState = state(proposalId);
if (currentState != ProposalState.Pending) {
revert GovernorUnexpectedProposalState(proposalId, currentState, _encodeStateBitmap(ProposalState.Pending));
}
if (_msgSender() != proposalProposer(proposalId)) {
revert GovernorOnlyProposer(_msgSender());
// after execute: cleanup governance call queue.
if (_executor() != address(this)) {
if (!_governanceCall.empty()) {
_governanceCall.clear();
}
}
frangio marked this conversation as resolved.
Show resolved Hide resolved
return _cancel(targets, values, calldatas, descriptionHash);

emit ProposalExecuted(proposalId);
}

/**
* @dev Internal execution mechanism. Can be overridden to implement different execution mechanism
* @dev Internal execution mechanism. Can be overridden (without a super call) to modify the way execution is
* performed (for example adding a vault/timelock).
*
* NOTE: Calling this function directly will NOT check the current state of the proposal, set the executed flag to
* true or emit the `ProposalExecuted` event. Executing a proposal should be done using {execute}.
*/
function _execute(
function _executeCalls(
uint256 /* proposalId */,
address[] memory targets,
uint256[] memory values,
Expand All @@ -389,39 +433,50 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
}

/**
* @dev Hook before execution is triggered.
* @dev Internal queuing mechanism. Can be overridden (with a super call) to add logic before or after the
* queuing.
*
* Emits a {IGovernor-ProposalQueued} event if the eta is not 0.
*/
function _beforeExecute(
uint256 /* proposalId */,
function _queue(
uint256 proposalId,
address[] memory targets,
uint256[] memory /* values */,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
bytes32 descriptionHash
) internal virtual {
if (_executor() != address(this)) {
for (uint256 i = 0; i < targets.length; ++i) {
if (targets[i] == address(this)) {
_governanceCall.pushBack(keccak256(calldatas[i]));
}
}
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Succeeded));

uint256 eta = _queueCalls(proposalId, targets, values, calldatas, descriptionHash);

if (eta == 0) {
revert GovernorQueueNotImplemented();
frangio marked this conversation as resolved.
Show resolved Hide resolved
} else {
emit ProposalQueued(proposalId, eta);
}
}

/**
* @dev Hook after execution is triggered.
*/
function _afterExecute(
uint256 /* proposalId */,
address[] memory /* targets */,
uint256[] memory /* values */,
bytes[] memory /* calldatas */,
* @dev Internal queuing mechanism. Can be overridden (without a super call) to modify the way queuing is
* performed (for example adding a vault/timelock).
*
* This is empty by default, and must be overridden to implement queuing.
*
* This function returns a timestamp that describes the expected eta for execution. If the returned value is 0
* (which is the default value), the core will consider queueing to not be implemented, and the public {queue}
* function will revert.
*
* NOTE: Calling this function directly will NOT check the current state of the proposal, or emit the
* `ProposalQueued` event. Queuing a proposal should be done using {queue}.
*/
function _queueCalls(
uint256 /*proposalId*/,
address[] memory /*targets*/,
uint256[] memory /*values*/,
bytes[] memory /*calldatas*/,
bytes32 /*descriptionHash*/
) internal virtual {
if (_executor() != address(this)) {
if (!_governanceCall.empty()) {
_governanceCall.clear();
}
}
) internal virtual returns (uint256) {
return 0;
}

/**
Expand All @@ -431,30 +486,22 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
* Emits a {IGovernor-ProposalCanceled} event.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);

ProposalState currentState = state(proposalId);
uint256 proposalId,
address[] memory /*targets*/,
uint256[] memory /*values*/,
bytes[] memory /*calldatas*/,
bytes32 /*descriptionHash*/
) internal virtual {
_validateStateBitmap(
proposalId,
_ALL_PROPOSAL_STATES_BITMAP ^
_encodeStateBitmap(ProposalState.Canceled) ^
_encodeStateBitmap(ProposalState.Expired) ^
_encodeStateBitmap(ProposalState.Executed)
);

bytes32 forbiddenStates = _encodeStateBitmap(ProposalState.Canceled) |
_encodeStateBitmap(ProposalState.Expired) |
_encodeStateBitmap(ProposalState.Executed);
if (forbiddenStates & _encodeStateBitmap(currentState) != 0) {
revert GovernorUnexpectedProposalState(
proposalId,
currentState,
_ALL_PROPOSAL_STATES_BITMAP ^ forbiddenStates
);
}
_proposals[proposalId].canceled = true;

emit ProposalCanceled(proposalId);

return proposalId;
}

/**
Expand Down Expand Up @@ -678,6 +725,20 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
return bytes32(1 << uint8(proposalState));
}

/**
* @dev Check that the current state of a proposal matches the requirements described by the `allowedStates` bitmap.
* This bitmap should be built using `_encodeStateBitmap`.
*
* If requirements are not met, revert with an `GovernorUnexpectedProposalState` error.
frangio marked this conversation as resolved.
Show resolved Hide resolved
*/
function _validateStateBitmap(uint256 proposalId, bytes32 allowedStates) private view returns (ProposalState) {
ProposalState currentState = state(proposalId);
if (_encodeStateBitmap(currentState) & allowedStates == bytes32(0)) {
revert GovernorUnexpectedProposalState(proposalId, currentState, allowedStates);
}
return currentState;
}

/*
* @dev Check if the proposer is authorized to submit a proposal with the given description.
*
Expand Down
Loading