// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MoonCatKeyVote {
// Should the MoonCatRescue developers destroy their private key so that no future Genesis MoonCats can ever be released?
// true = Yes
// false = No
event VoteSubmitted(address voter, bool vote);
uint public voteStartTime = 0;
bool public voteCancelled = false;
mapping (address => bool) public hasVoted;
uint32 public yesVotes = 0;
uint32 public noVotes = 0;
//bytes32 public immutable voterRollSha256;
bytes32 public immutable merkleRoot;
address public immutable owner;
modifier onlyOwner {
require(msg.sender == owner, "Owner Only");
_;
}
modifier voteContractIsPending {
require(!voteCancelled, "Vote Contract Cancelled");
require(voteStartTime == 0, "Vote Already Started");
_;
}
modifier voteContractIsActive {
require(!voteCancelled, "Vote Contract Cancelled");
require(voteStartTime > 0, "Vote Not Started");
require(block.timestamp < (voteStartTime + 48 hours), "Vote Ended");
_;
}
modifier voteContractIsComplete {
require(!voteCancelled, "Vote Contract Cancelled");
require(voteStartTime > 0, "Vote Not Started");
require(block.timestamp > (voteStartTime + 48 hours), "Vote Not Ended");
_;
}
constructor(bytes32 merkleRoot_) {
merkleRoot = merkleRoot_;
owner = msg.sender;
}
function startVote() public onlyOwner voteContractIsPending {
voteStartTime = block.timestamp;
}
function cancelVote() public onlyOwner voteContractIsPending {
voteCancelled = true;
}
function getResult() public view voteContractIsComplete returns (bool) {
return (yesVotes > noVotes);
}
uint24 empty = 0;
function submitVote(bytes32[] calldata eligibilityProof, bool vote) public voteContractIsActive {
require(!hasVoted[msg.sender], "Duplicate Vote");
// https://github.com/miguelmota/merkletreejs-solidity/blob/master/contracts/MerkleProof.sol
bytes32 computedHash = keccak256(abi.encodePacked(msg.sender));
for (uint256 i = 0; i < eligibilityProof.length; i++) {
bytes32 proofElement = eligibilityProof[i];
if (computedHash < proofElement) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
require(computedHash == merkleRoot, "Ineligible Voter");
hasVoted[msg.sender] = true;
if(vote){
yesVotes++;
} else {
noVotes++;
}
emit VoteSubmitted(msg.sender, vote);
}
}
{
"compilationTarget": {
"browser/VotingContract.sol": "MoonCatKeyVote"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"bytes32","name":"merkleRoot_","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bool","name":"vote","type":"bool"}],"name":"VoteSubmitted","type":"event"},{"inputs":[],"name":"cancelVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getResult","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasVoted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"noVotes","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"eligibilityProof","type":"bytes32[]"},{"internalType":"bool","name":"vote","type":"bool"}],"name":"submitVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"voteCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voteStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yesVotes","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]