257 lines
6.1 KiB
Solidity
257 lines
6.1 KiB
Solidity
pragma solidity ^0.4.23;
|
|
|
|
import "../common/Controlled.sol";
|
|
import "../token/MiniMeToken.sol";
|
|
|
|
|
|
|
|
contract PollManager is Controlled {
|
|
|
|
struct Poll {
|
|
uint startBlock;
|
|
uint endBlock;
|
|
address token;
|
|
bool canceled;
|
|
uint voters;
|
|
string description;
|
|
mapping(address => uint) votes;
|
|
uint results;
|
|
uint qvResults;
|
|
}
|
|
|
|
Poll[] _polls;
|
|
|
|
MiniMeTokenFactory public tokenFactory;
|
|
MiniMeToken public token;
|
|
|
|
constructor(address _tokenFactory, address _token)
|
|
public {
|
|
tokenFactory = MiniMeTokenFactory(_tokenFactory);
|
|
token = MiniMeToken(_token);
|
|
}
|
|
|
|
modifier onlySNTHolder {
|
|
// TODO: require min number of tokens?
|
|
require(token.balanceOf(msg.sender) > 0);
|
|
_;
|
|
}
|
|
|
|
function addPoll(
|
|
uint _endBlock,
|
|
string _description)
|
|
public
|
|
onlySNTHolder
|
|
returns (uint _idPoll)
|
|
{
|
|
require(_endBlock > block.number);
|
|
|
|
_idPoll = _polls.length;
|
|
_polls.length ++;
|
|
Poll storage p = _polls[ _idPoll ];
|
|
p.startBlock = block.number;
|
|
p.endBlock = _endBlock;
|
|
p.voters = 0;
|
|
p.description = _description;
|
|
|
|
p.token = tokenFactory.createCloneToken(
|
|
address(token),
|
|
block.number - 1,
|
|
"SNT Voting Token",
|
|
token.decimals(),
|
|
"SVT",
|
|
true);
|
|
|
|
emit PollCreated(_idPoll);
|
|
}
|
|
|
|
function cancelPoll(uint _idPoll)
|
|
onlyController
|
|
public
|
|
{
|
|
require(_idPoll < _polls.length);
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
|
|
require(p.endBlock < block.number);
|
|
|
|
p.canceled = true;
|
|
emit PollCanceled(_idPoll);
|
|
}
|
|
|
|
function canVote(uint _idPoll)
|
|
public
|
|
view
|
|
returns(bool)
|
|
{
|
|
if(_idPoll >= _polls.length) return false;
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
uint balance = MiniMeToken(p.token).balanceOfAt(msg.sender, p.startBlock - 1);
|
|
|
|
return block.number >= p.startBlock &&
|
|
block.number <= p.endBlock &&
|
|
!p.canceled &&
|
|
balance != 0;
|
|
}
|
|
|
|
function sqrt(uint256 x) public pure returns (uint256 y) {
|
|
uint256 z = (x + 1) / 2;
|
|
y = x;
|
|
while (z < y) {
|
|
y = z;
|
|
z = (x / z + z) / 2;
|
|
}
|
|
}
|
|
|
|
function vote(uint _idPoll) public {
|
|
require(_idPoll < _polls.length);
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
|
|
require(block.number >= p.startBlock && block.number < p.endBlock && !p.canceled);
|
|
|
|
unvote(_idPoll);
|
|
|
|
uint amount = MiniMeToken(p.token).balanceOf(msg.sender);
|
|
|
|
require(amount != 0);
|
|
require(MiniMeToken(p.token).transferFrom(msg.sender, address(this), amount));
|
|
|
|
p.votes[msg.sender] = amount;
|
|
p.voters++;
|
|
|
|
p.results += amount;
|
|
p.qvResults += sqrt(amount / 1 ether);
|
|
|
|
emit Vote(_idPoll, msg.sender, amount);
|
|
}
|
|
|
|
function customVote(uint _idPoll, uint _amount) public {
|
|
require(_idPoll < _polls.length);
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
|
|
require(block.number >= p.startBlock && block.number < p.endBlock && !p.canceled);
|
|
|
|
unvote(_idPoll);
|
|
|
|
uint balance = MiniMeToken(p.token).balanceOf(msg.sender);
|
|
|
|
require(balance != 0 && balance >= _amount && _amount != 0);
|
|
require(MiniMeToken(p.token).transferFrom(msg.sender, address(this), _amount));
|
|
|
|
p.votes[msg.sender] = _amount;
|
|
p.voters++;
|
|
|
|
p.results += _amount;
|
|
p.qvResults += sqrt(_amount / 1 ether);
|
|
|
|
emit Vote(_idPoll, msg.sender, _amount);
|
|
}
|
|
|
|
function unvote(uint _idPoll) public {
|
|
require(_idPoll < _polls.length);
|
|
Poll storage p = _polls[_idPoll];
|
|
|
|
require(block.number >= p.startBlock && block.number < p.endBlock && !p.canceled);
|
|
|
|
uint amount = p.votes[msg.sender];
|
|
if (amount == 0) return;
|
|
|
|
assert(p.voters != 0);
|
|
assert(amount <= p.results);
|
|
|
|
p.votes[msg.sender] = 0;
|
|
|
|
p.voters--;
|
|
p.results -= amount;
|
|
p.qvResults -= sqrt(amount / 1 ether);
|
|
|
|
require(MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount));
|
|
|
|
emit Unvote(_idPoll, msg.sender, amount);
|
|
}
|
|
|
|
// Constant Helper Function
|
|
|
|
function nPolls()
|
|
public
|
|
view
|
|
returns(uint)
|
|
{
|
|
return _polls.length;
|
|
}
|
|
|
|
function poll(uint _idPoll)
|
|
public
|
|
view
|
|
returns(
|
|
uint _startBlock,
|
|
uint _endBlock,
|
|
bool _canVote,
|
|
address _token,
|
|
bool _canceled,
|
|
string _description,
|
|
bool _finalized,
|
|
uint _totalCensus,
|
|
uint _voters,
|
|
uint _results,
|
|
uint _qvResults
|
|
)
|
|
{
|
|
require(_idPoll < _polls.length);
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
|
|
_startBlock = p.startBlock;
|
|
_endBlock = p.endBlock;
|
|
_token = p.token;
|
|
_canceled = p.canceled;
|
|
_canVote = canVote(_idPoll);
|
|
_description = p.description;
|
|
_finalized = (!p.canceled) && (block.number >= _endBlock);
|
|
_totalCensus = MiniMeToken(p.token).totalSupply();
|
|
_voters = p.voters;
|
|
_results = p.results;
|
|
_qvResults = p.qvResults;
|
|
}
|
|
|
|
function getVote(uint _idPoll, address _voter)
|
|
public
|
|
view
|
|
returns (uint)
|
|
{
|
|
require(_idPoll < _polls.length);
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
return p.votes[_voter];
|
|
}
|
|
|
|
function proxyPayment(address )
|
|
payable
|
|
returns(bool) {
|
|
return false;
|
|
}
|
|
|
|
|
|
function onTransfer(address , address , uint )
|
|
public
|
|
pure
|
|
returns(bool)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
function onApprove(address , address , uint )
|
|
public
|
|
pure
|
|
returns(bool) {
|
|
return true;
|
|
}
|
|
|
|
event Vote(uint indexed idPoll, address indexed _voter, uint amount);
|
|
event Unvote(uint indexed idPoll, address indexed _voter, uint amount);
|
|
event PollCanceled(uint indexed idPoll);
|
|
event PollCreated(uint indexed idPoll);
|
|
}
|