5

作者: xin激流勇进 | 来源:发表于2023-08-21 10:34 被阅读0次

功能梳理

通过智能合约实现去中心化投票

  1. 主持人创建投票主题与内容
  2. 给予“选民”在选票上投票的权利。
  3. 把你的投票委托给投票人。
  4. 投票
  5. 计算投票结果
  6. 获取投票结果

数据结构

// 投票人集合
  mapping(address => Voter) public voters;
{
  "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2": {
    weight: 1,
    voted: false,
    delegate:0x0000000000000000000000000000000000000000,
    vote:0
  },
  "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db": {
    weight: 1,
    voted: true
    delegate:0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, //委托
    vote:0
  },
   "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB": {
    weight: 1 + 1,
    voted: false,
    delegate:0x0000000000000000000000000000000000000000,
    vote:0
  },
  ....
}
// 主题集合
[{
  name: "海王",
  voteCount: 1
},
{
  name: "下水道",
  voteCount: 1
},
{
  name: "灌篮高手",
  voteCount: 4
}]

合约代码

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract Ballot {
    // 创建投票人结构体
    struct Voter {
        uint256 weight; // 权重
        bool voted; // 是否投票
        address delegate; //委托人地址
        uint256 vote; // 主题id
    }
    // 创建主题结构体
    struct Proposal {
        string name; // 主题名称
        uint256 voteCount; // 票数
    }

    //  主持人地址 也就是投票发起人地址
    address public chairperson;

    // 创建投票人集合
    mapping(address => Voter) public voters;
    // 主题集合
    Proposal[] public proposals;

    // 构造函数中保持主持人地址
    constructor(string[] memory proposalNames) {
        chairperson = msg.sender;
        // 其余的结构数据会给与默认值
        voters[chairperson].weight = 1;
        for (uint256 i = 0; i < proposalNames.length; i++) {
            Proposal memory proposalItem = Proposal(proposalNames[i], 0);
            proposals.push(proposalItem);
        }
    }

    // 返回主题集合
    function proposalList() public view returns (Proposal[] memory) {
        return proposals;
    }

    // 给某些地址赋予选票
    function giveRightToVote(address[] memory voteAddressList) public {
        // 只有算有着可以调用方法
        require(msg.sender == chairperson, "only ower can give right");
        for (uint256 i = 0; i < voteAddressList.length; i++) {
            // 如果该地址已经投过票 不处理,未投过票 赋予权
            if (!voters[voteAddressList[i]].voted) {
                voters[voteAddressList[i]].weight = 1;
            }
        }
    }

    // 将投票权委托给别人
    function delegate(address to) public {
        //  获取委托人信息
        Voter storage sender = voters[msg.sender];
        require(!sender.voted, "you already voted");
        require(msg.sender != to, "self delegate is no allow");
        //  循环判断 委托人不能为空 也不能为自己
        while (voters[to].delegate != address(0)) {
            //
            to = voters[to].delegate;
            require(to == msg.sender, "Found loop in delegete");
        }
        // 将委托人信息修改 投票状态 和 委托人信息
        sender.voted = true;
        sender.delegate = to;
        // 获取被委托人信息
        Voter storage delegate_ = voters[to];
        if (delegate_.voted) {
            // 被委托人如果投票直接将票数相加
            proposals[delegate_.vote].voteCount += sender.weight;
        } else {
            delegate_.weight += sender.weight;
        }
    }

    // 投票

    function vote(uint256 idx) public {
        Voter storage sender = voters[msg.sender];
        require(sender.weight != 0, "Has no right to vote");
        require(!sender.voted, "Already voted.");
        sender.voted = true;
        sender.vote = idx;
        proposals[idx].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;
            }
        }
    }
}

合约地址

合约地址:0xD68848E9DCbE305F4ff782d3194292Bcb1Bd6D41

web3与智能合约

https://web3js.readthedocs.io/en/v1.8.1/

实例web3

const Web3 = require("web3");
import mtcContract from "./contracts/contract_Ballot.json";
// 链接上web3 格尔丽的环境
const geerliWS =
  "wss://goerli.infura.io/ws/v3/e4f789009c9245eeaad1d93ce9d059bb";
var web3 = new Web3(Web3.givenProvider || geerliWS);

账户链接

 const account = await web3.eth.requestAccounts();

实例合约

new web3.eth.Contract(智能合约abi,合约地址)
this.votoContract = new web3.eth.Contract(
      mtcContract.abi,
      "0x1D108E4B9162668e1adACD07727b3de749818d0a"
    );

方法

  1. 不需要消耗gas的方法 call (不修改数据的)

    myContract.methods.myMethod([param1[, param2[, ...]]]).call(options [, defaultBlock] [, callback])
    
    // using the callback
    myContract.methods.myMethod(123).call({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}, function(error, result){
        ...
    });
    
    // using the promise
    myContract.methods.myMethod(123).call({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
    .then(function(result){
        ...
    });
    
    
  1. 修改数据消耗gas的方法 send

    myContract.methods.myMethod([param1[, param2[, ...]]]).send(options[, callback])
    
    // using the callback
    myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}, function(error, transactionHash){
        ...
    });
    
    // using the promise
    myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
    .then(function(receipt){
        // receipt can also be a new contract instance, when coming from a "contract.deploy({...}).send()"
    });
    
    

    注意:数据修改完成后根据需求监听receipt事件

事件

myContract.events.MyEvent([options][, callback])
myContract.events.MyEvent({
    filter: {myIndexedParam: [20,23], myOtherIndexedParam: '0x123456789...'}, // Using an array means OR: e.g. 20 or 23
    fromBlock: 0
}, function(error, event){ console.log(event); })
.on("connected", function(subscriptionId){
    console.log(subscriptionId);
})
.on('data', function(event){
    console.log(event); // same results as the optional callback above
})
.on('changed', function(event){
    // remove event from local database
})
.on('error', function(error, receipt) { // If the transaction was rejected by the network with a receipt, the second parameter will be the receipt.
    ...
});

前端代码

<template>
  <div>
    <h1>千锋去中心投票系统</h1>
    主持人: {{ chairperson }}<br />
    <button v-if="chairperson === account" @click="giveRightToVote">
      分发选票
    </button>
    <hr />
    当前账户:{{ account }}<br />
    权重:{{ voteInfo.weight }}<br />
    委托账户:{{ voteInfo.delegate }}<br />
    是否已投票:{{ voteInfo.voted }}<br />
    投票id:{{ voteInfo.vote }}<br />
    <hr />
    <div class="votolist">
      <div
        class="votolist-item"
        v-for="(item, index) in proposals"
        :key="index"
      >
        <div>
          {{ item.name }}
        </div>
        <div>
          {{ item.voteCount }}
        </div>
        <div>
          <button @click="vote(index)">投票</button>
          <button @click="delegate">委托投票</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
const Web3 = require("web3");
// 合约地址 0xD68848E9DCbE305F4ff782d3194292Bcb1Bd6D41
// 0x61C682E657c44021279DaE8a7652336ddD0b5d2e
import Ballot from "./contracts/contract_Ballot.json";
console.log(Ballot.abi);
var web3 = new Web3(Web3.givenProvider || geerliWS);
const geerliWS =
  "wss://goerli.infura.io/ws/v3/e4f789009c9245eeaad1d93ce9d059bb";
export default {
  data() {
    return {
      account: "",
      proposals: [],
      voteInfo: {},
      chairperson: "",
    };
  },
  methods: {
    delegate() {
      const address = prompt('请输入委托人的地址')
      this.Ballot.methods.delegate(address)
      .send({from:this.account})
      .on("receipt",(event) => {
        alert("委托成功");
        console.log(event)
        this.getVoteInfo();
      })
    },
    // 投票
    async vote(index) {
      const res = await this.Ballot.methods.vote(index).send({from: this.account});
      console.log(res);
    },
    // 分发选票
    async giveRightToVote() {
      const str = prompt("请输入选民地址用,分开");
      const arr = str.split(",");
      console.log(arr);
      this.Ballot.methods
        .giveRightToVote(arr)
        .send({ from: this.account })
        .on("receipt", (event) => {
          alert("选票分发成功");
          console.log(event);
          // 根据需求做一些页面刷新的处理
        });
    },
    // 获取主持人地址
    async getChairperson() {
      this.chairperson = await this.Ballot.methods.chairperson().call();
    },
    // 获取投票主题信息
    async getProposalsData() {
      const proposals = await this.Ballot.methods.getProposals().call();
      this.proposals = proposals;
    },
    // 根据地址获取个人投票者信息
    async getVoteInfo() {
      this.voteInfo = await this.Ballot.methods.voters(this.account).call();
    },
    // 事件监听
    initEventListen() {
      this.Ballot.events
        .voteSuccess({ fromBlock: 0 }, (err, event) => {
          console.log("监听执行");
          console.log(event);
        })
        .on("data", (event) => {
          console.log("智能合约触发事件", event);
          this.getProposalsData();
          this.getVoteInfo();
        });
    },
  },
  async created() {
    // 获取metamask钱包的账户信息
    const accounts = await web3.eth.requestAccounts();
    this.account = accounts[0];
    // 创建智能合约实例
    this.Ballot = new web3.eth.Contract(
      Ballot.abi,
      "0xD68848E9DCbE305F4ff782d3194292Bcb1Bd6D41"
    );
    this.initEventListen();
    this.getProposalsData();
    this.getVoteInfo();
    this.getChairperson();
  },
};
</script>
<style>
.votolist {
  display: flex;
}
.votolist-item {
  width: 200px;
  height: 200px;
  border: 1px solid red;
}
</style>

// 测试连接

0x9B0DbF610175F5c783ec169DAdDa5E8B17055626,0x61C682E657c44021279DaE8a7652336ddD0b5d2e,0x29D789aE3243cBfF3C3f85E0Bde2e835FFA7D202

相关文章

  • 灵心儿组计分

    9.24 棉芯➕5➕5 晨萌➕5 乐乐➕5➕5➕5➕5 楠楠➕5➕5➕5➕5 水儿➕5➕5 七炫➕5➕5➕5➕5

  • 未闻花名口琴简谱

    1 4 5 5 6 6 6 6 6 6 5 5 5 5 5 5 4 4 4 4 4 4 4 1 1 1 4 5 5...

  • 5/5

    该地区最重要的科技增长毫无疑问是半导体产品。在新泽西州Bell实验室里,William曾是晶体管发明人之一,195...

  • 5/5

    我允许自己表达疲惫,允许自己懒懒前行,允许自己自然而然……感觉到放松,感觉到对自我的接纳,自己理解自己,直面自己,...

  • 5、5

    しょんぼり [寂しく]孤零零(地);[がっくり]垂气(成),无精打采(成) ¶~立っている/无精打采地站着 ¶彼は...

  • 5 .5

    今天去的晚了一点,然后男队那边早早就开始在训练了,但是女队现在还在慢悠悠的走,有十多分钟,这种学生投入性低,缺...

  • 5/5

    五一后第一天上课,虽然起晚了一点,不过语文课还是听上了。数学老师的课就是摸鱼,老师讲评了五一的卷子,真的超难!我原...

  • 托姆拉物语暴力火线技能树

    剑术:战吼5+5+10 射击:弱狙5+5+10 火线5+10 要领10 分身5+5+10 魔法:冲击波5+5+5+...

  • 密码

    1【5】1 云1 2【5】2 云2 3【5】3 云3 4【5】4 云4 5【5】5 云5 《2.0》 公告 6【5...

  • 20171109

    5:00-5:10 洗漱 V 5:10-5:40 晨间日记 V 5:40-5:50 反思昨天 V 5:50-5: ...

网友评论

      本文标题:5

      本文链接:https://www.haomeiwen.com/subject/rvgemdtx.html