Skip to content

The Powers Protocol

Powers is a role-restricted governance protocol that provides a modular, flexible, decentralized, and efficient governance engine for DAOs. It is designed to be used in combination with implementations of Mandate.sol contracts.

Powers.sol is the central engine of the protocol, acting as the unopinionated core of any organisation’s governance structure. It is intentionally minimalistic and does not contain any specific governance logic itself. Instead, its primary responsibilities are to:

  1. Serve as the Governance Hub: All governance actions, from proposing a vote to executing a transaction, are initiated through Powers.sol. It provides a single, secure entry point for all interactions.
  2. Run checks: Before any action is executed, Powers.sol checks the relevant Mandate conditions to validate the request. This ensures that all governance actions adhere to the rules defined by the organisation’s mandates.
  3. Manage State: It is the authoritative source for the organisation’s state, including which mandates are active, which accounts hold which roles, and the status of all ongoing proposals and actions. It prefers to save as much data as possible on-chain to reduce reliance on off-chain indexing.
  4. Enforce Outcomes: After a Mandate.sol contract has validated an action and returned the execution data, Powers.sol is responsible for reliably executing the final transaction and updating the state accordingly.

In essence, Powers.sol acts as the immutable foundation of the organisation, providing the core mechanics for state management and execution, while the modular Mandate.sol contracts provide the flexible, adaptable governance logic. This separation makes the system both secure and highly extensible.

The Powers protocol implements a role-based access control system that is fundamental to its governance mechanism.

  • ADMIN_ROLE (0): The highest privilege role, assigned to the contract deployer.
  • PUBLIC_ROLE (type(uint256).max): A special role that everyone has by default.

The process of managing roles involves a few key functions.

An admin uses assignRole() to grant a specific role to a user’s account.

An admin uses revokeRole() to remove a role from a user’s account.

To make roles easier to identify, an admin can use labelRole() to give it a human-readable name.

An admin uses blacklistAddress() to prevent an account from interacting with the protocol.

Mandates are modular, role-restricted governance actions and the building blocks of the protocol’s governance system.

Managing the lifecycle of mandates is a core administrative task.

The organisation is born when an admin calls constitute() with the initial set of mandates (an array of MandateInitData). This can only be done once. The constitution phase is then closed using closeConstitute().

As the organisation evolves, new mandates can be added by an admin using adoptMandate().

If a mandate becomes obsolete, an admin can remove it using revokeMandate().

Proposals are used when an action requires community consensus.

A user calls propose() with the mandate details and a metadata URI description.

The proposal enters a voting period defined by the mandate.

Eligible voters use castVote() or castVoteWithReason().

The proposal can be Succeeded, Defeated, or Cancelled.

The execution flow involves a request and callback logic between Powers.sol and the Mandate contracts.

A user initiates an action by calling request() in Powers.sol with the mandate details and a metadata URI description.

Powers.sol calls executeMandate() on the target mandate contract, which validates the request and prepares the execution data.

The mandate calls back to Powers.sol’s fulfill() function, which executes the transaction and marks the action as complete.

See the github repo here.

Key Differences from OpenZeppelin’s Governor.sol:

Section titled “Key Differences from OpenZeppelin’s Governor.sol:”
  1. DAO actions must be encoded in role-restricted external contracts (mandates) following the IMandate interface.
  2. Proposing, voting, cancelling and executing actions are role-restricted along the target mandate.
  3. All DAO actions must run through the governance flow provided by Powers.sol.
  4. Uses a non-weighted voting mechanism: one account has one vote. Accounts vote with their roles, not with their tokens.
  5. Core protocol is intentionally minimalistic - complexity (multi-chain, oracles, timelocks, etc.) must be integrated through mandates.
  • _actions: mapping(uint256 actionId => Action)
  • mandates: mapping(uint16 mandateId => AdoptedMandate)
  • roles: mapping(uint256 roleId => Role)
  • _blacklist: mapping(address account => bool blacklisted)
  • Governance: request, fulfill, propose, cancel, castVote, castVoteWithReason
  • Admin: constitute, closeConstitute, adoptMandate, revokeMandate, assignRole, revokeRole, labelRole, blacklistAddress, setUri, setTreasury
  • Action: Tracks a proposal’s state, voting information, and execution data.
  • AdoptedMandate: Tracks an active mandate’s address, status, and conditions.
  • Role: Tracks role assignments and membership.
  • Conditions: Defines the governance parameters for a mandate (quorum, voting period, etc.).
  • MandateInitData: Used for initializing mandates during constitution or adoption.
  • Governance: ActionRequested, ProposedActionCreated, ProposedActionCancelled, VoteCast
  • Admin: RoleSet, RoleLabel, MandateAdopted, MandateRevoked, BlacklistSet, Powers__Initialized