-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathQuadraticVotingERC721.sol
177 lines (147 loc) · 5.83 KB
/
QuadraticVotingERC721.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract QuadraticVotingERC721 {
uint public proposalCount;
address public owner;
constructor() {
owner = msg.sender;
}
enum ProposalStatus {
IN_PROGRESS,
ENDED
}
struct Proposal {
address proposalForNFT;
address creator;
bytes description;
uint yesVotes;
uint noVotes;
uint expirationTime;
ProposalStatus status;
address[] voters;
uint proposalId;
uint totalVoters;
mapping(address => Voter) voterInfo;
}
struct Voter {
bool hasVoted;
bool vote;
uint weight;
}
mapping(uint => Proposal) public ProposalIdToProposal;
modifier hasBalance(address _nftAddress){
require(_nftAddress != address(0));
IERC721 erc721 = IERC721(_nftAddress);
uint voterBalance = erc721.balanceOf(msg.sender);
require(voterBalance >= 0, "You dont have enough tokens");
_;
}
modifier validProposal(uint _proposalId){
require(_proposalId <= proposalCount && _proposalId > 0);
require(getProposalStatus(_proposalId) == ProposalStatus.IN_PROGRESS);
require(getProposalExpirationTime(_proposalId) > block.timestamp, "Proposal has expired");
_;
}
modifier authUser(address _user, uint _proposalId) {
require(msg.sender == owner || msg.sender == ProposalIdToProposal[_proposalId].creator);
_;
}
function createProposal(address _nftAddress, string calldata _description, uint _expirationTime)
external
returns (uint)
{
// require(checkProposalLimit(_nftAddress), "There are 3 pending proposals"); // Need to check this require on final build
require(_expirationTime > block.timestamp, "Expiration time must be in future" );
proposalCount++;
Proposal storage currentProposal = ProposalIdToProposal[proposalCount];
currentProposal.proposalId = proposalCount;
currentProposal.proposalForNFT = _nftAddress;
currentProposal.creator = msg.sender;
currentProposal.description = abi.encode(_description);
currentProposal.expirationTime = _expirationTime;
currentProposal.status = ProposalStatus.IN_PROGRESS;
return proposalCount;
}
function castVote(uint _proposalId, bool _vote)
public
hasBalance(ProposalIdToProposal[_proposalId].proposalForNFT)
validProposal(_proposalId)
{
require(userHasVoted(_proposalId, msg.sender) != true, "User has already voted");
Proposal storage currentProposal = ProposalIdToProposal[_proposalId];
IERC721 erc721 = IERC721(ProposalIdToProposal[_proposalId].proposalForNFT);
uint voterBalance = erc721.balanceOf(msg.sender);
uint weight = sqrt(voterBalance);
currentProposal.voterInfo[msg.sender] = Voter(true, _vote, weight);
currentProposal.voters.push(msg.sender);
currentProposal.totalVoters = currentProposal.voters.length;
countVotes(_proposalId);
}
function countVotes(uint _proposalId) public
validProposal(_proposalId)
returns (uint, uint)
{
uint yesVotes;
uint noVotes;
address[] memory voters = ProposalIdToProposal[_proposalId].voters;
for (uint i = 0; i < voters.length; i++) {
address voter = voters[i];
bool vote = ProposalIdToProposal[_proposalId].voterInfo[voter].vote;
uint weight = ProposalIdToProposal[_proposalId].voterInfo[voter].weight;
if (vote == true) {
yesVotes += weight;
}
else {noVotes += weight;}
}
ProposalIdToProposal[_proposalId].yesVotes = yesVotes;
ProposalIdToProposal[_proposalId].noVotes = noVotes;
return (yesVotes, noVotes);
}
function setProposalStatus(uint _proposalId) external
validProposal(_proposalId)
authUser(msg.sender, _proposalId)
{
ProposalIdToProposal[_proposalId].status = ProposalStatus.ENDED;
}
function userHasVoted(uint _proposalId, address _user)internal view returns (bool) {
return (ProposalIdToProposal[_proposalId].voterInfo[_user].hasVoted);
}
function getProposalStatus(uint _proposalId) internal view returns(ProposalStatus) {
return ProposalIdToProposal[_proposalId].status;
}
function getProposalExpirationTime(uint _proposalId) internal view returns(uint) {
return ProposalIdToProposal[_proposalId].expirationTime;
}
function checkProposalLimit(address _nftAddress) internal view returns(bool) {
uint _proposalForNftAddress;
if (proposalCount > 2) {
for (uint i = 0; i < proposalCount; i++) {
if (ProposalIdToProposal[i].proposalForNFT == _nftAddress && ProposalIdToProposal[i].status == ProposalStatus.IN_PROGRESS) {
_proposalForNftAddress++;
} if (_proposalForNftAddress > 3) {
return false;
}
}
} else return true;
}
function sqrt(uint x) internal pure returns (uint y) {
uint z = (x + 1) / 2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
}
// function checkProposalLimit(address _nftAddress) internal view returns(bool) {
// uint _proposalForNftAddress;
// if (proposalCount < 3) {
// return true;
// } else for (uint i = 0; i < proposalCount; i++) {
// if (ProposalIdToProposal[i].proposalForNFT == _nftAddress && ProposalIdToProposal[i].status == ProposalStatus.IN_PROGRESS) {
// _proposalForNftAddress++;
// } if (_proposalForNftAddress > 3) {
// return false;
// }
// } return true;
}