ShareClassGovernor

Git Source

Inherits: StartupGovernor, ProposalGovernor, IShareClassGovernor

Author: Daniel Yamagata

A governance contract that enables the execution of arbitrary transactions by a super-majority of a treasury's signers. However, the management of the treasury's board members requires a proposal proccess. A preferred share class can be allocated control of up to three board seats, while the common shares do not have a limit on the number of seats that can be allocated. Once allocated, seats cannot be decreased. For example, if Preferred Shares Class A is allocated control over 2 board seats, its allocation cannot be decreased to 1 board seat. The allocation of seats is done directly by the board of directors. However, the assignment of the allocated board seats is left to the proposal process.

The following contract is heavily based on OZ's Governor Upgradeable, the Late Stage governor, and the Startup Governor The primary difference is proposals are fragmented by share classes. Therefore, direct inheritance from OZ's governor was not possible

State Variables

BALLOT_TYPEHASH

bytes32 public constant BALLOT_TYPEHASH = _BALLOT_TYPEHASH;

EXTENDED_BALLOT_TYPEHASH

bytes32 public constant EXTENDED_BALLOT_TYPEHASH = _EXTENDED_BALLOT_TYPEHASH;

ShareClassGovernorStorageLocation

bytes32 private constant ShareClassGovernorStorageLocation =
    0x562a9c0a18060dac6c99104dbc22c3187c8da12ab6cbbc0788d567773f540f00;

Functions

_getShareClassGovernorStorage

function _getShareClassGovernorStorage() internal pure returns (ShareClassGovernorStorage storage $);

constructor

constructor(address _mezzHub, address _mezzMigrator) StartupGovernor(_mezzHub, _mezzMigrator);

init

Intializes the Governor's state

function init(address initTreasury, bytes memory)
    external
    virtual
    override(StartupGovernor, MezzGovernor, IMezzGovernor)
    initializer;

Parameters

Name
Type
Description

initTreasury

address

The address of the Treasury that the Governor is in charge of

<none>

bytes

__ShareClassGovernor_init

The 'params' argument is maintained in case future versions require additional initialization parameters

function __ShareClassGovernor_init(address initTreasury, bytes memory) internal virtual onlyInitializing;

state

Returns the state of a proposal associated with 'proposalId' as a ProposalState enum

function state(uint256 proposalId)
    public
    view
    virtual
    override(ProposalGovernor, IProposalGovernor)
    returns (IModifiedGovernor.ProposalState);

increaseBoardSeatsForShareClass

Increases the board seats for a given share class.

This function should be called with caution: the number of board seats for a given share class cannot be decreased. Only callable by governance, itself.

function increaseBoardSeatsForShareClass(address shareClass, uint256 increaseAmount)
    external
    virtual
    onlyMezzGovernance
    freezable;

Parameters

Name
Type
Description

shareClass

address

The share class to increase the allocated board seats for

increaseAmount

uint256

The amount to increase the allocated board seats by

proposeBoardMemberChange

Proposes a board member change for a given 'shareClass'. The 'action' must be releated to adding a board member, removing a board member, or swapping a board member.

If the threshold is n, where n is the total number of board members, the new threshold will be n - 1. Otherwise, the threshold of the treasury will not change. The 'action' needs to be one that manages the owners of a team. These are defined in _validateAction(...)

function proposeBoardMemberChange(address shareClass, bytes4 action, bytes memory params, string memory description)
    public
    virtual
    freezable
    returns (uint256);

Parameters

Name
Type
Description

shareClass

address

The share class to propose a board member change for

action

bytes4

The action for managing the treasury's owners

params

bytes

The abi.encoded parameters for the given 'action'

description

string

The description of the proposal

Returns

Name
Type
Description

<none>

uint256

The proposal ID

cancelProposal

Cancels a pending proposal for 'shareClass'.

The caller must be the Proposal's proposer or the Mezz Hub's owner. The params are solely used to re-create the proposal's ID

function cancelProposal(address shareClass, bytes4 action, bytes memory params, bytes32 descriptionHash)
    public
    virtual
    freezable
    returns (uint256);

Returns

Name
Type
Description

<none>

uint256

The proposal ID

executeProposal

Executes a proposal that succeeded.

The caller is not access controlled. However, the proposal must have succeeded and cannot have been executed To succeed, the proposal's for-votes must have reached the quorum and must exceed its against votes. Unless the proposal, has a super-majority, which is 75% in this case, of the votes, it will only be executable once the votiing period has passed. The params and actions are re-validated. The 'target' will also be the treasury

function executeProposal(address shareClass, bytes4 action, bytes memory params, bytes32 descriptionHash)
    public
    virtual
    freezable
    returns (uint256);

executeTx

Same as StartupGovernor's executeTx(...) except with further data validation. Specifically, the management of the treasury's owners and its threshold are disabled. To change these, the proposal process is required.

function executeTx(address target, uint256 value, bytes memory data, bytes memory signatures, uint256 deadline)
    public
    virtual
    override(StartupGovernor, IStartupGovernor)
    freezable
    returns (bytes memory);

changeTreasuryThreshold

Changes the Treasury's security threshold. Must be called by the governor, itself.

Meant to be called via the Share Class Governor's executeTx()

function changeTreasuryThreshold(uint256 newTreasuryThreshold) public virtual onlyMezzGovernance freezable;

Parameters

Name
Type
Description

newTreasuryThreshold

uint256

The new threshold for the treasury

getVotes

Returns the number of votes for an 'account' given a 'shareClass' and 'timepoint'

function getVotes(address account, address shareClass, uint256 timepoint) public view virtual returns (uint256);

_getVotes

Non-adjusted for voting weight, since all proposals are separated by share classes

function _getVotes(address account, address shareClass, uint256 timepoint) internal view virtual returns (uint256);

quorum

Returns the nominal quorum for a proposal given a 'shareClass' and 'timepoint'. The nominal quorum is the minimum number of for-votes that a proposal needs to succeed It is based off the quorum percentage, which is set by governance, itself

function quorum(address shareClass, uint256 timepoint) public view virtual returns (uint256);

_quorum

function _quorum(address shareClass, uint256 timepoint) internal view virtual returns (uint256);

proposalThreshold

Returns the nominal proposal threshold given a 'shareClass' The nominal proposal threshold is the minimum number of votes that the sender must have for a proposal to be created It is based off the proposal threshold percentage, which is set by governance, itself

function proposalThreshold(address shareClass) public view virtual returns (uint256);

_proposalThreshold

function _proposalThreshold(address shareClass) internal view virtual returns (uint256);

superMajority

Returns the nominal super majority given a 'shareClass' and 'timepoint' A super majority in this context is 75% of the total votes for a share class. It a proposal has >75% of for-votes of the outstanding total votes, it is able to skip the voting period and be executed immediately

function superMajority(address shareClass, uint256 timepoint) public view virtual returns (uint256);

_superMajority

function _superMajority(address shareClass, uint256 timepoint) internal view virtual returns (uint256);

_castVote

'params' are unused outside of event emission

function _castVote(uint256 proposalId, address account, uint8 support, string memory reason, bytes memory params)
    internal
    virtual
    override
    returns (uint256);

hashProposal

Returns the hash of a proposal given a 'shareClass', 'action', 'params', and 'descriptionHash' All of the aforementioned argumetns can be found via events emitted by the Share Class Governor for existing proposals

function hashProposal(address shareClass, bytes4 action, bytes memory params, bytes32 descriptionHash)
    public
    pure
    virtual
    returns (uint256);

Returns

Name
Type
Description

<none>

uint256

The proposal ID as a uint256

proposalSnapshot

Returns a proposal's snapshot, which is the time at which votes are counted. Any votes accumulated past this point are not counted towards the proposal

The snapshot is set during the proposal's and is the sum of the voting delay and block.timestamp

function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256);

Parameters

Name
Type
Description

proposalId

uint256

The ID of the proposal

Returns

Name
Type
Description

<none>

uint256

The snapshot of the proposal, which is measured in seconds from the Unix epoch

proposalDeadline

Returns a proposal's deadline, which is the time at which the proposal can no longer be voted on

The deadline is set during the proposal's creation and is the sum of the voting delay, voting period, and block.timestamp

function proposalDeadline(uint256 proposalId) public view virtual returns (uint256);

Parameters

Name
Type
Description

proposalId

uint256

The ID of the proposal

Returns

Name
Type
Description

<none>

uint256

The deadline of the proposal, which is measured in seconds from the Unix epoch

proposalProposer

Returns the proposer of a proposal

function proposalProposer(uint256 proposalId) public view virtual returns (address);

Returns

Name
Type
Description

<none>

address

The address of the proposer

proposalShareClass

Returns the share class associated with a proposal

function proposalShareClass(uint256 proposalId) public view virtual returns (address);

Parameters

Name
Type
Description

proposalId

uint256

The ID of the proposal

Returns

Name
Type
Description

<none>

address

The address of the share class

allocatedBoardSeats

Returns the total number of allocated board seats across all share classes

This does not include the board seats that have been set. For example, company Foo can have 5 allocated seats yet only 4 members set

function allocatedBoardSeats() public view returns (uint256);

_allocatedBoardSeats

function _allocatedBoardSeats(address treasuryCache) internal view returns (uint256);

getAllocatedSeatsByShareClass

Returns the number of allocated board seats for a given share class

function getAllocatedSeatsByShareClass(address shareClass) public view returns (uint256);

getBoardMembersByShareClass

Returns the board members that have been set by a given share class

function getBoardMembersByShareClass(address shareClass) public view returns (address[] memory);

coreId

Returns the coreId of the implementation as a bytes32

The core ID is the keccak256 hash of the contract name followed by a version under the following syntax: "mezzanine.coreId.ContractName.vX" For example, the core ID of the 2nd version of the Treasury would be the following: keccak256(abi.encodePacked("mezzanine.coreId.Treasury.v2"))

function coreId() public pure virtual override(StartupGovernor, ProposalGovernor, ICredentialed) returns (bytes32);

version

Returns the version of the implementation as a uint256

function version() public pure virtual override(StartupGovernor, ProposalGovernor, ICredentialed) returns (uint256);

COUNTING_MODE

Returns a URL-encoded sequence of key-value pairs that each describe one aspect of voting

Mezzanine Proposal Governors use votes in line with Bravo. The quorum also only counts for votes, like in Bravo. Reference: https://docs.openzeppelin.com/contracts/4.x/api/governance#IGovernor-COUNTING_MODE--

function COUNTING_MODE() public view virtual returns (string memory);

Returns

Name
Type
Description

<none>

string

The URL-encoded sequence of key-value pairs

_updateBoardMembers

Updates state. Also reconstructs the params for a given proposal such that it includes the Treasury's threshold, if needed

function _updateBoardMembers(address treasuryCache, address shareClass, bytes4 action, bytes memory params)
    internal
    virtual
    returns (bytes memory);

Returns

Name
Type
Description

<none>

bytes

The calldata for the execution of the transaction

_addOwnerForShareClass

Adds 'ownerToAdd' to the board members for 'shareClass'

function _addOwnerForShareClass(address shareClass, address ownerToAdd) internal virtual;

_removeOwnerForShareClass

Removes 'ownerToRemove' from the board members for 'shareClass'

function _removeOwnerForShareClass(address shareClass, address ownerToRemove) internal virtual;

_checkInvariants

Reverts if the Treasury's total number of owners is greater than the allocated number of board seats

function _checkInvariants() internal view virtual;

_validateShareClass

Reverts if 'shareClass' is not tracked by 'treasuryCache'

function _validateShareClass(address shareClass, address treasuryCache) internal view virtual;

_validateAction

Validates that an action to manage the Treasury's owners. Unlike in TeamLogic's manageOwners(), the params for adding an owner and removing an owner do not include arguments to change the threshold for the treasury. This is to prevent a malicious proposal by a preferred share class that adds or switches an owner and then changes the threshold to 1. This would effectively enable them to control the treasury without the consent of the other signers. The threshold param is reconstructed in _updateBoardMembers(...) to include the Treasury's current threshold. If the threshold is the same as the number of owners, then the threshold is decreased by 1

function _validateAction(bytes4 action, address shareClass, address treasuryCache, bytes memory params)
    internal
    view
    virtual;

_encodeStateBitmap

function _encodeStateBitmap(IModifiedGovernor.ProposalState proposalState) internal pure virtual returns (bytes32);

_getTotalVotesAtTimepoint

Returns the past total votes for the 'shareClass' at timepoint. The voting weight of the shares is ignored

function _getTotalVotesAtTimepoint(address shareClass, uint256 timepoint) internal view virtual returns (uint256);

_superMajorityReached

Returns whether or not a super majority has been reached for a given proposal. Only for-votes are counted towards the super majority

function _superMajorityReached(uint256 proposalId) internal view virtual returns (bool);

Returns

Name
Type
Description

<none>

bool

True if the for-votes are greater than the super majority, false otherwise

_quorumReached

Returns whether or not the quourum has been reached for a given proposal. Only for-votes are counted towards the quorum

function _quorumReached(uint256 proposalId) internal view virtual returns (bool);

Returns

Name
Type
Description

<none>

bool

True if the for votes are greater than the quorum, false otherwise

supportsInterface

ERC165 support

function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(ProposalGovernor, StartupGovernor, IERC165)
    returns (bool);

Structs

ShareClassGovernorStorage

struct ShareClassGovernorStorage {
    mapping(address => EnumerableSet.AddressSet) _boardMembersByShareClass;
    mapping(address => uint256) _numberOfSeatsByShareClass;
    mapping(uint256 => DataTypes.ShareClassProposalCore) _proposals;
}

Last updated