Documentation
Tutorials
Launch a New DAO

Creating a new Dao with Flexible Voting

In this tutorial we will cover creating the contracts for a new DAO that include support for Flexible Voting.

As always, smart contract development must be done with care. The code in this tutorial is provided for educational purposes, and should not be deployed in production without proper auditing or security review.

Step 1: Use OpenZeppelin to build your Governor

Flexible Voting is built on top of OpenZeppelin's Governor contracts. A simple Governor will inherit from several OpenZeppelin Governor extensions to assemble the exact functionality desired for the DAO.

In this tutorial, we'll make a set of common choices, but these are by no means the only ones that could be chosen. Flexible Voting is designed to be compatible with all the other extensions provided by OpenZeppelin.

  1. GovernorVotes: Extension of Governor for calculating voting weight from delegations via an ERC20Votes token, or since v4.5, an ERC721Votes token.
  2. GovernorTimelockControl: Gives voters an opportunity to exit the system before a decision they disagree with is executed.
  3. GovernorSettings: Allows the Governor's configuration parameters—such as the proposal threshold, voting delay, and voting period—to be updated.

Step 2: Add Flexible Voting contract

In order to support Flexible Voting we need to inherit the GovernorCountingFractional extension, in addition to the OpenZeppelin extensions mentioned above. Our final Governor should look like this:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
 
import {GovernorVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import {GovernorSettings} from "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import {GovernorTimelockControl} from
  "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
import {
  Governor, GovernorCountingFractional
} from "flexible-voting/src/GovernorCountingFractional.sol";
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
 
contract FlexibleVotingGovernor is
  GovernorCountingFractional,
  GovernorVotes,
  GovernorTimelockControl,
  GovernorSettings
{
  uint256 private constant QUORUM = 100_000e18;
 
  constructor(
    uint256 _initialVotingDelay,
    uint256 _initialVotingPeriod,
    uint256 _initialProposalThreshold,
    ERC20Votes _token,
    TimelockController _timelock,
    string memory _name
  )
    GovernorVotes(_token)
    GovernorSettings(_initialVotingDelay, _initialVotingPeriod, _initialProposalThreshold)
    GovernorTimelockControl(_timelock)
    Governor(_name)
  {}
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(Governor, GovernorTimelockControl)
    returns (bool)
  {
    return GovernorTimelockControl.supportsInterface(interfaceId);
  }
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function castVoteWithReasonAndParamsBySig(
    uint256 proposalId,
    uint8 support,
    string calldata reason,
    bytes memory params,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public override(Governor, GovernorCountingFractional, IGovernor) returns (uint256) {
    return GovernorCountingFractional.castVoteWithReasonAndParamsBySig(
      proposalId, support, reason, params, v, r, s
    );
  }
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function proposalThreshold()
    public
    view
    virtual
    override(Governor, GovernorSettings)
    returns (uint256)
  {
    return GovernorSettings.proposalThreshold();
  }
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function state(uint256 proposalId)
    public
    view
    virtual
    override(Governor, GovernorTimelockControl)
    returns (ProposalState)
  {
    return GovernorTimelockControl.state(proposalId);
  }
 
  /// @notice The amount of POOL required to meet the quorum threshold for a proposal
  /// as of a given block.
  /// @dev Our implementation ignores the block number parameter and returns a constant.
  function quorum(uint256) public pure override returns (uint256) {
    return QUORUM;
  }
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function _execute(
    uint256 proposalId,
    address[] memory targets,
    uint256[] memory values,
    bytes[] memory calldatas,
    bytes32 descriptionHash
  ) internal virtual override(Governor, GovernorTimelockControl) {
    return GovernorTimelockControl._execute(proposalId, targets, values, calldatas, descriptionHash);
  }
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function _cancel(
    address[] memory targets,
    uint256[] memory values,
    bytes[] memory calldatas,
    bytes32 descriptionHash
  ) internal virtual override(Governor, GovernorTimelockControl) returns (uint256) {
    return GovernorTimelockControl._cancel(targets, values, calldatas, descriptionHash);
  }
 
  /// @dev We override this function to resolve ambiguity between inherited contracts.
  function _executor()
    internal
    view
    virtual
    override(Governor, GovernorTimelockControl)
    returns (address)
  {
    return GovernorTimelockControl._executor();
  }
}

This Governor contract now supports Flexible Voting. It must be deployed in conjunction with a properly configured ERC20 votes token and timelock contract. It opens up all the use cases that come along with Flexible Voting for the new DAO, with little downside.