EIPs/EIPS/eip-2767.md
Alex Beregszaszi f8e7c028f5
Simplify the EIP template (#2939)
* Try to clarify the meaning of EIP fields

* Remove unhelpful extra comments in the template

* Change EIP-1491 from CRLF to LF

* Remove template comments from EIPs

* Fix heading: Abstarct -> Abstract

* Update EIP-2014

* Change author list of EIP-1
2020-09-08 17:32:52 +08:00

11 KiB

eip title author discussions-to status type category created requires
2767 Contract Ownership Governance Soham Zemse (@zemse), Nick Mudge (@mudgen) https://github.com/ethereum/EIPs/issues/2766 Draft Standards Track ERC 2020-07-04 165, 173, 191

Simple Summary

A standard interface for Governance contracts that administratively decentralize the ownership of smart contracts.

Abstract

The following standard allows for the implementation of a standard API for a Governance smart contract. This standard provides basic functionality for on-chain and off-chain governance inheriting equal or privileged voting rights. Existing ERC-173 compatible contracts can upgrade from private key wallet ownership to a Governance smart contract. Smart contracts that use a Governance contract are more administratively decentralised and adhering to a standard API enables tools to populate governance information to dApp users.

Motivation

Traditionally, many contracts that require that they be owned or controlled in some way use ERC-173 which standardized the use of ownership in the smart contracts. For example to withdraw funds or perform administrative actions.

contract dApp {
  function doSomethingAdministrative() external onlyOwner {
    // admin logic that can be performed by a single wallet
  }
}

Often, such administrative rights for a contract are written for maintenance purpose but users need to trust the owner. Rescue operations by an owner have raised questions on decentralised nature of the projects. Also, there is a possibility of compromise of an owner's private key.

At present, there are various implementation ideas for handling governance. Adhering to a standard API would enable general tools (like EtherScan) to populate governance information, thus increasing transparency to users. This can result in a wide adoption for contract governance.

Specification

Notes

  • The following specifications use syntax from Solidity 0.6.0 (or above)
  • Governor is the board member who has voting right.
  • Power represents the voting privilege of a governor.

A contract that is compliant with ERC-2767 shall implement the following interface:

/// @title ERC-2767 Governance with Privileged Voting Rights
/// @dev ERC-165 InterfaceID: 0x4fe54581
interface ERC2767 {
    /// @dev This emits when governor power changes
    event GovernorPowerUpdated(address indexed governor, uint256 power);

    /// @notice Gets the consensus power of the governor
    /// @param _governor Address of the governor
    /// @return The governor's voting power
    function powerOf(address _governor) external view returns (uint256);

    /// @notice Gets the sum of the power of all governors
    /// @return Sum of the power of all governors
    function totalPower() external view returns (uint256);

    /// @notice Gets number votes required for achieving consensus
    /// @return Required number of consensus votes
    function required() external view returns (uint256);

    /// @notice Updates governor statuses
    /// @param _governor Governor address
    /// @param _newPower New power for the governor
    /// @dev Only Governance can call
    function setGovernor(address _governor, uint256 _newPower) external;
}

Implementations that require equal governance rights between the governors can simply have equal power values for every governor.

There is a possibility that totalPower() changes rapidly due to adding or removing governors. This can result in having a fixed required() return value easier or difficult to achieve. Implementations can have custom logic that affect the return value of required(). For example the return value of required() can be changed by custom logic in order to maintain 51% of totalPower(). So if more governors are added in such implementations, the required() return value would automatically take that into account.

Interface Identification

An ERC-2767 Governance Contract should also implement ERC-165. This helps general tools to identify whether a contract is a ERC-2767 Governance contract.

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Transaction execution (Optional)

There are two optional interfaces for transaction execution: On-chain and Off-chain. A ERC-2767 compatible governance contract can optionally implement one of them or both. The contract should specify the interfaceID(s) using ERC-165's supportsInterface method.

On-chain

/// @title ERC-2767 On-chain Governance
/// @dev ERC-165 InterfaceID: 0x947133b4
interface IGovernanceOnchain {
    struct Transaction {
        address destination;
        uint256 value;
        bytes data;
        bool executed;
        uint256 votes;
    }

    /// @dev Emits when a transaction is proposed
    event TransactionCreated(uint256 indexed transactionId);

    /// @dev Emits every time a governor confirms a transaction
    event TransactionConfirmed(uint256 indexed transactionId);

    /// @dev Emits whenever a governor takes back their confirmation
    event TransactionRevoked(uint256 indexed transactionId);

    /// @dev Emits when a transactions with enough confirmations is executed
    event TransactionExecuted(uint256 indexed transactionId);

    /// @notice Gets transaction parameters
    /// @param _transactionId TransactionID
    /// @return ABIV2 encoded Transaction struct
    function getTransaction(uint256 _transactionId) external view returns (Transaction memory);

    /// @notice Allows an governor to submit and confirm a transaction.
    /// @param _destination Transaction target address, the governed contract
    /// @param _value Transaction ether value
    /// @param _data Transaction data payload
    /// @return Returns transaction ID
    function createTransaction(
        address _destination,
        uint256 _value,
        bytes memory _data
    ) external returns (uint256);

    /// @notice Allows a governor to confirm a transaction.
    /// @param _transactionId Transaction ID
    function confirmTransaction(uint256 _transactionId) external;

    /// @notice Allows a governor to revoke a confirmation for a transaction
    /// @param _transactionId Transaction ID
    function revokeConfirmation(uint256 _transactionId) external;

    /// @notice Calls the governed contract to perform administrative task
    /// @param _transactionId Transaction ID
    /// @dev Only Governance can call
    function executeTransaction(uint256 _transactionId) external;
}

A governor proposes a transaction by calling createTransaction passing the parameters. Following this, rest of the governors can choose to confirm the transaction by calling confirmTransaction. If at later point of time, a governor wishes to take back their vote, they can call revokeConfirmation. Once a transaction reaches enough confirmations, it can be executed by calling confirmTransaction. The confirmTransaction method can internally call executeTransaction after it's primary logic if required votes have been registered.

Off-chain

/// @title ERC-2767 Off-chain Governance
/// @dev ERC-165 InterfaceID: 0x32542713
interface IGovernanceOffchain {
    /// @notice Get the transactions count
    /// @dev To be used as nonce
    /// @return The transactions count
    function transactionsCount() external view returns (uint256);

    /// @notice Calls the governed contract to perform an administrative task
    /// @param _nonce Serial number of transaction
    /// @param _destination Address of contract to make a call to, should be governed contract address
    /// @param _data Input data in the transaction
    /// @param _signatures Signatures of governors collected off chain
    /// @dev The signatures are required to be sorted to prevent duplicates
    /// @dev Only Governance can call
    function executeTransaction(
        uint256 _nonce,
        address _destination,
        bytes memory _data,
        bytes[] memory _signatures
    ) external payable;
}

Anyone can take an initiative to execute a transaction by preparing a ERC-191 signed data 0x 19 00 <20 bytes address> <32 bytes nonce> <20 bytes to-address> <input data> and collect signatures from required governors. The contract logic should expect signatures to be sorted, this is to save the expenditure gas to prevent duplicate signatures. If signatures are sorted, valid, and the required number of them (or more), the governance contract will make a call to the governed contract to perform the administrative task.

Rationale

The goals of this EIP have been the following:

  • standardize API of Governance contracts for governed contracts.
  • enable existing ERC-173 ownership smart contracts to become administratively decentralised.

Backwards Compatibility

Smart contracts that are ERC-173 compliant can transfer their ownership to a Governance contract. This enables existing contracts to become compatible with ERC-2767.

However, there are some existing projects with governance implementations and most of them have custom APIs, since a standard did not exist. Such projects need to deploy a new governance contract and transfer ownership to it to be ERC-2767 compatible. Not having an ERC-2767 compatible governance contract means only that general tools might be able to populate their governance information.

Implementation

The reference implementations are available in this repository.

  1. Governance Onchain with Equal Voting Rights (Contract, Tests)
  2. Governance Offchain with Privileged Voting Rights (Contract, Tests)

Security Considerations

The signatures in off-chain governance implementation should follow recommendations of ERC-191.

0x 19 00 <20 bytes governance address> <32 bytes nonce> <20 bytes to-address> <input data>.

To prevent repeated signatures in the signatures array, it is required that caller of executeTransaction should sort the signatures based on increasing addresses.

Copyright and related rights waived via CC0.