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*.