2018-06-26 13:34:24 -04:00
|
|
|
pragma solidity ^0.4.23;
|
2018-06-26 10:19:04 -04:00
|
|
|
|
|
|
|
import "../common/Controlled.sol";
|
|
|
|
import "./LowLevelStringManipulator.sol";
|
|
|
|
import "../token/MiniMeToken.sol";
|
|
|
|
|
|
|
|
contract IPollContract {
|
|
|
|
function deltaVote(int _amount, bytes32 _ballot) returns (bool _succes);
|
|
|
|
function pollType() constant returns (bytes32);
|
|
|
|
function question() constant returns (string);
|
|
|
|
}
|
|
|
|
|
|
|
|
contract IPollFactory {
|
|
|
|
function create(bytes _description) returns(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
contract PollManager is LowLevelStringManipulator, Controlled {
|
|
|
|
|
|
|
|
struct VoteLog {
|
|
|
|
bytes32 ballot;
|
|
|
|
uint amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Poll {
|
|
|
|
uint startBlock;
|
|
|
|
uint endBlock;
|
|
|
|
address token;
|
|
|
|
address pollContract;
|
|
|
|
bool canceled;
|
2018-06-26 13:34:24 -04:00
|
|
|
uint voters;
|
|
|
|
mapping(bytes32 => uint) votersPerBallot;
|
2018-06-26 10:19:04 -04:00
|
|
|
mapping(address => VoteLog) votes;
|
|
|
|
}
|
|
|
|
|
|
|
|
Poll[] _polls;
|
|
|
|
|
|
|
|
MiniMeTokenFactory public tokenFactory;
|
|
|
|
MiniMeToken public token;
|
|
|
|
|
|
|
|
function PollManager(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 _startBlock,
|
|
|
|
uint _endBlock,
|
|
|
|
address _pollFactory,
|
|
|
|
bytes _description)
|
|
|
|
onlySNTHolder
|
|
|
|
returns (uint _idPoll)
|
|
|
|
{
|
|
|
|
if (_endBlock <= _startBlock) throw;
|
|
|
|
if (_endBlock <= getBlockNumber()) throw;
|
|
|
|
_idPoll = _polls.length;
|
|
|
|
_polls.length ++;
|
|
|
|
Poll p = _polls[ _idPoll ];
|
|
|
|
p.startBlock = _startBlock;
|
|
|
|
p.endBlock = _endBlock;
|
2018-06-26 13:34:24 -04:00
|
|
|
p.voters = 0;
|
2018-06-26 10:19:04 -04:00
|
|
|
|
2018-06-26 13:34:24 -04:00
|
|
|
var (name, symbol) = getTokenNameSymbol(address(token));
|
|
|
|
string memory proposalName = strConcat(name, "_", uint2str(_idPoll));
|
2018-06-26 10:19:04 -04:00
|
|
|
string memory proposalSymbol = strConcat(symbol, "_", uint2str(_idPoll));
|
|
|
|
|
|
|
|
p.token = tokenFactory.createCloneToken(
|
|
|
|
address(token),
|
|
|
|
_startBlock - 1,
|
|
|
|
proposalName,
|
|
|
|
token.decimals(),
|
|
|
|
proposalSymbol,
|
|
|
|
true);
|
|
|
|
|
|
|
|
|
|
|
|
p.pollContract = IPollFactory(_pollFactory).create(_description);
|
|
|
|
|
|
|
|
if (p.pollContract == 0) throw;
|
2018-06-26 13:34:24 -04:00
|
|
|
|
|
|
|
emit PollCreated(_idPoll);
|
2018-06-26 10:19:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function cancelPoll(uint _idPoll) onlyController {
|
|
|
|
if (_idPoll >= _polls.length) throw;
|
|
|
|
Poll p = _polls[_idPoll];
|
|
|
|
if (getBlockNumber() >= p.endBlock) throw;
|
|
|
|
p.canceled = true;
|
|
|
|
PollCanceled(_idPoll);
|
|
|
|
}
|
|
|
|
|
2018-06-26 13:34:24 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-06-26 10:19:04 -04:00
|
|
|
function vote(uint _idPoll, bytes32 _ballot) {
|
|
|
|
if (_idPoll >= _polls.length) throw;
|
|
|
|
Poll p = _polls[_idPoll];
|
|
|
|
if (getBlockNumber() < p.startBlock) throw;
|
|
|
|
if (getBlockNumber() >= p.endBlock) throw;
|
|
|
|
if (p.canceled) throw;
|
|
|
|
|
|
|
|
unvote(_idPoll);
|
|
|
|
|
|
|
|
uint amount = MiniMeToken(p.token).balanceOf(msg.sender);
|
|
|
|
|
|
|
|
if (amount == 0) throw;
|
|
|
|
|
|
|
|
|
|
|
|
// enableTransfers = true;
|
|
|
|
if (!MiniMeToken(p.token).transferFrom(msg.sender, address(this), amount)) throw;
|
|
|
|
// enableTransfers = false;
|
|
|
|
|
|
|
|
p.votes[msg.sender].ballot = _ballot;
|
|
|
|
p.votes[msg.sender].amount = amount;
|
2018-06-26 13:34:24 -04:00
|
|
|
|
|
|
|
p.voters++;
|
|
|
|
|
|
|
|
p.votersPerBallot[_ballot]++;
|
2018-06-26 10:19:04 -04:00
|
|
|
|
|
|
|
if (!IPollContract(p.pollContract).deltaVote(int(amount), _ballot)) throw;
|
|
|
|
|
|
|
|
Vote(_idPoll, msg.sender, _ballot, amount);
|
|
|
|
}
|
|
|
|
|
|
|
|
function unvote(uint _idPoll) {
|
|
|
|
if (_idPoll >= _polls.length) throw;
|
|
|
|
Poll p = _polls[_idPoll];
|
|
|
|
if (getBlockNumber() < p.startBlock) throw;
|
|
|
|
if (getBlockNumber() >= p.endBlock) throw;
|
|
|
|
if (p.canceled) throw;
|
|
|
|
|
|
|
|
uint amount = p.votes[msg.sender].amount;
|
|
|
|
bytes32 ballot = p.votes[msg.sender].ballot;
|
|
|
|
if (amount == 0) return;
|
|
|
|
|
|
|
|
if (!IPollContract(p.pollContract).deltaVote(-int(amount), ballot)) throw;
|
|
|
|
|
|
|
|
p.votes[msg.sender].ballot = 0;
|
|
|
|
p.votes[msg.sender].amount = 0;
|
2018-06-26 13:34:24 -04:00
|
|
|
p.votersPerBallot[ballot]--;
|
|
|
|
|
|
|
|
p.voters--;
|
2018-06-26 10:19:04 -04:00
|
|
|
|
|
|
|
// enableTransfers = true;
|
|
|
|
if (!MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount)) throw;
|
|
|
|
// enableTransfers = false;
|
|
|
|
|
|
|
|
Unvote(_idPoll, msg.sender, ballot, amount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constant Helper Function
|
|
|
|
|
|
|
|
function nPolls() constant returns(uint) {
|
|
|
|
return _polls.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
function poll(uint _idPoll) constant returns(
|
|
|
|
uint _startBlock,
|
|
|
|
uint _endBlock,
|
|
|
|
address _token,
|
|
|
|
address _pollContract,
|
|
|
|
bool _canceled,
|
|
|
|
bytes32 _pollType,
|
|
|
|
string _question,
|
|
|
|
bool _finalized,
|
2018-06-26 13:34:24 -04:00
|
|
|
uint _totalCensus,
|
|
|
|
uint _voters
|
2018-06-26 10:19:04 -04:00
|
|
|
) {
|
|
|
|
if (_idPoll >= _polls.length) throw;
|
|
|
|
Poll 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(sha3("question()")));
|
|
|
|
_finalized = (!p.canceled) && (getBlockNumber() >= _endBlock);
|
|
|
|
_totalCensus = MiniMeToken(p.token).totalSupply();
|
2018-06-26 13:34:24 -04:00
|
|
|
_voters = p.voters;
|
2018-06-26 10:19:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function getVote(uint _idPoll, address _voter) constant returns (bytes32 _ballot, uint _amount) {
|
|
|
|
if (_idPoll >= _polls.length) throw;
|
|
|
|
Poll p = _polls[_idPoll];
|
|
|
|
|
|
|
|
_ballot = p.votes[_voter].ballot;
|
|
|
|
_amount = p.votes[_voter].amount;
|
|
|
|
}
|
|
|
|
|
2018-06-26 13:34:24 -04:00
|
|
|
function getVotesByBallot(uint _idPoll, bytes32 _ballot)
|
|
|
|
public view returns(uint voters, uint votes) {
|
|
|
|
if (_idPoll >= _polls.length) throw;
|
|
|
|
Poll storage p = _polls[_idPoll];
|
|
|
|
|
|
|
|
voters = p.votersPerBallot[_ballot];
|
|
|
|
votes = p.votersPerBallot[_ballot];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-06-26 10:19:04 -04:00
|
|
|
function proxyPayment(address ) payable returns(bool) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function onTransfer(address , address , uint ) returns(bool) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function onApprove(address , address , uint ) returns(bool) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getBlockNumber() internal constant returns (uint) {
|
|
|
|
return block.number;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2018-06-26 13:34:24 -04:00
|
|
|
event PollCreated(uint indexed idPoll);
|
2018-06-26 10:19:04 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|