> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/reserve-protocol/reserve-index-dtf/llms.txt
> Use this file to discover all available pages before exploring further.

# FolioGovernor Contract

> On-chain governance with dynamic proposal thresholds and timelock integration

## Overview

The **FolioGovernor** contract provides on-chain governance for Folio instances. It extends OpenZeppelin's Governor framework with dynamic proposal thresholds based on token supply.

### Key Features

* **Dynamic Proposal Threshold**: Percentage-based threshold that scales with supply
* **Timelock Integration**: All proposals execute through a timelock
* **Quorum Control**: Configurable quorum as percentage of supply
* **Vote Delegation**: Users can delegate voting power
* **Simple Counting**: For/Against/Abstain voting

## Architecture

FolioGovernor extends multiple OpenZeppelin governor modules:

```solidity FolioGovernor.sol theme={null}
contract FolioGovernor is
    GovernorUpgradeable,
    GovernorSettingsUpgradeable,
    GovernorCountingSimpleUpgradeable,
    GovernorVotesUpgradeable,
    GovernorVotesQuorumFractionUpgradeable,
    GovernorTimelockControlUpgradeable
```

## Initialization

<ParamField path="_token" type="IVotes">
  Voting token (typically StakingVault or Folio with voting enabled)
</ParamField>

<ParamField path="_timelock" type="TimelockControllerUpgradeable">
  Timelock contract for proposal execution
</ParamField>

<ParamField path="_votingDelay" type="uint48">
  Delay in seconds before voting starts after proposal
</ParamField>

<ParamField path="_votingPeriod" type="uint32">
  Duration in seconds that voting remains open
</ParamField>

<ParamField path="_proposalThreshold" type="uint256">
  Percentage of supply required to propose (e.g., 0.01e18 = 1%)
</ParamField>

<ParamField path="_quorumFraction" type="uint256">
  Percentage of supply required for quorum (e.g., 0.04e18 = 4%)
</ParamField>

```solidity FolioGovernor.sol theme={null}
function initialize(
    IVotes _token,
    TimelockControllerUpgradeable _timelock,
    uint48 _votingDelay,
    uint32 _votingPeriod,
    uint256 _proposalThreshold,
    uint256 _quorumFraction
) external initializer
```

## Governance Parameters

### Proposal Threshold

The number of tokens required to create a proposal is dynamically calculated:

```solidity FolioGovernor.sol theme={null}
function proposalThreshold() public view returns (uint256) {
    uint256 threshold = super.proposalThreshold(); // D18{1}
    uint256 pastSupply = Math.max(1, token().getPastTotalSupply(clock() - 1));

    // CEIL to ensure thresholds near 0% don't get rounded down to 0
    return (threshold * pastSupply + (1e18 - 1)) / 1e18;
}
```

<Info>
  The threshold is calculated as a percentage of the **previous block's supply**, preventing manipulation through same-block minting.
</Info>

### Quorum

Quorum is calculated using the same percentage-based approach:

```solidity FolioGovernor.sol theme={null}
function quorumDenominator() public pure override returns (uint256) {
    return 1e18; // Use 18 decimals for percentage precision
}
```

<ParamField path="Quorum Numerator" type="uint256">
  E.g., 0.04e18 for 4% quorum requirement
</ParamField>

<ParamField path="Quorum Denominator" type="uint256" default="1e18">
  Fixed at 1e18 for 18-decimal precision
</ParamField>

## Proposal Lifecycle

### 1. Create Proposal

```solidity Example Proposal theme={null}
address[] memory targets = new address[](1);
targets[0] = address(folio);

uint256[] memory values = new uint256[](1);
values[0] = 0;

bytes[] memory calldatas = new bytes[](1);
calldatas[0] = abi.encodeWithSignature(
    "setMintFee(uint256)",
    0.01e18 // 1% mint fee
);

string memory description = "Proposal: Set mint fee to 1%";

uint256 proposalId = governor.propose(
    targets,
    values,
    calldatas,
    description
);
```

### 2. Voting Delay

After creation, there's a delay before voting begins:

```solidity theme={null}
function votingDelay() public view returns (uint256) {
    // Returns delay in blocks/seconds
}
```

<Info>
  This delay allows users to acquire tokens and delegate voting power before the vote starts.
</Info>

### 3. Active Voting

During the voting period, token holders can vote:

```solidity Cast Vote theme={null}
// Vote options: Against (0), For (1), Abstain (2)
governor.castVote(proposalId, 1); // Vote For

// Or vote with reason
governor.castVoteWithReason(
    proposalId,
    1,
    "This improves protocol sustainability"
);
```

### 4. Queue in Timelock

Successful proposals must be queued:

```solidity Queue Proposal theme={null}
governor.queue(
    targets,
    values,
    calldatas,
    keccak256(bytes(description))
);
```

### 5. Execute After Timelock

Once the timelock delay passes:

```solidity Execute Proposal theme={null}
governor.execute(
    targets,
    values,
    calldatas,
    keccak256(bytes(description))
);
```

## Proposal States

```solidity Proposal States theme={null}
enum ProposalState {
    Pending,      // Waiting for voting delay to pass
    Active,       // Currently accepting votes
    Canceled,     // Canceled by proposer or guardian
    Defeated,     // Failed to reach quorum or majority
    Succeeded,    // Passed, ready to queue
    Queued,       // Queued in timelock
    Expired,      // Timelock expired without execution
    Executed      // Successfully executed
}
```

<ParamField path="Pending" type="state">
  Proposal created, waiting for voting delay
</ParamField>

<ParamField path="Active" type="state">
  Voting is open
</ParamField>

<ParamField path="Succeeded" type="state">
  Vote passed, ready to be queued
</ParamField>

<ParamField path="Queued" type="state">
  In timelock, waiting for execution delay
</ParamField>

<ParamField path="Executed" type="state">
  Successfully executed
</ParamField>

## Voting Power

### Token-Based Voting

Voting power comes from the voting token (IVotes):

```solidity Check Voting Power theme={null}
uint256 votingPower = token.getPastVotes(voter, proposalSnapshot);
```

<Info>
  Voting power is snapshotted at the proposal creation block to prevent double-voting.
</Info>

### Delegation

Users can delegate their voting power:

```solidity Delegate Votes theme={null}
// Delegate to another address
token.delegate(delegateAddress);

// Self-delegate to activate own voting power
token.delegate(msg.sender);
```

<Warning>
  Tokens do not have voting power until delegated (even to yourself).
</Warning>

## Timelock Integration

### Execution Delay

All proposals execute through a timelock:

```solidity Timelock Flow theme={null}
1. Proposal succeeds → Queue in timelock
2. Wait for timelock delay (e.g., 2 days)
3. Execute proposal
```

### Cancellation Rights

Timelock guardians can cancel malicious proposals:

```solidity Guardian Cancel theme={null}
// Guardians have CANCELLER_ROLE on timelock
timelock.cancel(operationId);
```

## Admin Functions

Governor settings can be updated via governance:

### Set Voting Delay

```solidity FolioGovernor.sol theme={null}
function setVotingDelay(uint256 newVotingDelay) external onlyGovernance
```

### Set Voting Period

```solidity FolioGovernor.sol theme={null}
function setVotingPeriod(uint256 newVotingPeriod) external onlyGovernance
```

### Set Proposal Threshold

```solidity FolioGovernor.sol theme={null}
function setProposalThreshold(uint256 newProposalThreshold) external onlyGovernance
```

<Warning>
  Proposal threshold cannot exceed 100% (1e18). This prevents locking governance.
</Warning>

### Update Quorum

```solidity FolioGovernor.sol theme={null}
function updateQuorumNumerator(uint256 newQuorumNumerator) external onlyGovernance
```

## View Functions

### Get Proposal State

```solidity FolioGovernor.sol theme={null}
function state(uint256 proposalId) public view returns (ProposalState)
```

### Check Voting

```solidity FolioGovernor.sol theme={null}
function hasVoted(uint256 proposalId, address account) public view returns (bool)
```

### Get Votes

```solidity FolioGovernor.sol theme={null}
function proposalVotes(uint256 proposalId) public view returns (
    uint256 againstVotes,
    uint256 forVotes,
    uint256 abstainVotes
)
```

## Events

<ResponseField name="ProposalCreated" type="event">
  Emitted when a new proposal is created

  **Parameters:**

  * `proposalId` - Unique proposal identifier
  * `proposer` - Address that created the proposal
  * `targets` - Target contract addresses
  * `values` - ETH values for calls
  * `signatures` - Function signatures
  * `calldatas` - Encoded function calls
  * `startBlock` - Voting start block
  * `endBlock` - Voting end block
  * `description` - Proposal description
</ResponseField>

<ResponseField name="VoteCast" type="event">
  Emitted when a vote is cast

  **Parameters:**

  * `voter` - Address that voted
  * `proposalId` - Proposal ID
  * `support` - Vote type (0=Against, 1=For, 2=Abstain)
  * `weight` - Voting power used
  * `reason` - Vote reason (if provided)
</ResponseField>

<ResponseField name="ProposalQueued" type="event">
  Emitted when proposal is queued in timelock

  **Parameters:**

  * `proposalId` - Proposal ID
  * `eta` - Earliest execution time
</ResponseField>

<ResponseField name="ProposalExecuted" type="event">
  Emitted when proposal is executed

  **Parameters:**

  * `proposalId` - Proposal ID
</ResponseField>

## Error Handling

<ResponseField name="Governor__InvalidProposalThreshold" type="error">
  Thrown when trying to set proposal threshold above 100%
</ResponseField>

## Best Practices

<Tip>
  **Proposal Creation**

  * Provide clear, detailed descriptions
  * Test proposal calldata on testnet first
  * Consider timelock delay in planning
  * Communicate with community before proposing
</Tip>

<Tip>
  **Voting**

  * Delegate your tokens to activate voting power
  * Vote early to signal direction
  * Provide reasoning for transparency
  * Monitor proposals actively
</Tip>

<Tip>
  **Security**

  * Use multisig or DAO for guardian role
  * Set reasonable timelock delays (2-7 days typical)
  * Keep proposal threshold accessible but meaningful (0.1-1%)
  * Monitor for malicious proposals
</Tip>
