-
Notifications
You must be signed in to change notification settings - Fork 0
/
12-ERC-20 Tokens.sol
132 lines (114 loc) · 3.54 KB
/
12-ERC-20 Tokens.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol";
contract WeightedVoting is ERC20 {
using EnumerableSet for EnumerableSet.AddressSet;
error TokensClaimed();
error AllTokensClaimed();
error NoTokensHeld();
error QuorumTooHigh();
error AlreadyVoted();
error VotingClosed();
uint private maxSupply = 1_000_000;
struct Issue {
EnumerableSet.AddressSet voters;
string issueDesc;
uint quorum;
uint totalVotes;
uint votesFor;
uint votesAgainst;
uint votesAbstain;
bool passed;
bool closed;
}
struct FormattedIssue {
address[] voters;
string issueDesc;
uint quorum;
uint totalVotes;
uint votesFor;
uint votesAgainst;
uint votesAbstain;
bool passed;
bool closed;
}
Issue[] private issues;
enum Vote {
AGAINST,
FOR,
ABSTAIN
}
mapping(address => bool) private claimed;
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {
Issue storage burntIssue = issues.push();
burntIssue.issueDesc = "burnt";
burntIssue.closed = true;
}
function claim() external {
if (claimed[msg.sender]) {
revert TokensClaimed();
}
if (maxSupply == totalSupply()) {
revert AllTokensClaimed();
}
claimed[msg.sender] = true;
_mint(msg.sender, 100);
}
function createIssue(string calldata _issueDesc, uint _quorum) external returns (uint) {
if (balanceOf(msg.sender) == 0) {
revert NoTokensHeld();
}
if (_quorum > totalSupply()) {
revert QuorumTooHigh();
}
Issue storage newIssue = issues.push();
newIssue.issueDesc = _issueDesc;
newIssue.quorum = _quorum;
unchecked {
return issues.length - 1;
}
}
function getIssue(uint _id) external view returns (FormattedIssue memory) {
Issue storage issue = issues[_id];
return FormattedIssue({
voters: issue.voters.values(),
issueDesc: issue.issueDesc,
quorum: issue.quorum,
totalVotes: issue.totalVotes,
votesFor: issue.votesFor,
votesAgainst: issue.votesAgainst,
votesAbstain: issue.votesAbstain,
passed: issue.passed,
closed: issue.closed
});
}
function vote(uint _issueId, Vote _vote) external {
Issue storage issue = issues[_issueId];
if (issue.closed) {
revert VotingClosed();
}
if (issue.voters.contains(msg.sender)) {
revert AlreadyVoted();
}
uint balance = balanceOf(msg.sender);
if (balance == 0) {
revert NoTokensHeld();
}
issue.voters.add(msg.sender);
if (_vote == Vote.AGAINST) {
issue.votesAgainst += balance;
} else if (_vote == Vote.FOR) {
issue.votesFor += balance;
} else {
issue.votesAbstain += balance;
}
issue.totalVotes += balance;
if (issue.totalVotes >= issue.quorum) {
issue.closed = true;
if (issue.votesFor > issue.votesAgainst) {
issue.passed = true;
}
}
}
}