EIP1202 - Voting Standard

# Simple Summary

Propose a standard interface for voting.

# Abstract

This proposal creates a standard API for implementing voting within smart contract. This standard provides functionalities to voting as well as to view the vote result and set voting status.

# Motivation

Voting is one of the earliest example of EVM programming, and also a key to DAO/organizational governance process. We foresee many DAOs will ultimately need to leverage voting as one of the important part of their governance. By creating a voting standard for smart contract / token, we can have the following benefits

# Benefits

  1. Allow general UI and applications to be built on top of a standardized voting to allow more general user to participate, and encourage more DApp and DAO to think about their governance
  2. Allow delegate voting / smart contract voting, automatic voting
  3. Allow voting results to be recorded on-chain, in a standard way, and allow DAOs and DApps to honor the voting result programmatically.
  4. Allow the compatibility with token standard such as ERC-20 or other new standards(EIP-777) and item standard such as EIP-721
  5. Create massive potential for interoperability within Ethereum echo systems and other system.
  6. Allow setting voting deadline, allow determine on single or multiple options. Allow requiring voting orders. (trade-off is interface complexity, we might need ERC-20 approach and later a EIP-777 for advanced voting)
  7. Recording the voting with weights with token amount.
  8. Possibly allow trust-worthy privacy-safe voting and anonymous voting (with either voter address being un-associated with the vote they cast, given a list of randomized/obfuscated voting options). 8
  9. Possibly allow result in reward by voting partitipation or voting result

# Use-cases:

  1. Determine on issuing new token, issuing more token or issuing sub-token
  2. Determine on creating new item under EIP-721
  3. Determine on election on certain person or smart contract to be delegated leader for project or subproject
  4. Determine on auditing result ownership allowing migration of smart contract proxy address

# Specifications

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;


/// @title Core interface of ERC1202: A list of *REQUIRED* methods and events for 
///        a contract to be considered conforming to ERC1202. 
/// 
/// @author Zainan Victor Zhou <zzn@zzn.im>
/// 
/// @dev Each ERC1202 contract is a cluster of issues being voted on, or done voted.
///      Any contract of ERC1202 **MUST** implement ALL the following methods and events.
/// 
///      Each *issue* is identified with an `issueId`,
///      For any given `issue`, each available option in that issue is
///      identified with an `optionId`.
interface ERC1202Core {
    
    /// @dev Cast a vote for an issue with `issueId` for option with `optionId`
    /// @param _issueId: the issue this vote is casting on.
    /// @param _optionIds: an *ordered* array of the options being casted for the issue.
    ///   Whenever referring to the options as a whole, the order MUST be maintained.
    /// @return a boolean if TRUE means the vote is casted successfully. 
    function vote(uint _issueId, uint[] memory _optionIds) external returns (bool);

    /// @dev Query the top ranked options of an issue given issueId and 
    ///      a limit of max number of top options.
    /// @param _issueId: the issue being queried for the top options.
    /// @param _limit: the max number of top options the caller expect to return.
    /// @return an ordered list of the top options for given issueId and limit, 
    ///         where the first in array is the most favorite one, and the last in 
    ///         array is the least favorite one among the list.
    ///         Specifically, WHEN limit = 0, returns the default length of winning
    ///         options in their ranking in an issue. 
    function topOptions(
        uint _issueId, uint _limit
        ) external view returns (uint[] memory);
    
    /// @dev This event is emitted when a vote has been casted.
    /// @param issueId the issue the vote is being cased on.
    /// @param optionIds an ordered list of the options the vote is casting for.
    event OnVote(uint indexed issueId, uint[] optionIds, address indexed voterAddr);

}

/// @title Metadata interface for ERC1202: A list of *RECOMMENDED* methods and events for 
///        a contract to be considered conforming to ERC1202. 
///
/// @author Zainan Victor Zhou <zzn@zzn.im>
interface ERC1202Metadata {

    /// @notice A descriptive text for an issue in this contract.
    function issueText() external view returns (string memory _text);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given issue.
    /// @dev Throws if `_issueId` is not a valid issue; 
    ///      URIs are defined in RFC 3986. 
    function issueURI(uint256 _issueId) external view returns (string memory _uri);

    /// @notice A descriptive text for an option in an issue in this contract.
    function optionText(uint _issueId, uint _optionId) external view returns (string memory _text);
    
    /// @notice A distinct Uniform Resource Identifier (URI) for a given option in a given issue.
    /// @dev Throws if `_issueId` is not a valid option-issue combination; 
    ///      URIs are defined in RFC 3986. 
    function optionURI(uint _issueId, uint _optionId) external view returns (string memory _uri);
}

/// @title Status interface for ERC1202: A list of *RECOMMENDED* methods and events for 
///        a contract to be considered conforming to ERC1202. 
///
/// @author Zainan Victor Zhou <zzn@zzn.im>
interface ERC1202Status {
    
    /// @dev This event is emitted when an issue has changed status.
    /// @param issueId the issue about which a status change has happened.
    /// @param isOpen the status
    event OnStatusChange(uint indexed issueId, bool indexed isOpen);
    
    /// @dev Sets the status of a issue, e.g. open for vote or closed for result.
    /// @param _issueId the issue of Status being set.
    /// @param _isOpen the status to set.
    /// @return _success whether the setStatus option succeeded.
    function setStatus(uint _issueId, bool _isOpen) external returns (bool _success);
    
    /// @dev Gets the status of a issue, e.g. open for vote or closed for result.
    /// @param _issueId the issue of Status being get.
    /// @return _isOpen the status of the issue.
    function getStatus(uint _issueId) external view returns (bool _isOpen);
    
    /// @dev Retrieves the ranked options voted by a given voter for a given issue.
    /// @param _issueId the issue
    /// @param _voter the aaddres of voter.
    /// @return _optionIds the ranked options voted by voter.
    function voteOf(uint _issueId, address _voter) external view returns (uint[] memory _optionIds);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

# Rationale

We made the following design decisions and here are the rationales.

  • Granularity and Anonymity:: We created a view function ballotOf primarily making it easier for people to check the vote from certain address. This has the following assumptions:

    • It's possible to check someone's vote directly given an address. If implementor don't want to make it so easiy, they can simply reject all calls to this function. We want to make sure that we support both anonymous voting an non-anonymous voting. However since all calls to a smart contract is logged in block history, there is really no secrecy unless done with cryptography tricks. I am not cryptography-savvy enough to comment on the possibility. Please see "Second Feedback Questions 2018" for related topic.

    • It's assumes for each individual address, they can only vote for one decision. They can distribute their available voting power into more granular level. If implementor wants allow this, they ask the user to create another wallet address and grant the new address certain power. For example, a token based voting where voting weight is determined by the amount of token held by a voter, a voter who wants to distribute its voting power in two different option(option set) can transfer some of the tokens to the new account and cast the votes from both accounts.

  • Weight: We assume there are weight of votes and can be checked by calling weightOf(address addr), and the weight distribution is either internally determined or determined by constructor. However we have not been considering updating the weight distribution. Please comment on this design decision as we want to learn how likely an implementor would want to be able to update the voting weight distributions.

# Backward Compatibility

There is no backward compatibility issue we are aware of.

# Security Considerations

EIP-1202 is a voting standard. We expect the voting standard to be used in connection with other contracts such as token distributions, conducting actions in consensus or on behalf of an entity, multi-signature wallets, etc.

The major security consideration is to ensure only using the standard interface for performing downstream actions or receiving upstream input (vote casting). We expect future audit tool to be based on standard interfaces.

It's also important to note as discussed in this standard that for the sake of simplicity, EIP-1202 is kept in the very basic form. It can be extended to support many different implementation variations. Such variations might contain different assumptions of the behavior and interpretation of actions. One example would be: What does it mean if someone votes multiple times through vote?

  • Would that mean the voter is increasing their weight, or
  • vote multiple options in the meanwhile, or
  • Does the latter vote override the previous vote?

Because of the flexible nature of voting, we expect many subsequent standards need to be created as an extension of EIP-1202. We suggest any extension or implementations of this standard be thoroughly audited before included in large scale or high asset volume applications.

The third consideration is non-trivialness. Some voting applications assume anonymity, randomness, time-based deadline, ordering, etc, these requirements in Ethereum are known to be non-trivial to achieve. We suggest any applications or organizations rely on audited and time-proven shared libraries when these requirements need to be enforced in their applications.

The fourth consideration is potential abuse. When voting is standardized and put on contract, it is possible to write another contract that rewards a voter to vote in a certain way. It creates potential issues of bribery and conflict of interest abuse that is previously hard to implement.

# Work Directory

The drafting and revision of EIP-1202 is conducted at GitHub/xinbenlv/eip-1202 (opens new window)

Copyright and related rights waived via CC0 (opens new window).

▲ Powered by Vercel