Simple Smart Contract Development

代理投票

需求

实现一个带有代理功能的投票智能合约。

思路

  1. 定义投票者 Voter 的结构体,定义提案 Proposal 的结构体。
  2. Voter 可以对 Proposal 进行投票,同时 VoterA 可以把自己的代理权给 VoterB,但是必须可以追踪到。例如 A->B->C ,从 C 的投票权追溯到 A。
  3. 要有一个投票管理者 Chairman。

投票流程

  1. 有一系列 Proposal,用数组 Proposals 来存储。
  2. 同时也有 Voters 数组,来存储 Voter
  3. 每个 Voter 可以自己投票,也可以让别人帮自己投票(代理)
  4. 最后统计当前最高票的提案
pragma solidity ^0.8.0;

contract Ballot{
  struct Voter{
    uint weight;
    bool voted;
    address delegate;
    uint vote;
  }

  struct Proposal{
    bytes32 name;
    uint voteCount;
  }

  address public chairman;
  mapping(address => Voter) public voters;

  Proposal [] public proposals;

  function Ballot(bytes32[] proposalNames) public{
    chairperson = msg.sender;
    voters[chairperson].weight = 1;

    // Create Proposals
    for(uint i = 0; i < proposalNames.length; i++)
    {
      proposals.push(Proposal({
        name: proposalNames[i],
        voteCount:0
      }));
    }
  }

  function giveRight2Vote(address voter) public{
    require(
      msg.sender == chairman;
      "Only chairman can give right to vore."
    );
    require(
      !voters[voter].voted,
      "The voter already voted."
    );
    require(voters[voter].weight == 0);
    voters[voter].weight = 1;
  }

  function delegate(address to) public{
    Voter storage sender = voters[msg.sender];
    require(!sender.voted, "You already voted.");
    require(to != msg.sender, "Self-delegation is disallowed.");

    while(voters[to].delegate != address(0))
    {
      to = voters[to].delegate;
      require(to != msg.sender, "Found loop in delegation.");

    }

    sender.voted = true;
    sender.delegate = to;
    Voter storage delegate_ = voters[to];

    if(delegate_.voted){
      proposal[delegate_.vote].voteCOunt += sender.weight;
    } else {
      delegate_.weight += sender.weight;
    }
  }


  function vote(uint proposal) public {
        Voter storage sender = voters[msg.sender];
        require(!sender.voted, "Already voted.");
        sender.voted = true;
        sender.vote = proposal;

        // 如果proposals的数组越界,会自动失败,并且还原变化
        proposals[proposal].voteCount += sender.weight;
    }

  function winningProposal() public view
            returns (uint winningProposal_)
    {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal_ = p;
            }
        }
    }

  function winnerName() public view
            returns (bytes32 winnerName_)
    {
        winnerName_ = proposals[winningProposal()].name;
    }
}