Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
76e6bc2
feat: add proposal validator (#367)
0xChin May 6, 2025
ee2f4b1
feat: duplicated proposals check (#378)
0xChin May 6, 2025
ed73cd0
feat: add upgradeability to ProposalValidator contract (#384)
0xChin May 23, 2025
49dd290
fix: spec incosistencies (#398)
0xChin May 28, 2025
ec4369c
feat: admin functions (#393)
0xChin May 28, 2025
726f2bd
refactor: proposal type data struct (#401)
0xChin Jun 2, 2025
9962ae5
refactor: voting module naming (#404)
0xChin Jun 3, 2025
dd493da
feat: add hashProposalWithModule function (#403)
0xChin Jun 3, 2025
f48a1a8
chore: remove submit proposal (#409)
0xChin Jun 6, 2025
bebd2e5
feat: add submit funding proposal (#411)
0xChin Jun 23, 2025
315b8e1
fix: funding proposal comments (#425)
0xChin Jun 23, 2025
db99363
feat: add submitCouncilMemberElectionsProposal function (#418)
0xChin Jun 24, 2025
7d3152c
feat: approve proposal (#427)
0xOneTony Jun 26, 2025
a87e8cc
feat: add submit upgrade proposal (#429)
0xChin Jun 30, 2025
0f2aec9
fix: missing attestation validations (#434)
0xChin Jul 2, 2025
1620415
test: add version function test (#438)
0xChin Jul 3, 2025
bc169ff
feat: move to vote (#435)
0xOneTony Jul 3, 2025
4613b94
refactor: improve tests (#440)
0xChin Jul 4, 2025
019d790
fix: voting window to use timestamp (#442)
0xOneTony Jul 7, 2025
a641388
fix: remove imported contracts (#443)
0xOneTony Jul 7, 2025
142d129
fix: check hash after proposal (#447)
0xChin Jul 18, 2025
d18f80b
refactor: fetch proposal types configurator externally (#448)
0xChin Jul 18, 2025
a32244f
fix: check for uninitialized voting modules (#446)
0xChin Jul 21, 2025
74a0363
feat: add check in approve if proposal has moved to vote (#450)
0xOneTony Jul 21, 2025
154065c
fix: msg sender to be consistent (#451)
0xOneTony Jul 21, 2025
261fc72
fix: approved proposer schema (#452)
0xOneTony Jul 21, 2025
6a4a6fc
fix: voting cycle validity on submit (#454)
0xOneTony Jul 22, 2025
6638584
fix: budget cap dos (#453)
0xOneTony Jul 22, 2025
505ae50
fix: move to vote logic (#455)
0xChin Jul 23, 2025
253cc6e
fix: normalize validate functions (#456)
0xChin Jul 24, 2025
332b1f3
chore: use fixed variable for contract version (#457)
0xChin Jul 24, 2025
2499ac1
fix: pp minors (#458)
0xChin Jul 24, 2025
39b6ca9
fix: descrepancies (#464)
0xOneTony Jul 28, 2025
96371dd
fix: add total budget overflow check
0xOneTony Jul 28, 2025
56edbe4
fix: internal review findings
0xOneTony Jul 28, 2025
55edad3
feat: add proposal validator (#367)
0xChin May 6, 2025
9d309ac
feat: duplicated proposals check (#378)
0xChin May 6, 2025
de3851c
feat: add upgradeability to ProposalValidator contract (#384)
0xChin May 23, 2025
c56734e
fix: spec incosistencies (#398)
0xChin May 28, 2025
1506286
feat: admin functions (#393)
0xChin May 28, 2025
d65062a
refactor: proposal type data struct (#401)
0xChin Jun 2, 2025
928d95e
refactor: voting module naming (#404)
0xChin Jun 3, 2025
ce51b0c
feat: add hashProposalWithModule function (#403)
0xChin Jun 3, 2025
525f45e
chore: remove submit proposal (#409)
0xChin Jun 6, 2025
1ade987
feat: add submit funding proposal (#411)
0xChin Jun 23, 2025
6feee1b
fix: funding proposal comments (#425)
0xChin Jun 23, 2025
932b8c8
feat: add submitCouncilMemberElectionsProposal function (#418)
0xChin Jun 24, 2025
3db6064
feat: approve proposal (#427)
0xOneTony Jun 26, 2025
8dd18ca
feat: add submit upgrade proposal (#429)
0xChin Jun 30, 2025
9699b32
fix: missing attestation validations (#434)
0xChin Jul 2, 2025
c9185ff
test: add version function test (#438)
0xChin Jul 3, 2025
c19485b
feat: move to vote (#435)
0xOneTony Jul 3, 2025
a96293c
refactor: improve tests (#440)
0xChin Jul 4, 2025
dc8f010
fix: voting window to use timestamp (#442)
0xOneTony Jul 7, 2025
41c9d3d
fix: remove imported contracts (#443)
0xOneTony Jul 7, 2025
a52974f
fix: check hash after proposal (#447)
0xChin Jul 18, 2025
f53d1b3
refactor: fetch proposal types configurator externally (#448)
0xChin Jul 18, 2025
056a7ec
fix: check for uninitialized voting modules (#446)
0xChin Jul 21, 2025
61ed0e1
feat: add check in approve if proposal has moved to vote (#450)
0xOneTony Jul 21, 2025
32c45b3
fix: msg sender to be consistent (#451)
0xOneTony Jul 21, 2025
02f0822
fix: approved proposer schema (#452)
0xOneTony Jul 21, 2025
2f0c65d
fix: voting cycle validity on submit (#454)
0xOneTony Jul 22, 2025
006e9ac
fix: budget cap dos (#453)
0xOneTony Jul 22, 2025
98143cc
fix: move to vote logic (#455)
0xChin Jul 23, 2025
161ce4f
fix: normalize validate functions (#456)
0xChin Jul 24, 2025
1a442a1
chore: use fixed variable for contract version (#457)
0xChin Jul 24, 2025
df382ab
fix: pp minors (#458)
0xChin Jul 24, 2025
ed61d76
fix: descrepancies (#464)
0xOneTony Jul 28, 2025
4e212e5
fix: add total budget overflow check
0xOneTony Jul 28, 2025
b40bf17
Merge branch 'sc-feat/permissionless-proposals' of https://github.com…
0xOneTony Jul 29, 2025
9811abb
Merge pull request #465 from defi-wonderland/chore/pp-sync-develop
0xOneTony Jul 29, 2025
ca8fb7d
fix: add approval timing check (#472)
0xOneTony Aug 8, 2025
f154e79
fix: misc findings (#479)
0xOneTony Aug 12, 2025
2e40422
fix: improve test vars names (#480)
0xOneTony Aug 12, 2025
ddd45ee
fix: make validator immutable (#481)
0xOneTony Aug 12, 2025
1300c98
fix: proposal validator pre-pr (#483)
0xiamflux Aug 12, 2025
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
1 change: 1 addition & 0 deletions .semgrep/rules/sol-rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ rules:
- packages/contracts-bedrock/src/dispute/SuperFaultDisputeGame.sol
- packages/contracts-bedrock/src/dispute/SuperPermissionedDisputeGame.sol
- packages/contracts-bedrock/src/governance/MintManager.sol
- packages/contracts-bedrock/src/governance/ProposalValidator.sol
- packages/contracts-bedrock/src/periphery/TransferOnion.sol
- packages/contracts-bedrock/src/periphery/faucet/Faucet.sol
- packages/contracts-bedrock/src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol
Expand Down
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);
Copy link
Collaborator

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. setAuthorizedProposer is 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.


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 (
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Author

Choose a reason for hiding this comment

The 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;
}
Loading