-
Notifications
You must be signed in to change notification settings - Fork 3.8k
feat(governance): proposal validator #16861
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
Changes from all commits
76e6bc2
ee2f4b1
ed73cd0
49dd290
ec4369c
726f2bd
9962ae5
dd493da
f48a1a8
bebd2e5
315b8e1
db99363
7d3152c
a87e8cc
0f2aec9
1620415
bc169ff
4613b94
019d790
a641388
142d129
d18f80b
a32244f
74a0363
154065c
261fc72
6a4a6fc
6638584
505ae50
253cc6e
332b1f3
2499ac1
39b6ca9
96371dd
56edbe4
55edad3
9d309ac
de3851c
c56734e
1506286
d65062a
928d95e
ce51b0c
525f45e
1ade987
6feee1b
932b8c8
3db6064
8dd18ca
9699b32
c9185ff
c19485b
a96293c
dc8f010
41c9d3d
a52974f
f53d1b3
056a7ec
61ed0e1
32c45b3
02f0822
2f0c65d
006e9ac
98143cc
161ce4f
1a442a1
df382ab
ed61d76
4e212e5
b40bf17
9811abb
ca8fb7d
f154e79
2e40422
ddd45ee
1300c98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| /// @title IApprovalVotingModule | ||
| /// @notice Interface for the Approval Voting Module containing only the essential types | ||
| /// needed by the ProposalValidator contract. | ||
| interface IApprovalVotingModule { | ||
| struct ProposalOption { | ||
| uint256 budgetTokensSpent; | ||
| address[] targets; | ||
| uint256[] values; | ||
| bytes[] calldatas; | ||
| string description; | ||
| } | ||
|
|
||
| struct ProposalSettings { | ||
| uint8 maxApprovals; | ||
| uint8 criteria; | ||
| address budgetToken; | ||
| uint128 criteriaValue; | ||
| uint128 budgetAmount; | ||
| } | ||
|
|
||
| enum PassingCriteria { | ||
| Threshold, | ||
| TopChoices | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {IVotesUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/utils/IVotesUpgradeable.sol"; | ||
|
|
||
| interface IOptimismGovernor { | ||
| function propose( | ||
| address[] memory targets, | ||
| uint256[] memory values, | ||
| bytes[] memory calldatas, | ||
| string memory description, | ||
| uint8 proposalType | ||
| ) external returns (uint256 proposalId); | ||
|
|
||
| function proposeWithModule( | ||
| address module, | ||
| bytes memory proposalData, | ||
| string memory description, | ||
| uint8 proposalType | ||
| ) external returns (uint256 proposalId); | ||
|
|
||
| function timelock() external view returns (address); | ||
|
|
||
| function PROPOSAL_TYPES_CONFIGURATOR() external view returns (address); | ||
|
|
||
| function token() external view returns (IVotesUpgradeable); | ||
|
|
||
| function getProposalType(uint256 proposalId) external view returns (uint8); | ||
|
|
||
| function proposalVotes(uint256 proposalId) | ||
| external | ||
| view | ||
| returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes); | ||
|
|
||
| /// @notice Returns the snapshot block number for a proposal, 0 if proposal doesn't exist | ||
| /// @param proposalId The ID of the proposal | ||
| /// @return The snapshot block number, or 0 if proposal doesn't exist | ||
| function proposalSnapshot(uint256 proposalId) external view returns (uint256); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| /// @title IOptimisticModule | ||
| /// @notice Interface for the Optimistic Module containing only the essential types | ||
| /// needed by the ProposalValidator contract. | ||
| interface IOptimisticModule { | ||
| struct ProposalSettings { | ||
| uint248 againstThreshold; | ||
| bool isRelativeToVotableSupply; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| interface IProposalTypesConfigurator { | ||
| /*////////////////////////////////////////////////////////////// | ||
| ERRORS | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
||
| error InvalidQuorum(); | ||
| error InvalidApprovalThreshold(); | ||
| error NotManagerOrTimelock(); | ||
| error AlreadyInit(); | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| EVENTS | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
||
| event ProposalTypeSet( | ||
| uint8 indexed proposalTypeId, uint16 quorum, uint16 approvalThreshold, string name, string description | ||
| ); | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| STRUCTS | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
||
| struct ProposalType { | ||
| uint16 quorum; | ||
| uint16 approvalThreshold; | ||
| string name; | ||
| string description; | ||
| address module; | ||
| } | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| FUNCTIONS | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
||
| function initialize(address _governor, ProposalType[] calldata _proposalTypes) external; | ||
|
|
||
| function proposalTypes(uint8 proposalTypeId) external view returns (ProposalType memory); | ||
|
|
||
| function setProposalType( | ||
| uint8 proposalTypeId, | ||
| uint16 quorum, | ||
| uint16 approvalThreshold, | ||
| string memory name, | ||
| string memory description, | ||
| address module | ||
| ) external; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| // Interfaces | ||
| import { IOptimismGovernor } from "./IOptimismGovernor.sol"; | ||
| import { ISemver } from "interfaces/universal/ISemver.sol"; | ||
|
|
||
| /// @title IProposalValidator | ||
| /// @notice Interface for the ProposalValidator contract. | ||
| interface IProposalValidator is ISemver { | ||
| error ProposalValidator_InsufficientApprovals(); | ||
| error ProposalValidator_ProposalAlreadyApproved(); | ||
| error ProposalValidator_ProposalAlreadySubmitted(); | ||
| error ProposalValidator_ProposalAlreadyMovedToVote(); | ||
| error ProposalValidator_InvalidAttestation(); | ||
| error ProposalValidator_VotingCycleAlreadySet(); | ||
| error ProposalValidator_ProposalDoesNotExist(); | ||
| error ProposalValidator_ProposalTypesDataLengthMismatch(); | ||
| error ProposalValidator_InvalidFundingProposalType(); | ||
| error ProposalValidator_ExceedsDistributionThreshold(); | ||
| error ProposalValidator_InvalidOptionsLength(); | ||
| error ProposalValidator_AttestationRevoked(); | ||
| error ProposalValidator_AttestationExpired(); | ||
| error ProposalValidator_InvalidAttestationSchema(); | ||
| error ProposalValidator_InvalidCriteriaValue(); | ||
| error ProposalValidator_InvalidAgainstThreshold(); | ||
| error ProposalValidator_InvalidUpgradeProposalType(); | ||
| error ProposalValidator_InvalidVotingCycle(); | ||
| error ProposalValidator_ProposalIdMismatch(); | ||
| error ProposalValidator_InvalidProposer(); | ||
| error ProposalValidator_InvalidProposal(); | ||
| error ProposalValidator_InvalidVotingModule(); | ||
| error ProposalValidator_InvalidTotalBudget(); | ||
| error ProposalValidator_AttestationCreatedAfterLastVotingCycle(); | ||
| error ProposalValidator_PreviousVotingCycleNotStarted(); | ||
|
|
||
| event ProposalSubmitted( | ||
| uint256 indexed proposalId, | ||
| address indexed proposer, | ||
| string description, | ||
| ProposalType proposalType | ||
| ); | ||
|
|
||
| event ProposalApproved( | ||
| uint256 indexed proposalId, | ||
| address indexed approver | ||
| ); | ||
|
|
||
| event ProposalMovedToVote( | ||
| uint256 indexed proposalId, | ||
| address indexed executor | ||
| ); | ||
|
|
||
| event VotingCycleDataSet( | ||
| uint256 cycleNumber, | ||
| uint256 startingTimestamp, | ||
| uint256 duration, | ||
| uint256 votingCycleDistributionLimit | ||
| ); | ||
|
|
||
| event ProposalDistributionThresholdSet(uint256 newProposalDistributionThreshold); | ||
|
|
||
| event ProposalTypeDataSet( | ||
| ProposalType proposalType, | ||
| uint256 requiredApprovals, | ||
| uint8 idInConfigurator | ||
| ); | ||
|
|
||
| event ProposalVotingModuleData( | ||
| uint256 indexed proposalId, | ||
| bytes encodedVotingModuleData | ||
| ); | ||
|
|
||
| event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | ||
|
|
||
| struct ProposalData { | ||
| address proposer; | ||
| ProposalType proposalType; | ||
| bool movedToVote; | ||
| mapping(address => bool) delegateApprovals; | ||
| uint256 approvalCount; | ||
| uint256 votingCycle; | ||
| } | ||
|
|
||
| struct ProposalTypeData { | ||
| uint256 requiredApprovals; | ||
| uint8 idInConfigurator; | ||
| } | ||
|
|
||
| struct VotingCycleData { | ||
| uint256 startingTimestamp; | ||
| uint256 duration; | ||
| uint256 votingCycleDistributionLimit; | ||
| uint256 movedToVoteTokenCount; | ||
| } | ||
|
|
||
| enum ProposalType { | ||
| ProtocolOrGovernorUpgrade, | ||
| MaintenanceUpgrade, | ||
| CouncilMemberElections, | ||
| GovernanceFund, | ||
| CouncilBudget | ||
| } | ||
|
|
||
| function submitUpgradeProposal( | ||
| uint248 _againstThreshold, | ||
| string memory _proposalDescription, | ||
| bytes32 _attestationUid, | ||
| ProposalType _proposalType, | ||
| uint256 _latestVotingCycle | ||
| ) external returns (uint256 proposalId_); | ||
|
|
||
| function submitCouncilMemberElectionsProposal( | ||
| uint128 _criteriaValue, | ||
| string[] memory _optionDescriptions, | ||
| string memory _proposalDescription, | ||
| bytes32 _attestationUid, | ||
| uint256 _votingCycle | ||
| ) external returns (uint256 proposalId_); | ||
|
|
||
| function submitFundingProposal( | ||
| uint128 _criteriaValue, | ||
| string[] memory _optionsDescriptions, | ||
| address[] memory _optionsRecipients, | ||
| uint256[] memory _optionsAmounts, | ||
| string memory _description, | ||
| ProposalType _proposalType, | ||
| uint256 _votingCycle | ||
| ) external returns (uint256 proposalId_); | ||
|
|
||
| function approveProposal(uint256 _proposalId, bytes32 _attestationUid) external; | ||
|
|
||
| function moveToVoteProtocolOrGovernorUpgradeProposal( | ||
| uint248 _againstThreshold, | ||
| string memory _proposalDescription | ||
| ) external returns (uint256 proposalId_); | ||
|
|
||
| function moveToVoteCouncilMemberElectionsProposal( | ||
| uint128 _criteriaValue, | ||
| string[] memory _optionsDescriptions, | ||
| string memory _proposalDescription | ||
| ) external returns (uint256 proposalId_); | ||
|
|
||
| function moveToVoteFundingProposal( | ||
| uint128 _criteriaValue, | ||
| string[] memory _optionsDescriptions, | ||
| address[] memory _optionsRecipients, | ||
| uint256[] memory _optionsAmounts, | ||
| string memory _description, | ||
| ProposalType _proposalType | ||
| ) external returns (uint256 proposalId_); | ||
|
|
||
| function setVotingCycleData( | ||
| uint256 _cycleNumber, | ||
| uint256 _startingTimestamp, | ||
| uint256 _duration, | ||
| uint256 _votingCycleDistributionLimit | ||
| ) external; | ||
|
|
||
| function setProposalDistributionThreshold(uint256 _proposalDistributionThreshold) external; | ||
|
|
||
| function setProposalTypeData( | ||
| ProposalType _proposalType, | ||
| ProposalTypeData memory _proposalTypeData | ||
| ) external; | ||
|
|
||
| function renounceOwnership() external; | ||
|
|
||
| function transferOwnership(address newOwner) external; | ||
|
|
||
| function proposalDistributionThreshold() external view returns (uint256); | ||
|
|
||
| function GOVERNOR() external view returns (IOptimismGovernor); | ||
|
|
||
| function owner() external view returns (address); | ||
|
|
||
|
|
||
| function APPROVED_PROPOSER_ATTESTATION_SCHEMA_UID() external view returns (bytes32); | ||
|
|
||
| function TOP_DELEGATES_ATTESTATION_SCHEMA_UID() external view returns (bytes32); | ||
|
|
||
| function OPTIMISTIC_MODULE_PERCENT_DIVISOR() external view returns (uint256); | ||
|
|
||
| function proposalTypesData(ProposalType) external view returns (uint256 requiredApprovals, uint8 idInConfigurator); | ||
|
|
||
| function votingCycles(uint256) external view returns ( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't we return the struct here or is that an anti-pattern?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it is a good practice to return the fields instead of the struct for the getter function. |
||
| uint256 startingTimestamp, | ||
| uint256 duration, | ||
| uint256 votingCycleDistributionLimit, | ||
| uint256 movedToVoteTokenCount | ||
| ); | ||
|
|
||
| function __constructor__( | ||
| address _owner, | ||
| IOptimismGovernor _governor, | ||
| bytes32 _approvedProposerAttestationSchemaUid, | ||
| bytes32 _topDelegatesAttestationSchemaUid | ||
| ) external; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting. I initially misinterpreted this as being about proxy-related ownership, which I think it is not. But: it does make me wonder how much complexity we introduce with these sorts of stateful config updates via the
owner.I hadn't thought about it initially, but -- if the OF can simply
setAuthorizedProposer, then in theory, this entire contract could be immutable/non-upgradable/non-configurable.setAuthorizedProposeris basically a superset of the upgrade functionality here.There are pros and cons to this, maybe it is worth a bigger discussion in chat. There are obviously some cons around in-flight proposals, indexers, etc. But actually with indexers for the Governor, we've seen breaking changes cause issues with historic votes. Separating addresses could have benefits there.