evm-tokenvote

Voting machine using ERC20 tokens as votes.
Info | Log | Files | Refs | README

README.md (9512B)


      1 # Overview
      2 
      3 This smart contract enables voting on proposals using ERC20 tokens.
      4 
      5 ## Tooling
      6 
      7 Tests and tools are implemented using the
      8 [chainlib-eth](https://pypi.org/project/chainlib-eth) python package.
      9 
     10 To learn more about the individual CLI tools mentioned in this document,
     11 please refer to their individual man pages. The man pages should be
     12 installed as part of the python packages.
     13 
     14 ## Interoperability
     15 
     16 The `evm-tokenvote` contract implements the `cic-contracts:TokenVote`
     17 interface.
     18 
     19 ## Publishing the contract
     20 
     21 In python, the smart contract can be published using the `constructor`
     22 method of the `evm_tokenvote.Voter` class.
     23 
     24 Also, constructor bytecode can be generated on the command-line using
     25 `chainlib-gen`. Thereafter, `eth-gen` can be used to publish the
     26 contract through an rpc endpoint:
     27 
     28     # Make sure evm_tokenvote is in your PYTHONPATH
     29     $ chainlib-gen evm_tokenvote create --token_address <token_address> > data.bin
     30     $ eth-gas -p <rpc_url> -y <json_keyfile> [ --passphrase-file <file_with_keyfile_passphrase> ] --data data.bin -i <evm:chain:chain_id:common_name> -s -w 0
     31 
     32 The `token_address` argument **MUST** be an existing and valid `ERC20`
     33 contract.
     34 
     35 # Proposal
     36 
     37 Proposals are created with a 32-byte description.
     38 
     39 How the description should be interpreted is up to the client
     40 application.
     41 
     42 A proposal may be a binary (yes or no) vote, or define a number of
     43 different options to choose from.
     44 
     45 ## Parameters
     46 
     47 The arguments required to create a proposal depends on the type of
     48 proposal to create.
     49 
     50 ### Deadline
     51 
     52 The deadline must be defined for all proposals.
     53 
     54 It is defined as a period of number of blocks during which votes may be
     55 made.
     56 
     57 If a vote has been completed before the deadline is over, the result
     58 will be available immediately.
     59 
     60 The contract does not check the sanity of the deadline value. For
     61 example, setting the deadline to `1` block wait will be useless as
     62 voting will expire immediately.
     63 
     64 ## Target vote
     65 
     66 The target vote must be defined for all proposals.
     67 
     68 This defines the minimum participation in the vote. It is defined as
     69 parts-per-million of the total supply of tokens.
     70 
     71 For example. a value of `500000` will require 50% of all tokens in the
     72 vote. A value of `1000000` will require the full supply.
     73 
     74 ## Options
     75 
     76 A proposal may define one or more options for voters to choose between.
     77 
     78 Options are defined as 32-byte hexadecimal values. As with the
     79 description, it is up to the client application to decide how to
     80 interpret the values.
     81 
     82 ## Creating a proposal
     83 
     84 To create a proposal without options:
     85 
     86     # solidity:
     87     function propose(bytes32 _description, uint256 _blockWait, uint24 _targetVotePpm) public returns (uint256);
     88 
     89     # chainlib-python:
     90     def propose(self, contract_address, sender_address, description, block_deadline, tx_format=TxFormat.JSONRPC, id_generator=None)
     91 
     92     # eth-encode CLI:
     93     $ eth-encode --mode tx --signature propose -e <voter_contract_address> -y <keyfile_json> a:<token_address> u:<blocks_until_deadline> u:<target_vote_ppm>
     94 
     95 To create a proposal with options:
     96 
     97     solidity:
     98     function proposeMulti(bytes32 _description, uint256 _blockWait, uint24 _targetVotePpm) public returns (uint256);
     99 
    100     chainlib-python:
    101     def propose(self, contract_address, sender_address, description, block_deadline, options=[<options_hex>, ...])
    102 
    103 (Unfortunately, `eth-encode` does not currently support dynamic array
    104 arguments.)
    105 
    106 # Voting
    107 
    108 Votes are defined in magnitudes of ERC20 tokens.
    109 
    110 A vote will transfer ERC20 tokens to the custody of the smart contract
    111 for the duration of the voting.
    112 
    113 The smart contract uses the `transferFrom` method to transfer tokens. It
    114 is the caller’s responsibility to make the necessary token allowance
    115 (using `approve`). Votes with insufficient allowance will fail.
    116 
    117 ## Proposal context
    118 
    119 Votes are always cast on the oldest proposal the has not been completed.
    120 
    121 ## Options
    122 
    123 If multiple options exist, the token holder may freely distribute votes
    124 between the options.
    125 
    126 ## Cancellation votes
    127 
    128 In both proposals with and without options, votes can be cast to cancel
    129 the proposal.
    130 
    131 For proposals with no or one option, the *cancel* amounts to a vote
    132 against the proposal.
    133 
    134 For proposals with two or more options, the *cancel* amounts to a vote
    135 against proposal and its all options.
    136 
    137 ## How to vote
    138 
    139 In each case, make sure the necessary *allowance* has been successfully
    140 executed.
    141 
    142 For proposals without options, a simplified method can be called:
    143 
    144     # solidity:
    145     function vote(uint256 _value) public returns (bool);
    146 
    147 
    148     # chainlib-python:
    149     def vote(self, contract_address, sender_address, value)
    150 
    151 
    152     # eth-encode CLI:
    153     $ eth-encode --mode tx --signature vote -e <voter_contract_address> -y <keyfile_json> u:<value>
    154 
    155 For proposal with options, the call is slightly more verbose:
    156 
    157     # solidity:
    158     function voteOption(uint256 _optionIndex, uint256 _value) public returns (bool);
    159 
    160 
    161     # chainlib-python:
    162     def vote(self, contract_address, sender_address, option=<option_index>, value)
    163 
    164 
    165     # eth-encode CLI:
    166     $ eth-encode --mode tx --signature voteOption -e <voter_contract_address> -y <keyfile_json> u:<option_index> u:<value>
    167 
    168 To cast votes for cancellation, the call will be:
    169 
    170     # solidity:
    171     function voteCancel(uint256 _value) public returns (bool);
    172 
    173 
    174     # chainlib-python:
    175     def vote_cancel(self, contract_address, sender_address, value)
    176 
    177 
    178     # eth-encode CLI:
    179     $ eth-encode --mode tx --signature voteCancel -e <voter_contract_address> -y <keyfile_json> u:<value>
    180 
    181 # Results
    182 
    183 A proposal vote is completed if either of the following are true:
    184 
    185 - The deadline has been reached
    186 
    187 - Proposal contains zero or one options, and target participation has
    188   been reached.
    189 
    190 - Cancellation votes have the majority.
    191 
    192 ## Finalizing the proposal
    193 
    194 Once a proposal vote has been completed, the proposal must be explicitly
    195 finalized.
    196 
    197 Finalization analyzes the results of the vote, and marks the proposal
    198 state accordingly.
    199 
    200 It also moves the proposal cursor to activate the next proposal in the
    201 queue.
    202 
    203 Finally, it releases the ERC20 tokens used for the vote.
    204 
    205 Finalization is performed using the `finalize()` contract method. It
    206 will fail if used before the proposal vote has been *completed*.
    207 
    208 ### Enhanced results
    209 
    210 The optional method `scan(uint256 _proposalIndex, uint256 _count` can be
    211 called on a completed proposal to further analyze the results of the
    212 vote.
    213 
    214 In the current state of the contract, it will iterate the options of the
    215 proposal, and mark the state as `TIED` if two or more options have the
    216 same amount of votes.
    217 
    218 This method may be called any time after proposal has been completed
    219 (even before `finalize()`. The proposal is identified by the
    220 `_proposalIndex` parameter, where the index is the order of addition of
    221 the proposal.
    222 
    223 The `_count` parameter limits the amount of options that will be
    224 scanned. A consecutive call will start at the option where the previous
    225 left off.
    226 
    227 ## Recovering tokens
    228 
    229 Before a new vote can take place, the ERC20 tokens used in previous
    230 voting must be withdrawn.
    231 
    232 Withdrawal is performed using the `withdraw()` contract method. It will
    233 fail if used before the proposal vote has been *finalized*.
    234 
    235 # Proposal states
    236 
    237 | label          | bit value | description                                                                                                                                                          |
    238 |----------------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
    239 | INIT           | 1         | Proposal has been initiated. It is used to disambiguate a proposal struct that has not yet been added, due to the ambiguity of the default struct value in solidity. |
    240 | `FINAL`        | 2         | `finalize()` has been successfully called.                                                                                                                           |
    241 | `SCANNED`      | 4         | `scan(...)` has been successfully called for all available options.                                                                                                  |
    242 | `INSUFFICIENT` | 8         | Voting participation did not been the required target vote before the deadline.                                                                                      |
    243 | `TIED`         | 16        | Proposal contains two or more options, and two or more options have received the same amount of votes before the deadline.                                           |
    244 | `SUPPLYCHANGE` | 32        | The token supply changed between when the proposal was added and voting ended. If supply is protected, this has also `CANCELLED` the vote.                           |
    245 | `IMMEDIATE`    | 64        | Voting was completed before the deadline.                                                                                                                            |
    246 | `CANCELLED`    | 128       | Interpretation depends on context. See below.                                                                                                                        |
    247 
    248 Interpreting `CANCELLED`:
    249 
    250 - With `SUPPLYCHANGE`, this means that vote has been invalidated and
    251   should be discarded.
    252 
    253 - If proposal has options, it should be interpreted as that the
    254   existence of the proposal itself has been *rejected*.
    255 
    256 - With no or one option, it should be interpreted as the proposal has
    257   been *defeated*.