# SolidityCaseBallotAnalysis **Published by:** [weijunzi](https://paragraph.com/@weijunzi/) **Published on:** 2022-06-06 **URL:** https://paragraph.com/@weijunzi/soliditycaseballotanalysis ## Content official case Ballot过去几个月,每次说学习solidity都无动于衷,这次终于行动起来了,希望一直坚持下去.0x1来自官方案例:pragma solidity ^0.4.22; /// @title 委托投票 contract Ballot { // 这里声明了一个新的复合类型用于稍后的变量 // 它用来表示一个选民 struct Voter { uint weight; // 计票的权重 bool voted; // 若为真,代表该人已投票 address delegate; // 被委托人 uint vote; // 投票提案的索引 } // 提案的类型 struct Proposal { bytes32 name; // 简称(最长32个字节) uint voteCount; // 得票数 } address public chairperson; // 这声明了一个状态变量,为每个可能的地址存储一个 `Voter`。 mapping(address => Voter) public voters; // 一个 `Proposal` 结构类型的动态数组 Proposal[] public proposals; /// 为 `proposalNames` 中的每个提案,创建一个新的(投票)表决 constructor(bytes32[] proposalNames) public { chairperson = msg.sender; voters[chairperson].weight = 1; //对于提供的每个提案名称, //创建一个新的 Proposal 对象并把它添加到数组的末尾。 for (uint i = 0; i < proposalNames.length; i++) { // `Proposal({...})` 创建一个临时 Proposal 对象, // `proposals.push(...)` 将其添加到 `proposals` 的末尾 proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 })); } } // 授权 `voter` 对这个(投票)表决进行投票 // 只有 `chairperson` 可以调用该函数。 function giveRightToVote(address voter) public { // 若 `require` 的第一个参数的计算结果为 `false`, // 则终止执行,撤销所有对状态和以太币余额的改动。 // 在旧版的 EVM 中这曾经会消耗所有 gas,但现在不会了。 // 使用 require 来检查函数是否被正确地调用,是一个好习惯。 // 你也可以在 require 的第二个参数中提供一个对错误情况的解释。 require( msg.sender == chairperson, "Only chairperson can give right to vote." ); require( !voters[voter].voted, "The voter already voted." ); require(voters[voter].weight == 0); voters[voter].weight = 1; } /// 把你的投票委托到投票者 `to`。 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."); // 委托是可以传递的,只要被委托者 `to` 也设置了委托。 // 一般来说,这种循环委托是危险的。因为,如果传递的链条太长, // 则可能需消耗的gas要多于区块中剩余的(大于区块设置的gasLimit), // 这种情况下,委托不会被执行。 // 而在另一些情况下,如果形成闭环,则会让合约完全卡住。 while (voters[to].delegate != address(0)) { to = voters[to].delegate; // 不允许闭环委托 require(to != msg.sender, "Found loop in delegation."); } // `sender` 是一个引用, 相当于对 `voters[msg.sender].voted` 进行修改 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; } } /// 把你的票(包括委托给你的票), /// 投给提案 `proposals[proposal].name`. function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; require(!sender.voted, "Already voted."); sender.voted = true; sender.vote = proposal; // 如果 `proposal` 超过了数组的范围,则会自动抛出异常,并恢复所有的改动 proposals[proposal].voteCount += sender.weight; } /// @dev 结合之前所有的投票,计算出最终胜出的提案 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; } } } // 调用 winningProposal() 函数以获取提案数组中获胜者的索引,并以此返回获胜者的名称 function winnerName() public view returns (bytes32 winnerName_) { winnerName_ = proposals[winningProposal()].name; } } 跟nodejs有那么一点相似语法上有一点相似. 官方推荐 Remix IDE web端就能使用 不用下载0x2remix 部署合约先compile 然后在部署里可以看到刚刚编译好的合约这里部署需要传入bytes32[]类型的数组,其实就是提案数组 constructor(bytes32[] proposalNames) public { chairperson = msg.sender; voters[chairperson].weight = 1; for (uint i = 0; i < proposalNames.length; i++) { proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 })); } 合约部署者 就是proposal的chairperson remix 这里是不会自动补全的 传入的bytes32的length应该为66=0x+64hexconst web3 = require('web3'); // change string to hex const hex1 = web3.utils.toHex('proposal 1'); const hex2 = web3.utils.toHex('proposal 2'); // fill 0 in the end const filledHex1 = web3.utils.padRight(hex1, 64); const filledHex2 = web3.utils.padRight(hex2, 64); console.log("filledHex1",filledHex1); console.log("filledHex2",filledHex2); //filledHex1 0x70726f706f73616c203100000000000000000000000000000000000000000000 //filledHex2 0x70726f706f73616c203200000000000000000000000000000000000000000000 // change hex to string const str = web3.utils.hexToAscii(hex2); console.log("str",str); deploy的入参就传入["0x70726f706f73616c203100000000000000000000000000000000000000000000","0x70726f706f73616c203200000000000000000000000000000000000000000000"] 如果bytes32不补全的化会出现如下错误 creation of Ballot errored: Error encoding arguments: Error: incorrect data length (argument=null, value="0x1231", code=INVALID_ARGUMENT, version=abi/5.5.0) 部署完成可以看到当前的account是有gas消耗的.0x3对应的方法和变量可以在下面查看 ## Publication Information - [weijunzi](https://paragraph.com/@weijunzi/): Publication homepage - [All Posts](https://paragraph.com/@weijunzi/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@weijunzi): Subscribe to updates