294 lines
7.5 KiB
Solidity
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);
|
|
}
|