# 打算研究下合约,会持续记录分析一些合约的代码,刚开始学习,错误疏漏之处之处希望大家不吝多多留言赐教。
合约1:投票
```jsx
pragma solidity ^0.8.0;
contract Ballot {
// 这里声明一个新的复合类型用于稍后的变量
// 它用来表示 一个选民
struct Voter{
uint weight; //计票的权重
bool voted; //若为真,表明该人已投票
address delegate; //被委托人
uint vote; //投票提案的索引
}
address public chairperson;
// 这声明了一个状态变量,为每个可能的地址存储一个`Voter`。
mapping(address=>Voter)public voters;
// 提案的类型
struct Proposal{
bytes32 name; // 简称(最长32个字节)
uint voteCount; // 得票数
}
// 一个`Proposal`结构类型的动态数组
Proposal[] public proposals;
/// 为`proposalNames`中每个提案,创建一个新的(投票)表决
constructor(bytes32[] memory proposalNames) {
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(
msg.sender==chairperson,
"Only chairperson can giv 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(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;
}
// function Ballot(){
// }
}
```
![Untitled](%E5%90%88%E7%BA%A6%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95%200805c9dc86fc42548d952750da869f85/Untitled.png)
部署后的合约地址为:0xb2193b5e61d29aC21D6a1Ec704BE53D5012633cB
⇒ 1. 部署时,需要输入的内容是**构造函数constructor中的初始化数据**:
```solidity
/// 为`proposalNames`中每个提案,创建一个新的(投票)表决
constructor(bytes32[] memory proposalNames) {
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
}));
}
}
```
→ 即为传入的参数bytes32[]的proposalNames,
→ 对传传入的proposalNames的处理是将其从index为0的Name开始,压入Proposal结构类型的动态数组proposals中,作为**备选提案。**
⇒ 2. **委托操作,委托某人帮你完成投票,即他**
```solidity
/// 把你的投票委托到投票者`to`
function delegate(address to) public{
// 传引用
Voter storage sender=voters[msg.sender];
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;
}
}
}
```
—> 传入参数’to’即为**受委托方,受委托方的权重会加上委托方的权重,委托方权重清空为0。**
—> 这里的msg.sender即为发起委托的人,即为委托人,addres(0)也是。
—> 受委托者不能是委托者自己。
—> 受委托者可以继续作为委托者将投票委托给其它人,但不能又反过来委托给委托人,形成闭环loop.
—> 受委托者已经投过票了则直接增加受委托者所投票提案的得票数。
⇒ 3. **给予投票权**,让某人能投票,只有主席才有操作权力。
```solidity
// 授权 `voter` 对这个(投票)表决进行投票
// 只有 `chairperson` 可以调用该函数。
function giveRightToVote(address voter) public{
require(
msg.sender==chairperson,
"Only chairperson can giv right to vote."
);
require(!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight==0);
voters[voter].weight=1;
}
```
⇒ 4. 给某提案投票,输入提案名proposalName.
```solidity
/// 把你(包括委托给你的票),
/// 投给提案`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;
}
```
**⇒ 5. 主席,也即为投票主持人、发起人,这里只有查询功能**
```solidity
address public chairperson;
```
⇒ 6. 提案列表
```solidity
// 一个`Proposal`结构类型的动态数组
Proposal[] public proposals;
```
⇒ 7. 查询投票人,需要传入地址:
![Untitled](%E5%90%88%E7%BA%A6%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95%200805c9dc86fc42548d952750da869f85/Untitled%201.png)
```solidity
// 这里声明一个新的复合类型用于稍后的变量
// 它用来表示 一个选民
struct Voter{
uint weight; //计票的权重
bool voted; //若为真,表明该人已投票
address delegate; //被委托人
uint vote; //投票提案的索引
}
```
⇒ 8. 投票结果胜出者名字,只供查询操作:
```solidity
// 调用winningProposal() 函数以获取提案数组中获胜者的索引,并以此返回获胜者的名称
function winnerName() public view
returns(bytes32 winnerName_)
{
winnerName_=proposals[winningProposal()].name;
}
```
⇒ 9. 结合之前所有的投票,计算出最终胜出的提案
```solidity
/// @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;
}
}
}
```