Skip to content

putracuy/Deplobase

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

// SPDX-License-Identifier: MIT pragma solidity ^0.8.17;

// ERC20 token standard import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// EnumerableSet library for managing sets of addresses import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract WeightedVoting is ERC20 { using EnumerableSet for EnumerableSet.AddressSet;

// --- State Variables ---
uint256 public constant maxSupply = 1_000_000;

// Tracks which addresses have claimed tokens to prevent multiple claims.
mapping(address => bool) private hasClaimed;

// --- Errors ---
error TokensClaimed();
error AllTokensClaimed();
error NoTokensHeld();
error QuorumTooHigh(uint256 proposedQuorum);
error AlreadyVoted();
error VotingClosed();

// --- Structs & Enums ---
// The main data structure for each voting issue.
// The order of these variables is crucial for the unit tests.
struct Issue {
    EnumerableSet.AddressSet voters;
    string issueDesc;
    uint256 votesFor;
    uint256 votesAgainst;
    uint256 votesAbstain;
    uint256 totalVotes;
    uint256 quorum;
    bool passed;
    bool closed;
}

// A separate struct to return issue data, as EnumerableSet cannot be returned.
struct IssueData {
    address[] voters;
    string issueDesc;
    uint256 votesFor;
    uint256 votesAgainst;
    uint256 votesAbstain;
    uint256 totalVotes;
    uint256 quorum;
    bool passed;
    bool closed;
}

// A mapping from an issue ID to the issue data.
Issue[] private issues;

// Enum to represent the different vote options.
enum Vote {
    AGAINST,
    FOR,
    ABSTAIN
}

// --- Constructor ---
constructor() ERC20("WeightedVoting", "WV") {
    // As per the exercise requirements, we add a dummy element at index 0.
    // This ensures all valid issues will start at index 1.
    issues.push();
}

// --- Functions ---
function claim() public {
    if (hasClaimed[msg.sender]) {
        revert TokensClaimed();
    }
    if (totalSupply() >= maxSupply) {
        revert AllTokensClaimed();
    }

    hasClaimed[msg.sender] = true;
    _mint(msg.sender, 100);
}

function createIssue(string calldata _issueDesc, uint256 _quorum) external returns (uint256) {
    // Crucial: This check must come before the quorum check to pass the unit tests.
    if (balanceOf(msg.sender) == 0) {
        revert NoTokensHeld();
    }
    if (_quorum > totalSupply()) {
        revert QuorumTooHigh(_quorum);
    }

    uint256 newId = issues.length;
    Issue storage newIssue = issues.push();
    newIssue.issueDesc = _issueDesc;
    newIssue.quorum = _quorum;

    return newId;
}

function getIssue(uint256 _issueId) external view returns (IssueData memory) {
    Issue storage issue = issues[_issueId];
    uint256 votersCount = issue.voters.length();
    address[] memory votersArray = new address[](votersCount);

    for (uint256 i = 0; i < votersCount; i++) {
        votersArray[i] = issue.voters.at(i);
    }

    return IssueData({
        voters: votersArray,
        issueDesc: issue.issueDesc,
        votesFor: issue.votesFor,
        votesAgainst: issue.votesAgainst,
        votesAbstain: issue.votesAbstain,
        totalVotes: issue.totalVotes,
        quorum: issue.quorum,
        passed: issue.passed,
        closed: issue.closed
    });
}

function vote(uint256 _issueId, Vote _vote) public {
    Issue storage issue = issues[_issueId];
    
    if (issue.closed) {
        revert VotingClosed();
    }
    if (issue.voters.contains(msg.sender)) {
        revert AlreadyVoted();
    }
    
    uint256 tokenBalance = balanceOf(msg.sender);
    if (tokenBalance == 0) {
        revert NoTokensHeld();
    }

    if (_vote == Vote.FOR) {
        issue.votesFor += tokenBalance;
    } else if (_vote == Vote.AGAINST) {
        issue.votesAgainst += tokenBalance;
    } else if (_vote == Vote.ABSTAIN) {
        issue.votesAbstain += tokenBalance;
    }

    issue.totalVotes += tokenBalance;
    issue.voters.add(msg.sender);

    // Check if the quorum has been met or exceeded.
    if (issue.totalVotes >= issue.quorum) {
        issue.closed = true;
        if (issue.votesFor > issue.votesAgainst) {
            issue.passed = true;
        }
    }
}

}

About

File ds

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published