🎓 A1Learner
🔍
0 of 45 lessons completed

Solidity Voting Systems

A voting system is a contract where token holders cast for or against votes weighted by their token balance. A proposal passes only when enough total votes arrive and the for votes beat the against votes.

Weights count for and against

One holder with weight 60 votes for and one with weight 40 votes against. The totals read 60 for and 40 against.

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

contract SimpleVote {
    address public owner;
    mapping(address => uint256) public weight;
    mapping(address => bool) public voted;
    uint256 public forVotes;
    uint256 public againstVotes;
    uint256 public quorum;
    constructor(uint256 quorumNeeded) {
        owner = msg.sender;
        quorum = quorumNeeded;
    }
    function setWeight(address voter, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        weight[voter] = amount;
    }
    function vote(bool support) public {
        require(!voted[msg.sender], "already voted");
        require(weight[msg.sender] > 0, "no voting weight");
        voted[msg.sender] = true;
        if (support) {
            forVotes += weight[msg.sender];
        } else {
            againstVotes += weight[msg.sender];
        }
    }
    function passed() public view returns (bool) {
        return forVotes > againstVotes && (forVotes + againstVotes) >= quorum;
    }
}

A proposal passes when quorum is met

With a quorum of 50, the same 60 for and 40 against reach a total of 100. The for votes also beat the against votes, so the proposal passes.

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

contract SimpleVote {
    address public owner;
    mapping(address => uint256) public weight;
    mapping(address => bool) public voted;
    uint256 public forVotes;
    uint256 public againstVotes;
    uint256 public quorum;
    constructor(uint256 quorumNeeded) {
        owner = msg.sender;
        quorum = quorumNeeded;
    }
    function setWeight(address voter, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        weight[voter] = amount;
    }
    function vote(bool support) public {
        require(!voted[msg.sender], "already voted");
        require(weight[msg.sender] > 0, "no voting weight");
        voted[msg.sender] = true;
        if (support) {
            forVotes += weight[msg.sender];
        } else {
            againstVotes += weight[msg.sender];
        }
    }
    function passed() public view returns (bool) {
        return forVotes > againstVotes && (forVotes + againstVotes) >= quorum;
    }
}

A proposal fails without quorum

With a quorum of 200, the same votes total only 100. The for votes still beat the against votes, but the proposal fails for lack of quorum.

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

contract SimpleVote {
    address public owner;
    mapping(address => uint256) public weight;
    mapping(address => bool) public voted;
    uint256 public forVotes;
    uint256 public againstVotes;
    uint256 public quorum;
    constructor(uint256 quorumNeeded) {
        owner = msg.sender;
        quorum = quorumNeeded;
    }
    function setWeight(address voter, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        weight[voter] = amount;
    }
    function vote(bool support) public {
        require(!voted[msg.sender], "already voted");
        require(weight[msg.sender] > 0, "no voting weight");
        voted[msg.sender] = true;
        if (support) {
            forVotes += weight[msg.sender];
        } else {
            againstVotes += weight[msg.sender];
        }
    }
    function passed() public view returns (bool) {
        return forVotes > againstVotes && (forVotes + againstVotes) >= quorum;
    }
}

Example

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

contract SimpleVote {
    address public owner;
    mapping(address => uint256) public weight;
    mapping(address => bool) public voted;
    uint256 public forVotes;
    uint256 public againstVotes;
    uint256 public quorum;
    constructor(uint256 quorumNeeded) {
        owner = msg.sender;
        quorum = quorumNeeded;
    }
    function setWeight(address voter, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        weight[voter] = amount;
    }
    function vote(bool support) public {
        require(!voted[msg.sender], "already voted");
        require(weight[msg.sender] > 0, "no voting weight");
        voted[msg.sender] = true;
        if (support) {
            forVotes += weight[msg.sender];
        } else {
            againstVotes += weight[msg.sender];
        }
    }
    function passed() public view returns (bool) {
        return forVotes > againstVotes && (forVotes + againstVotes) >= quorum;
    }
}

Key points

  • Voting system: A voting system is a contract where token holders cast for or against votes weighted by their token balance.
  • Vote weight: Vote weight is the number of votes one holder casts, set by their token balance.
  • Quorum: A quorum is the minimum total votes a proposal needs before it can pass.
  • Pass threshold: A pass threshold is the rule that for votes must beat against votes.
  • Double vote: A double vote is a second vote from the same holder, which the contract must reject.
  • Restatement: A proposal passes when the for votes beat the against votes and the total votes reach the quorum.

In this example, two holders vote for with weights 50 and 30, and one holder votes against with weight 20. The total reaches the quorum of 80, the for votes win, and the first holder voting again fails.

Note

Weight here is set by the owner in this small example, so the lesson can focus on counting rather than on a full token.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

For votes are 80, against votes are 20, and the quorum is 100. Does the proposal pass?