commit 5136e51a73d95a689b6283d9ad4777cd4231647e
Author: lash <dev@holbrook.no>
Date: Wed, 3 May 2023 07:59:48 +0100
Initial commit
Diffstat:
A | solidity/Vote.sol | | | 121 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 121 insertions(+), 0 deletions(-)
diff --git a/solidity/Vote.sol b/solidity/Vote.sol
@@ -0,0 +1,121 @@
+pragma solidity ^0.8.0;
+
+// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// File-Version: 1
+// Description: Voting contract using ERC20 tokens as shares
+
+contract ERC20Vote {
+ address public token;
+
+ struct Proposal {
+ bytes32 digest;
+ uint256 voterMax;
+ address proposer;
+ bool voterVote;
+ bool valid;
+ bool active;
+ }
+
+ mapping ( address => uint256 ) voterState;
+ address[] public voters;
+ mapping ( bytes32 => mapping ( address => uint256 ) ) ack;
+ mapping ( bytes32 => mapping ( address => uint256 ) ) vote;
+
+ mapping ( address => bytes32 ) newVoterDigest;
+ Proposal []proposals;
+
+ event Vote(uint256 indexed _proposalIdx, address indexed _voter, uint256 indexed _total, uint256 _delta);
+
+ constructor(address _token) {
+ token = _token;
+ }
+
+ function propose(bytes32 _digest) public returns (uint256) {
+ require(voters.length > 1);
+
+ Proposal memory proposal;
+ uint256 idx;
+
+ proposal.digest = _digest;
+ proposal.proposer = msg.sender;
+ proposal.voterMax = voters.length - 1;
+ proposal.valid = true;
+ proposal.active = true;
+
+ idx = proposals.length;
+ proposals.push(proposal);
+ return idx;
+ }
+
+ function proposeVoter(address _voter) public returns (uint256) {
+ bytes32 voterDigest;
+ bytes memory voterDigestMaterial;
+ uint256 proposalIdx;
+
+ voterDigestMaterial = abi.encodePacked("bytes", bytes20(_voter));
+ voterDigest = sha256(voterDigestMaterial);
+ newVoterDigest[_voter] = voterDigest;
+
+ proposalIdx = propose(voterDigest);
+ proposals[proposalIdx].voterVote = true;
+ return proposalIdx;
+ }
+
+ function ackVote(uint256 _proposalIdx) public returns (bool) {
+ Proposal storage proposal;
+ uint256 balance;
+
+ proposal = proposals[_proposalIdx];
+
+ if (ack[proposal.digest][msg.sender] > 0) {
+ return false;
+ }
+ balance = getBalance(msg.sender);
+ ack[proposal.digest][msg.sender] = balance;
+ return true;
+ }
+
+ function spendVote(uint256 _proposalIdx, uint256 _amount) public returns (uint256) {
+ uint256 balance;
+ uint256 usedBalance;
+ uint256 origBalance;
+ Proposal storage proposal;
+
+ proposal = getAsActive(_proposalIdx);
+
+ balance = getBalance(msg.sender);
+
+ origBalance = ack[proposal.digest][msg.sender];
+ if (origBalance != balance) {
+ proposal.valid = false;
+ proposal.active = false;
+ return 0;
+ }
+
+ usedBalance = vote[proposal.digest][msg.sender];
+ require(usedBalance >= _amount);
+ usedBalance += _amount;
+ vote[proposal.digest][msg.sender] = usedBalance;
+ emit Vote(_proposalIdx, msg.sender, usedBalance, _amount);
+
+ return usedBalance;
+ }
+
+ function getBalance(address _voter) private returns (uint256) {
+ bytes memory v;
+ bool ok;
+
+ (ok, v) = token.call(abi.encodeWithSignature('balanceOf', _voter));
+ require(ok);
+ return abi.decode(v, (uint256));
+ }
+
+ function getAsActive(uint256 _digestIdx) private view returns (Proposal storage) {
+ Proposal storage proposal;
+
+ proposal = proposals[_digestIdx];
+ require(proposal.active);
+ return proposal;
+ }
+}