实现一个投票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);
![image 6.png](https://img.learnblockchain.cn/attachments/2024/10/iGTh1izd671cc8f915f45.png)
![image 7.png](https://img.learnblockchain.cn/attachments/2024/10/ASATZKJn671cc8f8eec19.png)
// 构造函数,部署合约时,初始化添加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如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!