Untitled

 avatar
unknown
plain_text
a year ago
3.4 kB
8
Indexable
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract WeightedVoting {
    using EnumerableSet for EnumerableSet.AddressSet;

    ERC20 public token;
    uint public constant maxSupply = 1000000;

    error TokensClaimed();
    error AllTokensClaimed();
    error NoTokensHeld();
    error QuorumTooHigh(uint quorum);
    error AlreadyVoted();
    error VotingClosed();

    struct Issue {
        EnumerableSet.AddressSet voters;
        string issueDesc;
        uint votesFor;
        uint votesAgainst;
        uint votesAbstain;
        uint totalVotes;
        uint quorum;
        bool passed;
        bool closed;
    }

    Issue[] private issues;

    enum Votes { AGAINST, FOR, ABSTAIN }    

    constructor(address _tokenAddress) {
        token = ERC20(_tokenAddress);
        issues.push();
        token.transferFrom(msg.sender, address(this), maxSupply);
    }

    function claim() external {
        if (token.balanceOf(msg.sender) > 0){
            revert TokensClaimed();
        }

        if (issues[0].totalVotes >= maxSupply) {
            revert AllTokensClaimed();
        }

        token.transfer(msg.sender, 100);
        issues[0].totalVotes += 100;
        issues[0].voters.add(msg.sender);
    }

    function createIssue(string memory _issueDesc, uint _quorum) external returns (uint) {
        if(token.balanceOf(msg.sender) == 0){
            revert NoTokensHeld();
        }

        if(_quorum >= token.totalSupply()){
            revert QuorumTooHigh(_quorum);
        }

        uint newIssueId = issues.length;
        issues.push();
        Issue storage newIssue = issues[newIssueId];
        newIssue.issueDesc = _issueDesc;
        newIssue.quorum = _quorum;
        return newIssueId;
    }

    function getIssue(uint _id) external view returns (string memory, uint, uint, uint, uint, bool, bool) {
        if(_id > issues.length){
            revert("Issue does not exist");
        }
        Issue storage issue = issues[_id];
        return (issue.issueDesc, issue.votesFor, issue.votesAgainst, issue.votesAbstain, issue.totalVotes, issue.passed, issue.closed);
    }

    function vote(uint _issueId, Votes _vote) external {
        Issue storage issue = issues[_issueId];

        if(issue.closed){
            revert VotingClosed();
        }
        
        if(issue.voters.contains(msg.sender)){
            revert AlreadyVoted();
        }
        
        uint senderBalance = token.balanceOf(msg.sender);
        issue.voters.add(msg.sender);
        
        if (_vote == Votes.FOR) {
            issue.votesFor += senderBalance;
        } else if (_vote == Votes.AGAINST) {
            issue.votesAgainst += senderBalance;
        } else if (_vote == Votes.ABSTAIN) {
            issue.votesAbstain += senderBalance;
        } else {
            revert("Invalid vote option");
        }
        
        issue.totalVotes += senderBalance;

        if (issue.totalVotes >= issue.quorum) {
            issue.closed = true;
            if (issue.votesFor > issue.votesAgainst) {
                issue.passed = true;
            }
        }
    }
}
Editor is loading...
Leave a Comment