visual-identity/contracts/polls/PollManager.sol

294 lines
7.5 KiB
Solidity

pragma solidity ^0.4.23;
import "../common/Controlled.sol";
import "./LowLevelStringManipulator.sol";
import "../token/MiniMeToken.sol";
import "./IPollFactory.sol";
import "./SingleChoiceFactory.sol";
contract IPollContract {
function deltaVote(int _amount, bytes32 _ballot) public returns (bool _succes);
function pollType() public constant returns (bytes32);
function question() public constant returns (string);
}
contract PollManager is LowLevelStringManipulator, Controlled {
struct VoteLog {
bytes32 ballot;
uint amount;
}
struct Poll {
uint startBlock;
uint endBlock;
address token;
address pollContract;
bool canceled;
uint voters;
mapping(bytes32 => uint) votersPerBallot;
mapping(address => VoteLog) votes;
}
Poll[] _polls;
IPollFactory pollFactory;
MiniMeTokenFactory public tokenFactory;
MiniMeToken public token;
constructor(address _tokenFactory, address _token)
public {
tokenFactory = MiniMeTokenFactory(_tokenFactory);
token = MiniMeToken(_token);
pollFactory = IPollFactory(new SingleChoiceFactory());
}
modifier onlySNTHolder {
// TODO: require min number of tokens?
require(token.balanceOf(msg.sender) > 0);
_;
}
function addPoll(
uint _endBlock,
bytes _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;
string memory name;
string memory symbol;
(name, symbol) = getTokenNameSymbol(address(token));
string memory proposalName = strConcat(name, "_", uint2str(_idPoll));
string memory proposalSymbol = strConcat(symbol, "_", uint2str(_idPoll));
p.token = tokenFactory.createCloneToken(
address(token),
block.number - 1,
proposalName,
token.decimals(),
proposalSymbol,
true);
p.pollContract = pollFactory.create(_description);
require(p.pollContract != 0);
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).balanceOf(msg.sender);
return block.number >= p.startBlock &&
block.number <= p.endBlock &&
!p.canceled &&
balance != 0;
}
function vote(uint _idPoll, bytes32 _ballot) 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].ballot = _ballot;
p.votes[msg.sender].amount = amount;
p.voters++;
p.votersPerBallot[_ballot]++;
require(IPollContract(p.pollContract).deltaVote(int(amount), _ballot));
emit Vote(_idPoll, msg.sender, _ballot, amount);
}
function customVote(uint _idPoll, bytes32 _ballot, 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);
require(MiniMeToken(p.token).transferFrom(msg.sender, address(this), _amount));
p.votes[msg.sender].ballot = _ballot;
p.votes[msg.sender].amount = _amount;
p.voters++;
p.votersPerBallot[_ballot]++;
require(IPollContract(p.pollContract).deltaVote(int(_amount), _ballot));
emit Vote(_idPoll, msg.sender, _ballot, _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].amount;
bytes32 ballot = p.votes[msg.sender].ballot;
if (amount == 0) return;
require(IPollContract(p.pollContract).deltaVote(-int(amount), ballot));
p.votes[msg.sender].ballot = 0x00;
p.votes[msg.sender].amount = 0;
p.votersPerBallot[ballot]--;
p.voters--;
require(MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount));
emit Unvote(_idPoll, msg.sender, ballot, amount);
}
// Constant Helper Function
function nPolls()
public
view
returns(uint)
{
return _polls.length;
}
function poll(uint _idPoll)
public
view
returns(
uint _startBlock,
uint _endBlock,
address _token,
address _pollContract,
bool _canceled,
bytes32 _pollType,
string _question,
bool _finalized,
uint _totalCensus,
uint _voters
)
{
require(_idPoll < _polls.length);
Poll storage p = _polls[_idPoll];
_startBlock = p.startBlock;
_endBlock = p.endBlock;
_token = p.token;
_pollContract = p.pollContract;
_canceled = p.canceled;
_pollType = IPollContract(p.pollContract).pollType();
_question = getString(p.pollContract, bytes4(keccak256("question()")));
_finalized = (!p.canceled) && (block.number >= _endBlock);
_totalCensus = MiniMeToken(p.token).totalSupply();
_voters = p.voters;
}
function getVote(uint _idPoll, address _voter)
public
view
returns (bytes32 _ballot, uint _amount)
{
require(_idPoll < _polls.length);
Poll storage p = _polls[_idPoll];
_ballot = p.votes[_voter].ballot;
_amount = p.votes[_voter].amount;
}
function getVotesByBallot(uint _idPoll, bytes32 _ballot)
public
view
returns(uint voters, uint votes)
{
require(_idPoll < _polls.length);
Poll storage p = _polls[_idPoll];
voters = p.votersPerBallot[_ballot];
votes = p.votersPerBallot[_ballot];
}
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, bytes32 ballot, uint amount);
event Unvote(uint indexed idPoll, address indexed _voter, bytes32 ballot, uint amount);
event PollCanceled(uint indexed idPoll);
event PollCreated(uint indexed idPoll);
}