Skip to content

Commit c6cdc38

Browse files
authored
Merge pull request #329 from PolymathNetwork/voting-module-oct
Voting module
2 parents 5ed3fa4 + 72fe74a commit c6cdc38

File tree

4 files changed

+1037
-143
lines changed

4 files changed

+1037
-143
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
pragma solidity ^0.4.24;
2+
3+
import "./ICheckpoint.sol";
4+
import "../Module.sol";
5+
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
6+
7+
/**
8+
* @title Checkpoint module for token weighted vote
9+
* @notice This voting system uses public votes
10+
*/
11+
contract WeightedVoteCheckpoint is ICheckpoint, Module {
12+
using SafeMath for uint256;
13+
14+
struct Ballot {
15+
uint256 checkpointId;
16+
uint256 totalSupply;
17+
uint256 startTime;
18+
uint256 endTime;
19+
uint256 cumulativeYes;
20+
uint256 cumulativeNo;
21+
uint256 numVotes;
22+
mapping(address => Vote) voteByAddress;
23+
bool isActive;
24+
}
25+
26+
Ballot[] public ballots;
27+
28+
struct Vote {
29+
uint256 time;
30+
uint256 weight;
31+
bool vote;
32+
}
33+
34+
event BallotCreated(uint256 _startTime, uint256 _endTime, uint256 _ballotId, uint256 _checkpointId);
35+
event VoteCasted(uint256 _ballotId, uint256 _time, address indexed _investor, uint256 _weight, bool _vote);
36+
event BallotActiveStatsChanged(uint256 _ballotId, bool _isActive);
37+
38+
/**
39+
* @notice Constructor
40+
* @param _securityToken Address of the security token
41+
* @param _polyAddress Address of the polytoken
42+
*/
43+
constructor(address _securityToken, address _polyAddress) public Module(_securityToken, _polyAddress) {
44+
45+
}
46+
47+
/**
48+
* @notice Queries the result of a given ballot
49+
* @param _ballotId Id of the target ballot
50+
* @return uint256 cummulativeYes
51+
* @return uint256 cummulativeNo
52+
* @return uint256 totalAbstain
53+
* @return uint256 remainingTime
54+
*/
55+
function getResults(uint256 _ballotId) public view returns (uint256 cummulativeYes, uint256 cummulativeNo, uint256 totalAbstain, uint256 remainingTime) {
56+
uint256 abstain = (ballots[_ballotId].totalSupply.sub(ballots[_ballotId].cumulativeYes)).sub(ballots[_ballotId].cumulativeNo);
57+
uint256 time = (ballots[_ballotId].endTime > now) ? ballots[_ballotId].endTime.sub(now) : 0;
58+
return (ballots[_ballotId].cumulativeYes, ballots[_ballotId].cumulativeNo, abstain, time);
59+
}
60+
61+
/**
62+
* @notice Allows a token holder to cast their vote on a specific ballot
63+
* @param _vote The vote (true/false) in favor or against the proposal
64+
* @param _ballotId The index of the target ballot
65+
* @return bool success
66+
*/
67+
function castVote(bool _vote, uint256 _ballotId) public returns (bool) {
68+
require(now > ballots[_ballotId].startTime && now < ballots[_ballotId].endTime, "Voting period is not active.");
69+
require(ballots[_ballotId].voteByAddress[msg.sender].time == 0, "Token holder has already voted.");
70+
require(ballots[_ballotId].isActive == true, "This ballot is deactiveated.");
71+
uint256 checkpointId = ballots[_ballotId].checkpointId;
72+
uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender,checkpointId);
73+
require(weight > 0, "Token Holder balance is zero.");
74+
ballots[_ballotId].voteByAddress[msg.sender].time = now;
75+
ballots[_ballotId].voteByAddress[msg.sender].weight = weight;
76+
ballots[_ballotId].voteByAddress[msg.sender].vote = _vote;
77+
ballots[_ballotId].numVotes = ballots[_ballotId].numVotes.add(1);
78+
if (_vote == true) {
79+
ballots[_ballotId].cumulativeYes = ballots[_ballotId].cumulativeYes.add(weight);
80+
} else {
81+
ballots[_ballotId].cumulativeNo = ballots[_ballotId].cumulativeNo.add(weight);
82+
}
83+
emit VoteCasted(_ballotId, now, msg.sender, weight, _vote);
84+
return true;
85+
}
86+
87+
/**
88+
* @notice Allows the token issuer to create a ballot
89+
* @param _duration The duration of the voting period in seconds
90+
* @return bool success
91+
*/
92+
function createBallot(uint256 _duration) public onlyOwner {
93+
require(_duration > 0, "Incorrect ballot duration.");
94+
uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint();
95+
uint256 endTime = now.add(_duration);
96+
createCustomBallot(now, endTime, checkpointId);
97+
}
98+
99+
/**
100+
* @notice Allows the token issuer to create a ballot with custom settings
101+
* @param _startTime Start time of the voting period in Unix Epoch time
102+
* @param _endTime End time of the voting period in Unix Epoch time
103+
* @param _checkpointId Index of the checkpoint to use for token balances
104+
* @return bool success
105+
*/
106+
function createCustomBallot(uint256 _startTime, uint256 _endTime, uint256 _checkpointId) public onlyOwner {
107+
require(_endTime > _startTime, "Ballot end time must be later than start time.");
108+
uint256 ballotId = ballots.length;
109+
uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(_checkpointId);
110+
ballots.push(Ballot(_checkpointId,supplyAtCheckpoint,_startTime,_endTime,0,0,0, true));
111+
emit BallotCreated(_startTime, _endTime, ballotId, _checkpointId);
112+
}
113+
114+
/**
115+
* @notice Allows the token issuer to set the active stats of a ballot
116+
* @param _ballotId The index of the target ballot
117+
* @param _isActive The bool value of the active stats of the ballot
118+
* @return bool success
119+
*/
120+
function setActiveStatsBallot(uint256 _ballotId, bool _isActive) public onlyOwner {
121+
require(now < ballots[_ballotId].endTime, "This ballot has already ended.");
122+
require(ballots[_ballotId].isActive != _isActive, "Active state unchanged");
123+
ballots[_ballotId].isActive = _isActive;
124+
emit BallotActiveStatsChanged(_ballotId, _isActive);
125+
}
126+
127+
128+
function getInitFunction() public returns(bytes4) {
129+
return bytes4(0);
130+
}
131+
132+
/**
133+
* @notice Return the permissions flag that are associated with STO
134+
* @return bytes32 array
135+
*/
136+
function getPermissions() public view returns(bytes32[]) {
137+
bytes32[] memory allPermissions = new bytes32[](0);
138+
return allPermissions;
139+
}
140+
141+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
pragma solidity ^0.4.24;
2+
3+
import "./WeightedVoteCheckpoint.sol";
4+
import "../ModuleFactory.sol";
5+
6+
/**
7+
* @title Factory for deploying WeightedVoteCheckpoint module
8+
*/
9+
contract WeightedVoteCheckpointFactory is ModuleFactory {
10+
11+
/**
12+
* @notice Constructor
13+
* @param _polyAddress Address of the polytoken
14+
* @param _setupCost Setup cost of the module
15+
* @param _usageCost Usage cost of the module
16+
* @param _subscriptionCost Subscription cost of the module
17+
*/
18+
constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public
19+
ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost)
20+
{
21+
version = "1.0.0";
22+
name = "WeightedVoteCheckpoint";
23+
title = "Weighted Vote Checkpoint";
24+
description = "Weighted votes based on token amount";
25+
compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
26+
compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
27+
28+
}
29+
30+
/**
31+
* @notice used to launch the Module with the help of factory
32+
* @return address Contract address of the Module
33+
*/
34+
function deploy(bytes /* _data */) external returns(address) {
35+
if (setupCost > 0)
36+
require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided");
37+
return address(new WeightedVoteCheckpoint(msg.sender, address(polyToken)));
38+
}
39+
40+
/**
41+
* @notice Type of the Module factory
42+
*/
43+
function getTypes() external view returns(uint8[]) {
44+
uint8[] memory res = new uint8[](1);
45+
res[0] = 4;
46+
return res;
47+
}
48+
49+
/**
50+
* @notice Get the name of the Module
51+
*/
52+
function getName() public view returns(bytes32) {
53+
return name;
54+
}
55+
56+
/**
57+
* @notice Get the description of the Module
58+
*/
59+
function getDescription() external view returns(string) {
60+
return description;
61+
}
62+
63+
/**
64+
* @notice Get the title of the Module
65+
*/
66+
function getTitle() external view returns(string) {
67+
return title;
68+
}
69+
70+
/**
71+
* @notice Get the version of the Module
72+
*/
73+
function getVersion() external view returns(string) {
74+
return version;
75+
}
76+
77+
/**
78+
* @notice Get the setup cost of the module
79+
*/
80+
function getSetupCost() external view returns (uint256) {
81+
return setupCost;
82+
}
83+
84+
/**
85+
* @notice Get the Instructions that helped to used the module
86+
*/
87+
function getInstructions() external view returns(string) {
88+
return "Create a vote which allows token holders to vote on an issue with a weight proportional to their balances at the point the vote is created.";
89+
}
90+
91+
/**
92+
* @notice Get the tags related to the module factory
93+
*/
94+
function getTags() external view returns(bytes32[]) {
95+
bytes32[] memory availableTags = new bytes32[](0);
96+
return availableTags;
97+
}
98+
}

0 commit comments

Comments
 (0)