Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-1417: Poll Standard

Authors Chaitanya Potti (@chaitanyapotti), Partha Bhattacharya (@pb25193)
Created 2018-09-16
Discussion Link https://github.com/ethereum/EIPs/issues/1417
Requires EIP-165, EIP-1261

Note to Readers

  1. We have created a couple of implementations of polls for varied use cases. Please refer to them here

Simple Summary

A standard interface for Polls to be used with EIP-1261 (MVT).

Abstract

The following standard allows for the implementation of a standard API for polls to be used with MVTs (refer EIP-1261). The standard provides basic functionality to vote, unvote, tally votes, get voter turnout, and a lot more. The poll standard attempts to modularize blockchain voting by breaking down a poll into 4 crucial building blocks: voterbase qualification, vote weight calculation, vote consequences, and vote tallying. By creating a common interface for polls that have different kinds of building blocks, the poll standard makes it possible to make interactive front end applications which can seamlessly get data from a poll contract in order to bring transparency into consensus and decision making on the blockchain.

We considered the usage of polls with MVTs because MVTs serve as a permissioning mechanism. The manual permissioning of polls allows for vote weightage functions to take up several shapes and forms. Hence the voterbase function applies several logical checks on the vote sender to confirm that they are member(see EIP 1261) of a certain entity or combination of entities. For the specification of the nature of voting, we define the vote weight function. The vote weight function decides how much of vote share each voter will receive and this can be based on several criteria, some of which are listed below in this article. There are certain kinds of polls that enforce certain consequences on the voter, for example a poll may require a voter to lock in a certain amount of tokens, or require the voter to pay a small fee. These on-chain consequences can be coded into the consequence module of the poll standard. Finally, the last module is where the votes are added. A ballot for each candidate is updated whenever relevant, depending on the vote value, and the corresponding NoV count(number of voters). This module is common for most polls, and is the most straightforward. Polls may be time bound, ie. having a finish time, after which no votes are recorded, or be unbound, such that there is no finish time. The following are some examples of specific polls which leverage the flexibility of the poll standard, and it is possible to come up with several others:

  • Plurality Voting: The simplest form of voting is when you want all eligible voters to have one vote per person. This is the simplest to code, as the vote weight is 1, and there is no vote consequence. The only relevant module here is the voterbase, which can be categorized by one or more MVT contracts.
  • Token proportional voting: This kind of a poll is actually possible without the use of a voterbase function, because the vote weight function having token proportionality automatically rules out addresses which don’t hold the appropriate ERC - 20/ ERC - 777 token. However the voterbase function may be leveraged to further permission the system and give voting rights only to a fixed subset of token holders.
  • Capped Token Proportional Voting: This is a modified version of the previous example, where each voter is given proportional vote share only until a certain limit of token ownership. After exceeding that limit, holding more coins does not add more vote share. This format leverages the voterbase module effectively, disallowing people from spreading their coins across multiple addresses by allowing the admin to control which addresses can vote.
  • Delegated Voting: Certain polls may allow voters to delegate their votes to other voters. This is known as delegated voting or liquid democracy. For such a poll, a complicated vote weight function is needed, and a data structure concerning the voterbase is also required. A consequence of voting here would be that a user cannot delegate, and a consequence of delegating is that a user cannot vote. Sample implementation of polls contains an example of this vote scheme.
  • Karma Based Voting: A certain form of poll may be based on weightage from digital respect. This digital respect would be like a simple upvote from one member of voterbase to another. A mapping of mappings along with an appropriate vote weight function can serve this purpose. Sample implementation has an example.
  • Quadratic voting: A system where each vote is associated with a fee, and the fee is proportional to the square of the vote weight that the voter wants. This can be designed by applying a vote weight based on the transaction message, and then charging a fee in the vote consequence module.

The poll standard is intended to be a smart contract standard that makes poll deployment flexible, transparent and accessible.

Motivation

A standard interface allows any user or applications to work with any Poll contract on Ethereum. We provide for simple ERC-1417 smart contracts. Additional applications are discussed below.

This standard is inspired by the lack of governance tools in the blockchain space. Whenever there is a consensus collection exercise, someone goes ahead and deploys some kind of poll, and there is no standard software for accessing the data on the poll. For an end user who is not a developer, this is a real problem. The poll, which might be fully transparent, appears to be completely opaque to a common user who does not understand blockchain. In order for developers to build applications for interacting with and accessing poll data, and for poll deployers to have ready application level support, there must be a standardization of poll interfaces.

This realization happened while conducting market research on DAICOs. The first ever DAICO, Abyss, had far from optimal user experience, and abysmal transparency. Since then, we have been working on a poll standard. During the process, we came across EIP 1202, the voting standard, and found that the discussion there had already diverged from our thoughts to an extent that it made sense to publish a separate proposal altogether. Some of the benefits brought by the poll standard - EIP 1417 aims to offer some additional benefits.

  1. Modularization: EIP 1417 modularizes the code present in the poll standard into 4 major building blocks based on functionality. These are: voterbase logic, vote weight calculation, vote consequence processing, and tallying module. This makes it easy for developers to change parts of a poll without disrupting other parts, and also helps people understand better, code written in the same format by other people.

  2. Permissioning: Permissioning is an important aspect of polls, and is missing in most poll proposals so far, on the blockchain. For some reason, most blockchain based polls seem to consider token holding as the only way to permission a poll. However this hampers flexibility, and hence our poll standard is leveraging EIP 1261 in order to clear the permissioning hurdle. Not only does it allow for more creative poll structures in terms of vote weightage, but even improves the flexibility in permissioning by allowing developers to combine several entities and read attributes from entities.

  3. Flexibility: The vote weight module of the poll standard can be used effectively to design various kinds of poll contracts which function differently and are suited to different environments. Some examples are quadratic voting, karma voting, delegated voting, token based voting, and one person one vote systems. These schemes are possible due to the separation of voterbase creation and vote weight calculation.

  4. NoV Counts: Several weighted polls have struggled to provide proper transparency because they only show the final result without enough granularity. This is because they do not store the number of voters that have voted for each proposal, and only store the total accrued vote for each option. EIP 1417 solves this by additionally recording number of voters(NoV) in each proposal. This NoV count is redundant in the case of one person one vote, but elsewhere, it is helpful in figuring out concentration of power. This ensures that malicious parties can be traced to a larger extent.

  5. Event Logging: The poll standard logs an event during a successful vote, unsuccessful vote, and a successful unvote. This is being done so that in the event of a malicious admin removing real members or adding fake members, communities can build tools in order to perform advanced audits and simulate results in the absence of the malicious attack. Such advanced features are completely absent in most polls, and hence, it is hard to investigate such polls.

  6. Pollscan.io: The Electus foundation is working on a web based application for accessing and interacting with poll data on the blockchain, it will be deployed on the domain name www.pollscan.io in the coming months.

All that being said, we are very excited to share our proposal with the community and open up to suggestions in this space.

Benefits

  1. Building applications (pollscan.io) on top of a standardized voting interface enables transparency and encourage more DAO/DAICO’s to act responsibly in terms of governance
  2. Create Action contracts which take actions programmatically based on the result of a poll
  3. Allow the compatibility with token standard such as ERC-20 or (./eip-777.md)) and membership standard such as EIP-1261
  4. Flexibility allows for various voting schemes including but not limited to modern schemes such as PLCR Voting

Use-cases:

Polls are useful in any context of collective decision making, which include but aren’t limited to:

  1. Governing public resources, like ponds, playgrounds, streets etc
  2. Maintaining fiscal policy in a transparent consensus driven manner
  3. Governing crowdfunded projects - refer DAICO, Vitalik Buterin
  4. Implementation of Futarchy
  5. Decision making in political parties, and municipal corporations
  6. Governing expenditure of a cryptocurrency community

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Every ERC-1417 compliant contract must implement the ERC1417 and ERC165 interfaces (subject to “caveats” below):

/// @title ERC-1417 Poll Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1417.md
///  Note: the ERC-165 identifier for this interface is 0x4fad898b.
interface IPoll {
    /// @dev This emits when a person tries to vote without permissions. Useful for auditing purposes.
    ///  E.g.: To prevent an admin to revoke permissions; calculate the result had they not been removed.
    /// @param _from User who tried to vote
    /// @param _to the index of the proposal he voted to
    /// @param voteWeight the weight of his vote
    event TriedToVote(address indexed _from, uint8 indexed _to, uint voteWeight);

    /// @dev This emits when a person votes successfully
    /// @param _from User who successfully voted
    /// @param _to the index of the proposal he voted to
    /// @param voteWeight the weight of his vote
    event CastVote(address indexed _from, uint8 indexed _to, uint voteWeight);

    /// @dev This emits when a person revokes his vote
    /// @param _from User who successfully unvoted
    /// @param _to the index of the proposal he unvoted
    /// @param voteWeight the weight of his vote
    event RevokedVote(address indexed _from, uint8 indexed _to, uint voteWeight);

    /// @notice Handles the vote logic
    /// @dev updates the appropriate data structures regarding the vote.
    ///  stores the proposalId against the user to allow for unvote
    /// @param _proposalId the index of the proposal in the proposals array
    function vote(uint8 _proposalId) external;

    /// @notice Handles the unvote logic
    /// @dev updates the appropriate data structures regarding the unvote
    function revokeVote() external;

    /// @notice gets the proposal names
    /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the proposal list
    /// @return the list of names of proposals
    function getProposals() external view returns (bytes32[]);

    /// @notice returns a boolean specifying whether the user can vote
    /// @dev implement logic to enable checks to determine whether the user can vote
    ///  if using eip-1261, use protocol addresses and interface (IERC1261) to enable checking with attributes
    /// @param _to the person who can vote/not
    /// @return a boolean as to whether the user can vote
    function canVote(address _to) external view returns (bool);

    /// @notice gets the vote weight of the proposalId
    /// @dev returns the current cumulative vote weight of a proposal
    /// @param _proposalId the index of the proposal in the proposals array
    /// @return the cumulative vote weight of the specified proposal
    function getVoteTally(uint _proposalId) external view returns (uint);

    /// @notice gets the no. of voters who voted for the proposal
    /// @dev use a struct to keep a track of voteWeights and voterCount
    /// @param _proposalId the index of the proposal in the proposals array
    /// @return the voter count of the people who voted for the specified proposal
    function getVoterCount(uint _proposalId) external view returns (uint);

    /// @notice calculates the vote weight associated with the person `_to`
    /// @dev use appropriate logic to determine the vote weight of the individual
    ///  For sample implementations, refer to end of the eip
    /// @param _to the person whose vote weight is being calculated
    /// @return the vote weight of the individual
    function calculateVoteWeight(address _to) external view returns (uint);

    /// @notice gets the leading proposal at the current time
    /// @dev calculate the leading proposal at the current time
    ///  For practical reasons, limit proposal count to 32.
    /// @return the index of the proposal which is leading
    function winningProposal() external view returns (uint8);

    /// @notice gets the name of the poll e.g.: "Admin Election for Autumn 2018"
    /// @dev Set the name in the constructor of the poll
    /// @return the name of the poll
    function getName() external view returns (bytes32);

    /// @notice gets the type of the Poll e.g.: Token (XYZ) weighted poll
    /// @dev Set the poll type in the constructor of the poll
    /// @return the type of the poll
    function getPollType() external view returns (bytes32);

    /// @notice gets the logic to be used in a poll's `canVote` function
    ///  e.g.: "XYZ Token | US & China(attributes in erc-1261) | Developers(attributes in erc-1261)"
    /// @dev Set the Voterbase logic in the constructor of the poll
    /// @return the voterbase logic
    function getVoterBaseLogic() external view returns (bytes32);

    /// @notice gets the start time for the poll
    /// @dev Set the start time in the constructor of the poll as Unix Standard Time
    /// @return start time as Unix Standard Time
    function getStartTime() external view returns (uint);

    /// @notice gets the end time for the poll
    /// @dev Set the end time in the constructor of the poll as Unix Time or specify duration in constructor
    /// @return end time as Unix Standard Time
    function getEndTime() external view returns (uint);

    /// @notice returns the list of entity addresses (eip-1261) used for perimissioning purposes.
    /// @dev addresses list can be used along with IERC1261 interface to define the logic inside `canVote()` function
    /// @return the list of addresses of entities
    function getProtocolAddresses() external view returns (address[]);

    /// @notice gets the vote weight against all proposals
    /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the vote tally list
    /// @return the list of vote weights against all proposals
    function getVoteTallies() external view returns (uint[]);

    /// @notice gets the no. of people who voted against all proposals
    /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the vote count list
    /// @return the list of voter count against all proposals
    function getVoterCounts() external view returns (uint[]);

    /// @notice For single proposal polls, returns the total voterbase count.
    ///  For multi proposal polls, returns the total vote weight against all proposals
    ///  this is used to calculate the percentages for each proposal
    /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the voter base denominator
    /// @return an integer which specifies the above mentioned amount
    function getVoterBaseDenominator() external view returns (uint);
}

Caveats

The 0.4.24 Solidity interface grammar is not expressive enough to document the ERC-1417 standard. A contract which complies with ERC-1417 MUST also abide by the following:

  • Solidity issue #3412: The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: payable, implicit nonpayable, view, and pure. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a payable function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.24 is that you can edit this interface to add stricter mutability before inheriting from your contract.
  • Solidity issue #2330: If a function is shown in this specification as external then a contract will be compliant if it uses public visibility. As a workaround for version 0.4.24, you can edit this interface to switch to public before inheriting from your contract.

If a newer version of Solidity allows the caveats to be expressed in code, then this EIP MAY be updated and the caveats removed, such will be equivalent to the original specification.

Rationale

As the poll standard is built with the intention of creating a system that allows for more transparency and accessibility of governance data, the design choices in the poll standard are driven by this motivator. In this section we go over some of the major design choices, and why these choices were made:

  1. Event logging: The logic behind maintaining event logs in the cases of:

    • Cast Vote
    • Unvote
    • Failed Vote is to ensure that in the event of a manipulated voterbase, simple off chain checks can be performed to audit the integrity of the poll result.
  2. No poll finish trigger: There was a consideration of adding functions in the poll which execute after completion of the poll to carry out some pre-decided logic. However this was deemed to be unnecessary - because such an action can be deployed in a separate contract which simply reads the result of a given poll, and against the spirit of modularity, because no actions can be created after the poll has been deployed. Also, such functions would not be able to combine the results of polls, and definitely would not fit into polls that do not have an end time.

  3. Allow for unbound polls: The poll standard, unlike other voting standard proposals, does not force polls to have an end time. This becomes relevant in some cases where the purpose of a poll is to have a live register of ongoing consensus. Some other use cases come into picture when you want to deploy a set of action contracts which read from the poll, and want to be able to execute the action contract whenever a poll reaches a certain threshold, rather than waiting for the end of the poll.

  4. Modularization: There have been opinions in the Ethereum community that there cannot exist a voting standard, because voting contracts can be of various types, and have several shapes and forms. However we disagree, and make the case that modularization is the solution. While different polls may need different logic, they all need consistent end points. All polls need to give out results along with headcounts, all polls should have event logs, all polls should be examinable with frontend tools, and so on. The poll standard is not a statement saying “all polls should be token based” or any such specific system. However the poll standard is a statement saying that all polls should have a common access and modification protocol - this will enable more apps to include governance without having to go through the trouble of making customers start using command line.

Having explained our rationale, we are looking forward to hearing from the community some thoughts on how this can be made more useful or powerful.

Gas and Complexity (regarding the enumeration for proposal count)

This specification contemplates implementations that contain a sample of 32 proposals (max up to blockgaslimit). If your application is able to grow and needs more than 32 proposals, then avoid using for/while loops in your code. These indicate your contract may be unable to scale and gas costs will rise over time without bound

Privacy

Personal information: The standard does not put any personal information on to the blockchain, so there is no compromise of privacy in that respect.

Community Consensus

We have been very inclusive in this process and invite anyone with questions or contributions into our discussion. However, this standard is written only to support the identified use cases which are listed herein.

Test Cases

Voting Standard includes test cases written using Truffle.

Implementations

Voting Standard – a reference implementation

  • MIT licensed, so you can freely use it for your projects
  • Includes test cases
  • Also available as a npm package - npm i electusvoting

References

Standards

Issues

  1. The Original ERC-1417 Issue. https://github.com/ethereum/eips/issues/1417
  2. Solidity Issue #2330 – Interface Functions are Axternal. https://github.com/ethereum/solidity/issues/2330
  3. Solidity Issue #3412 – Implement Interface: Allow Stricter Mutability. https://github.com/ethereum/solidity/issues/3412
  4. Solidity Issue #3419 – Interfaces Can’t Inherit. https://github.com/ethereum/solidity/issues/3419

Discussions

  1. ERC-1417 (announcement of first live discussion). https://github.com/ethereum/eips/issues/1417

Voting Implementations and Other Projects

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Chaitanya Potti (@chaitanyapotti), Partha Bhattacharya (@pb25193), "ERC-1417: Poll Standard [DRAFT]," Ethereum Improvement Proposals, no. 1417, September 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1417.