实现一个投票DAPP的基本需求: 每人(钱包/账号)只能投票一次 记录一共有多少个候选人 记录每个候选人的的得票数完整的项目结构

truffle inti 初始化项目,生成项目结构及配置文件






// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.5.0;
contract Election {
    // 记录候选人及得票数
    struct Candidate {
        uint id;
        string name; // 候选人名字
        uint voteCount; //得票数
    }
    // 定义一个mapping记录投票纪录:每人(账号)只能投票一次
    mapping(address => bool) public voters;
    // 通过 id 作为 key 访问映射candidates来获取候选人名单
    mapping(uint => Candidate) public candidates;
    // 共有多少个候选人
    uint public candidatesCount;
    // 投票事件
    event votedEvent(uint indexed _candidateId);


    // 构造函数,部署合约时,初始化添加2个候选人
    constructor() public {
        addCandidate("Tiny 熊");
        addCandidate("Big 牛");
    }
    // 添加候选人方法,把名字添加到candidates映射中,同时候选人数量加1
    function addCandidate(string memory _name) private {
        candidatesCount++;
        candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
    }
    // 投票方法,在对应候选人的voteCount加1
    function vote(uint _candidateId) public {
        // 检查是否已经投票
        require(!voters[msg.sender]);
        // 检查投票id是否在候选人名单中
        require(_candidateId > 0 && _candidateId <= candidatesCount);
        // 记录谁投票了
        voters[msg.sender] = true;
        // 候选人票数加1
        candidates[_candidateId].voteCount++;
        // 触发投票事件
        emit votedEvent(_candidateId);
    }truffle compile  进行编译合约
const Election = artifacts.require("Election")
module.exports = function(deployer){
    deployer.deploy(Election);
}truffle migrate  进行迁移(部署)
const Election = artifacts.require("Election");
contract("Election",function (accounts){
    let instance;
    // it 定义一个测试用例
    it("Election",async function(){
        instance = await Election.deployed();
        // 获取候选人数量
        count =  await instance.candidatesCount();
                // 断言测试用例,满足 ✔,异常 ×
        assert.equal(count, 2, "候选人数目应为2");
    })
});truffle test 运行测试用例
<table class="table">
        <thead>
            <tr>
                <th scope="col">#</th>
                <th scope="col">候选人</th>
                <th scope="col">得票数</th>
            </tr>
        </thead>
        <tbody id="candidatesResults"></tbody>
  </table>
  <form onSubmit="App.castVote(); return false;">
      <div class="form-group">
          <label for="candidatesSelect">选择候选人</label>
          <select class="form-control" id="candidatesSelect"></select>
      </div>
      <button type="submit" class="btn btn-primary">投票</button>
      <hr/>
  </form>initWeb3: async function() {
        // 检查浏览器 ethereum对象
        if (window.ethereum) {
          App.web3Provider = window.ethereum;
          try {
            // 请求账号访问权限
            await window.ethereum.enable();
          } catch (error) {
            // 用户拒绝访问
            console.error("User denied account access")
          }
        } 
        // 用于兼容老的浏览器钱包插件
        else if (window.web3) {
          App.web3Provider = window.web3.currentProvider;
        }
        // 如果没有检测到注入的 web3 实例,则回退到 Ganache 网络
        else {
          App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545 ');
        }
        web3 = new Web3(App.web3Provider);
      return App.initContract();
    },
      // 用来进行合约初始化
    initContract: function() {
      $.getJSON("Election.json", function(election) {
        // Instantiate a new truffle contract from the artifact
        App.contracts.Election = TruffleContract(election);
        // Connect provider to interact with contract
        App.contracts.Election.setProvider(App.web3Provider);
        App.listenForEvents();
        return App.render();
      });
    }render: function() {
    var electionInstance;
    var loader = $("#loader");
    var content = $("#content");
    loader.show();
    content.hide();
    // Load account data
    web3.eth.getCoinbase(function(err, account) {
      if (err === null) {
        App.account = account;
        $("#accountAddress").html("Your Account: " + account);
      }
    });
    // Load contract data
    App.contracts.Election.deployed().then(function(instance) {
      electionInstance = instance;
      // 获取候选人数量
      return electionInstance.candidatesCount();
    }).then(function(candidatesCount) {
      var candidatesResults = $("#candidatesResults");
      candidatesResults.empty();
      var candidatesSelect = $('#candidatesSelect');
      candidatesSelect.empty();
      for (var i = 1; i <= candidatesCount; i++) {
        electionInstance.candidates(i).then(function(candidate) {
        // 渲染候选人 candidate 结构体
          var id = candidate[0];
          var name = candidate[1];
          var voteCount = candidate[2];
          // 将候选人信息存入候选人表格中
          var candidateTemplate = "<tr><th>" + id + "</th><td>" + name + "</td><td>" + voteCount + "</td></tr>"
          candidatesResults.append(candidateTemplate);
          // 将候选人信息存入投票选项
          var candidateOption = "<option value='" + id + "' >" + name + "</ option>"
          candidatesSelect.append(candidateOption);
        });
      }
      return electionInstance.voters(App.account);
    }).then(function(hasVoted) {
      // Do not allow a user to vote
      if(hasVoted) {
        $('form').hide();
      }
      loader.hide();
      content.show();
    }).catch(function(error) {
      console.warn(error);
    });
  }npm install --save-dev lite-server{
  "server": {
         // ./src是网页文件目录 ./build/contracts是Truffle编译部署合约输出的目录
    "baseDir": ["./src", "./build/contracts"]
  }
}
{
  "scripts": {
    "dev": "lite-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}npm run dev 启动 DAPP













 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!