Skip to content

Commit

Permalink
Add governor delay
Browse files Browse the repository at this point in the history
  • Loading branch information
neokry committed Dec 6, 2023
1 parent e81cfce commit 9d24647
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 78 deletions.
41 changes: 19 additions & 22 deletions src/governance/governor/Governor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EIP712 } from "../../lib/utils/EIP712.sol";
import { SafeCast } from "../../lib/utils/SafeCast.sol";

import { GovernorStorageV1 } from "./storage/GovernorStorageV1.sol";
import { GovernorStorageV2 } from "./storage/GovernorStorageV2.sol";
import { Token } from "../../token/Token.sol";
import { Treasury } from "../treasury/Treasury.sol";
import { IManager } from "../../manager/IManager.sol";
Expand All @@ -21,7 +22,7 @@ import { VersionedContract } from "../../VersionedContract.sol";
/// Modified from:
/// - OpenZeppelin Contracts v4.7.3 (governance/extensions/GovernorTimelockControl.sol)
/// - NounsDAOLogicV1.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license.
contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, ProposalHasher, GovernorStorageV1 {
contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, ProposalHasher, GovernorStorageV1, GovernorStorageV2 {
/// ///
/// IMMUTABLES ///
/// ///
Expand Down Expand Up @@ -53,6 +54,9 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
/// @notice The maximum voting period setting
uint256 public immutable MAX_VOTING_PERIOD = 24 weeks;

/// @notice The maximum delayed governance setting
uint256 public immutable DELAYED_GOVERNANCE_MAX_DURATION = 30 days;

/// @notice The basis points for 100%
uint256 private immutable BPS_PER_100_PERCENT = 10_000;

Expand Down Expand Up @@ -80,14 +84,16 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
/// @param _votingPeriod The voting period
/// @param _proposalThresholdBps The proposal threshold basis points
/// @param _quorumThresholdBps The quorum threshold basis points
/// @param _delayedGovernanceExpirationTimestamp The delayed governance expiration timestamp
function initialize(
address _treasury,
address _token,
address _vetoer,
uint256 _votingDelay,
uint256 _votingPeriod,
uint256 _proposalThresholdBps,
uint256 _quorumThresholdBps
uint256 _quorumThresholdBps,
uint256 _delayedGovernanceExpirationTimestamp
) external initializer {
// Ensure the caller is the contract manager
if (msg.sender != address(manager)) revert ONLY_MANAGER();
Expand All @@ -106,6 +112,8 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
if (_proposalThresholdBps >= _quorumThresholdBps) revert INVALID_PROPOSAL_THRESHOLD_BPS();
if (_votingDelay < MIN_VOTING_DELAY || _votingDelay > MAX_VOTING_DELAY) revert INVALID_VOTING_DELAY();
if (_votingPeriod < MIN_VOTING_PERIOD || _votingPeriod > MAX_VOTING_PERIOD) revert INVALID_VOTING_PERIOD();
if (_delayedGovernanceExpirationTimestamp > block.timestamp + DELAYED_GOVERNANCE_MAX_DURATION)
revert INVALID_DELAYED_GOVERNANCE_EXPIRATION_TIMESTAMP();

// Store the governor settings
settings.treasury = Treasury(payable(_treasury));
Expand All @@ -114,6 +122,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
settings.votingPeriod = SafeCast.toUint48(_votingPeriod);
settings.proposalThresholdBps = SafeCast.toUint16(_proposalThresholdBps);
settings.quorumThresholdBps = SafeCast.toUint16(_quorumThresholdBps);
delayedGovernanceExpirationTimestamp = _delayedGovernanceExpirationTimestamp;

// Initialize EIP-712 support
__EIP712_init(string.concat(settings.token.symbol(), " GOV"), "1");
Expand All @@ -137,6 +146,11 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
bytes[] memory _calldatas,
string memory _description
) external returns (bytes32) {
// Ensure the governor is not delayed
if (block.timestamp > delayedGovernanceExpirationTimestamp) {
revert WAITING_FOR_EXPIRATION();
}

// Get the current proposal threshold
uint256 currentProposalThreshold = proposalThreshold();

Expand Down Expand Up @@ -209,11 +223,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
/// @param _proposalId The proposal id
/// @param _support The support value (0 = Against, 1 = For, 2 = Abstain)
/// @param _reason The vote reason
function castVoteWithReason(
bytes32 _proposalId,
uint256 _support,
string memory _reason
) external returns (uint256) {
function castVoteWithReason(bytes32 _proposalId, uint256 _support, string memory _reason) external returns (uint256) {
return _castVote(_proposalId, msg.sender, _support, _reason);
}

Expand Down Expand Up @@ -265,12 +275,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos
/// @param _proposalId The proposal id
/// @param _voter The voter address
/// @param _support The vote choice
function _castVote(
bytes32 _proposalId,
address _voter,
uint256 _support,
string memory _reason
) internal returns (uint256) {
function _castVote(bytes32 _proposalId, address _voter, uint256 _support, string memory _reason) internal returns (uint256) {
// Ensure voting is active
if (state(_proposalId) != ProposalState.Active) revert VOTING_NOT_STARTED();

Expand Down Expand Up @@ -518,15 +523,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos

/// @notice The vote counts for a proposal
/// @param _proposalId The proposal id
function proposalVotes(bytes32 _proposalId)
external
view
returns (
uint256,
uint256,
uint256
)
{
function proposalVotes(bytes32 _proposalId) external view returns (uint256, uint256, uint256) {
Proposal memory proposal = proposals[_proposalId];

return (proposal.againstVotes, proposal.forVotes, proposal.abstainVotes);
Expand Down
24 changes: 10 additions & 14 deletions src/governance/governor/IGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 {
/// ERRORS ///
/// ///

error INVALID_DELAYED_GOVERNANCE_EXPIRATION_TIMESTAMP();

error INVALID_PROPOSAL_THRESHOLD_BPS();

error INVALID_QUORUM_THRESHOLD_BPS();
Expand Down Expand Up @@ -113,6 +115,9 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 {
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();

/// @dev Reverts if delayed governance is not expired
error WAITING_FOR_EXPIRATION();

/// ///
/// FUNCTIONS ///
/// ///
Expand All @@ -125,14 +130,16 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 {
/// @param votingPeriod The voting period
/// @param proposalThresholdBps The proposal threshold basis points
/// @param quorumThresholdBps The quorum threshold basis points
/// @param delayedGovernanceExpirationTimestamp The delayed governance expiration timestamp
function initialize(
address treasury,
address token,
address vetoer,
uint256 votingDelay,
uint256 votingPeriod,
uint256 proposalThresholdBps,
uint256 quorumThresholdBps
uint256 quorumThresholdBps,
uint256 delayedGovernanceExpirationTimestamp
) external;

/// @notice Creates a proposal
Expand All @@ -156,11 +163,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 {
/// @param proposalId The proposal id
/// @param support The support value (0 = Against, 1 = For, 2 = Abstain)
/// @param reason The vote reason
function castVoteWithReason(
bytes32 proposalId,
uint256 support,
string memory reason
) external returns (uint256);
function castVoteWithReason(bytes32 proposalId, uint256 support, string memory reason) external returns (uint256);

/// @notice Casts a signed vote
/// @param voter The voter address
Expand Down Expand Up @@ -235,14 +238,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 {

/// @notice The vote counts for a proposal
/// @param proposalId The proposal id
function proposalVotes(bytes32 proposalId)
external
view
returns (
uint256 againstVotes,
uint256 forVotes,
uint256 abstainVotes
);
function proposalVotes(bytes32 proposalId) external view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes);

/// @notice The timestamp valid to execute a proposal
/// @param proposalId The proposal id
Expand Down
10 changes: 10 additions & 0 deletions src/governance/governor/storage/GovernorStorageV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

/// @title GovernorStorageV1
/// @author Rohan Kulkarni
/// @notice The Governor storage contract
contract GovernorStorageV2 {
/// @notice Delays proposal submission to a specific block timestamp
uint256 delayedGovernanceExpirationTimestamp;
}
21 changes: 4 additions & 17 deletions src/manager/IManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,15 @@ interface IManager is IUUPS, IOwnable {
/// @param votingPeriod The time period to vote on a proposal
/// @param proposalThresholdBps The basis points of the token supply required to create a proposal
/// @param quorumThresholdBps The basis points of the token supply required to reach quorum
/// @param delayedGovernanceExpirationTimestamp The timestamp to delay governance until
/// @param vetoer The address authorized to veto proposals (address(0) if none desired)
struct GovParams {
uint256 timelockDelay;
uint256 votingDelay;
uint256 votingPeriod;
uint256 proposalThresholdBps;
uint256 quorumThresholdBps;
uint256 delayedGovernanceExpirationTimestamp;
address vetoer;
}

Expand Down Expand Up @@ -135,26 +137,11 @@ interface IManager is IUUPS, IOwnable {
TokenParams calldata tokenParams,
AuctionParams calldata auctionParams,
GovParams calldata govParams
)
external
returns (
address token,
address metadataRenderer,
address auction,
address treasury,
address governor
);
) external returns (address token, address metadataRenderer, address auction, address treasury, address governor);

/// @notice A DAO's remaining contract addresses from its token address
/// @param token The ERC-721 token address
function getAddresses(address token)
external
returns (
address metadataRenderer,
address auction,
address treasury,
address governor
);
function getAddresses(address token) external returns (address metadataRenderer, address auction, address treasury, address governor);

/// @notice If an implementation is registered by the Builder DAO as an optional upgrade
/// @param baseImpl The base implementation address
Expand Down
29 changes: 4 additions & 25 deletions src/manager/Manager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1
TokenParams calldata _tokenParams,
AuctionParams calldata _auctionParams,
GovParams calldata _govParams
)
external
returns (
address token,
address metadata,
address auction,
address treasury,
address governor
)
{
) external returns (address token, address metadata, address auction, address treasury, address governor) {
// Used to store the address of the first (or only) founder
// This founder is responsible for adding token artwork and launching the first auction -- they're also free to transfer this responsiblity
address founder;
Expand Down Expand Up @@ -156,6 +147,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1
votingDelay: _govParams.votingDelay,
votingPeriod: _govParams.votingPeriod,
proposalThresholdBps: _govParams.proposalThresholdBps,
delayedGovernanceExpirationTimestamp: _govParams.delayedGovernanceExpirationTimestamp,
quorumThresholdBps: _govParams.quorumThresholdBps
});

Expand All @@ -169,11 +161,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1
/// @notice Set a new metadata renderer
/// @param _newRendererImpl new renderer address to use
/// @param _setupRenderer data to setup new renderer with
function setMetadataRenderer(
address _token,
address _newRendererImpl,
bytes memory _setupRenderer
) external returns (address metadata) {
function setMetadataRenderer(address _token, address _newRendererImpl, bytes memory _setupRenderer) external returns (address metadata) {
if (msg.sender != IOwnable(_token).owner()) {
revert ONLY_TOKEN_OWNER();
}
Expand All @@ -200,16 +188,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1
/// @return auction Auction deployed address
/// @return treasury Treasury deployed address
/// @return governor Governor deployed address
function getAddresses(address _token)
public
view
returns (
address metadata,
address auction,
address treasury,
address governor
)
{
function getAddresses(address _token) public view returns (address metadata, address auction, address treasury, address governor) {
DAOAddresses storage addresses = daoAddressesByToken[_token];

metadata = addresses.metadata;
Expand Down

0 comments on commit 9d24647

Please sign in to comment.