Browse Source

PostVoting init

develop
Ezerous 4 years ago
parent
commit
072b6eb35f
  1. 3
      packages/concordia-app/src/options/drizzleOptions.js
  2. 40
      packages/concordia-contracts/contracts/Forum.sol
  3. 115
      packages/concordia-contracts/contracts/PostVoting.sol
  4. 38
      packages/concordia-contracts/contracts/Voting.sol
  5. 6
      packages/concordia-contracts/migrations/2_deploy_contracts.js

3
packages/concordia-app/src/options/drizzleOptions.js

@ -9,7 +9,8 @@ const drizzleOptions = {
contracts, contracts,
events: { events: {
Forum: ['UserSignedUp', 'UsernameUpdated', 'TopicCreated', 'PostCreated'], Forum: ['UserSignedUp', 'UsernameUpdated', 'TopicCreated', 'PostCreated'],
Voting: ['PollCreated', 'UserVoted'], Voting: ['PollCreated', 'UserVotedPoll'],
PostVoting: ['UserVotedPost'],
}, },
reloadWindowOnNetworkChange: true, reloadWindowOnNetworkChange: true,
reloadWindowOnAccountChange: true, // We need it to reinitialize breeze and create new Orbit databases reloadWindowOnAccountChange: true, // We need it to reinitialize breeze and create new Orbit databases

40
packages/concordia-contracts/contracts/Forum.sol

@ -117,7 +117,7 @@ contract Forum {
function createPost(uint topicID) public returns (uint) { function createPost(uint topicID) public returns (uint) {
require(hasUserSignedUp(msg.sender)); // Only registered users can create posts require(hasUserSignedUp(msg.sender)); // Only registered users can create posts
require(topicID<numTopics); // Only allow posting to a topic that exists require(topicExists(topicID)); // Only allow posting to a topic that exists
uint postID = numPosts++; uint postID = numPosts++;
posts[postID] = Post(postID, msg.sender, block.timestamp, topicID); posts[postID] = Post(postID, msg.sender, block.timestamp, topicID);
topics[topicID].postIDs.push(postID); topics[topicID].postIDs.push(postID);
@ -126,37 +126,51 @@ contract Forum {
return postID; return postID;
} }
// Verify that topic exists
function topicExists(uint topicID) public view returns (bool) {
return topicID<numTopics;
}
// Verify that post exists
function postExists(uint postID) public view returns (bool) {
return postID<numPosts;
}
function getNumberOfTopics() public view returns (uint) { function getNumberOfTopics() public view returns (uint) {
return numTopics; return numTopics;
} }
function getNumberOfPosts() public view returns (uint) {
return numPosts;
}
function getTopic(uint topicID) public view returns (address, string memory, uint, uint[] memory) { function getTopic(uint topicID) public view returns (address, string memory, uint, uint[] memory) {
require(topicID<numTopics); require(topicExists(topicID));
return ( return (
topics[topicID].author, topics[topicID].author,
users[topics[topicID].author].username, users[topics[topicID].author].username,
topics[topicID].timestamp, topics[topicID].timestamp,
topics[topicID].postIDs topics[topicID].postIDs
); );
} }
function getTopicPosts(uint topicID) public view returns (uint[] memory) { function getTopicPosts(uint topicID) public view returns (uint[] memory) {
require(topicID<numTopics); // Topic should exist require(topicExists(topicID)); // Topic should exist
return topics[topicID].postIDs; return topics[topicID].postIDs;
} }
function getTopicAuthor(uint topicID) public view returns (address) { function getTopicAuthor(uint topicID) public view returns (address) {
require(topicID<numTopics); // Topic should exist require(topicExists(topicID)); // Topic should exist
return topics[topicID].author; return topics[topicID].author;
} }
function getPost(uint postID) public view returns (address, string memory, uint, uint) { function getPost(uint postID) public view returns (address, string memory, uint, uint) {
require(postID<numPosts); require(postExists(postID));
return ( return (
posts[postID].author, posts[postID].author,
users[posts[postID].author].username, users[posts[postID].author].username,
posts[postID].timestamp, posts[postID].timestamp,
posts[postID].topicID posts[postID].topicID
); );
} }
} }

115
packages/concordia-contracts/contracts/PostVoting.sol

@ -0,0 +1,115 @@
//SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
import "./Forum.sol";
contract PostVoting {
Forum public forum;
constructor(Forum addr) {
forum = Forum(addr);
}
enum Option { NONE, UP, DOWN } // NONE -> 0, UP -> 1, DOWN -> 2
Option constant defaultOption = Option.NONE;
function getDefaultChoice() public pure returns (uint) {
return uint(defaultOption);
}
struct PostBallot {
mapping (address => Option) votes;
mapping (Option => address[]) voters;
}
mapping (uint => PostBallot) postBallots;
event UserVotedPost(address userAddress, uint postID, Option option);
function getVote(uint postID, address voter) public view returns (Option) {
require(forum.postExists(postID));
return postBallots[postID].votes[voter];
}
// Gets vote count for a specific option (Option.UP/ Option.DOWN)
function getVoteCount(uint postID, Option option) private view returns (uint) {
require(forum.postExists(postID));
return (postBallots[postID].voters[option].length);
}
function getUpvoteCount(uint postID) public view returns (uint) {
return (getVoteCount(postID, Option.UP));
}
function getDownvoteCount(uint postID) public view returns (uint) {
return (getVoteCount(postID, Option.DOWN));
}
// Gets voters for a specific option (Option.UP/ Option.DOWN)
function getVoters(uint postID, Option option) private view returns (address[] memory) {
require(forum.postExists(postID));
return (postBallots[postID].voters[option]);
}
function getUpvoters(uint postID) public view returns (address[] memory) {
return (getVoters(postID, Option.UP));
}
function getDownvoters(uint postID) public view returns (address[] memory) {
return (getVoters(postID, Option.DOWN));
}
function getVoterIndex(uint postID, address voter) private view returns (uint) {
require(forum.hasUserSignedUp(voter));
require(forum.postExists(postID));
PostBallot storage postBallot = postBallots[postID];
Option votedOption = getVote(postID, voter);
address[] storage optionVoters = postBallot.voters[votedOption];
for (uint voterIndex = 0; voterIndex < optionVoters.length; voterIndex++)
if (optionVoters[voterIndex] == voter)
return voterIndex;
revert("Couldn't find voter's index!");
}
function vote(uint postID, Option option) private {
require(forum.hasUserSignedUp(msg.sender));
require(forum.postExists(postID)); // Only allow voting if post exists
PostBallot storage postBallot = postBallots[postID];
address voter = msg.sender;
Option prevOption = postBallot.votes[voter];
if(prevOption == option)
return;
// Remove previous vote if exists
if(prevOption != Option.NONE){
uint voterIndex = getVoterIndex(postID, voter);
// Swap with last voter address and delete vote
postBallot.voters[prevOption][voterIndex] = postBallot.voters[prevOption][postBallot.voters[prevOption].length - 1];
postBallot.voters[prevOption].pop();
}
// Add new vote
if(option != Option.NONE)
postBallot.voters[option].push(voter);
postBallot.votes[voter] = option;
emit UserVotedPost(voter, postID, option);
}
function upvote(uint postID) public{
vote(postID, Option.UP);
}
function downvote(uint postID) public{
vote(postID, Option.DOWN);
}
function unvote(uint postID) public{
vote(postID, Option.NONE);
}
}

38
packages/concordia-contracts/contracts/Voting.sol

@ -23,10 +23,10 @@ contract Voting {
mapping (uint => Poll) polls; mapping (uint => Poll) polls;
event PollCreated(uint topicID); event PollCreated(uint topicID);
event UserVoted(address userAddress); event UserVotedPoll(address userAddress, uint topicID, uint vote);
// Verify that poll exists // Verifies that a poll exists
function isPollExistent(uint topicID) public view returns (bool) { function pollExists(uint topicID) public view returns (bool) {
if (polls[topicID].timestamp != 0) if (polls[topicID].timestamp != 0)
return true; return true;
return false; return false;
@ -34,9 +34,9 @@ contract Voting {
function createPoll(uint topicID, uint numOptions, string memory dataHash, bool enableVoteChanges) public returns (uint) { function createPoll(uint topicID, uint numOptions, string memory dataHash, bool enableVoteChanges) public returns (uint) {
require(forum.hasUserSignedUp(msg.sender)); // Only registered users can create polls require(forum.hasUserSignedUp(msg.sender)); // Only registered users can create polls
require(topicID<forum.getNumberOfTopics()); // Only allow poll creation if topic exists require(forum.topicExists(topicID)); // Only allow poll creation if topic exists
require (forum.getTopicAuthor(topicID) == msg.sender); // Only allow poll creation from the author of the topic require (forum.getTopicAuthor(topicID) == msg.sender); // Only allow poll creation from the author of the topic
require(!isPollExistent(topicID)); // Only allow poll creation if it doesn't already exist require(!pollExists(topicID)); // Only allow poll creation if it doesn't already exist
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
poll.topicID = topicID; poll.topicID = topicID;
@ -50,27 +50,27 @@ contract Voting {
} }
function getPollInfo(uint topicID) public view returns (uint, string memory, uint, uint) { function getPollInfo(uint topicID) public view returns (uint, string memory, uint, uint) {
require(isPollExistent(topicID)); require(pollExists(topicID));
uint totalVotes = getTotalVotes(topicID); uint totalVotes = getTotalVotes(topicID);
return ( return (
polls[topicID].numOptions, polls[topicID].numOptions,
polls[topicID].dataHash, polls[topicID].dataHash,
polls[topicID].timestamp, polls[topicID].timestamp,
totalVotes totalVotes
); );
} }
function isOptionValid(uint topicID, uint option) public view returns (bool) { function isOptionValid(uint topicID, uint option) public view returns (bool) {
require(isPollExistent(topicID)); require(pollExists(topicID));
if (option <= polls[topicID].numOptions) // Option 0 is valid as well (no option chosen) if (option <= polls[topicID].numOptions) // Option 0 is valid as well (no option chosen)
return true; return true;
return false; return false;
} }
function hasVoted(uint topicID, address voter) public view returns (bool) { function hasVoted(uint topicID, address voter) public view returns (bool) {
require(isPollExistent(topicID)); require(pollExists(topicID));
if (polls[topicID].votes[voter] != 0) if (polls[topicID].votes[voter] != 0)
return true; return true;
return false; return false;
@ -83,13 +83,13 @@ contract Voting {
// Gets vote count for a specific option // Gets vote count for a specific option
function getVoteCount(uint topicID, uint option) public view returns (uint) { function getVoteCount(uint topicID, uint option) public view returns (uint) {
require(isPollExistent(topicID)); require(pollExists(topicID));
require(isOptionValid(topicID, option)); require(isOptionValid(topicID, option));
return (polls[topicID].voters[option].length); return (polls[topicID].voters[option].length);
} }
function getTotalVotes(uint topicID) public view returns (uint) { function getTotalVotes(uint topicID) public view returns (uint) {
require(isPollExistent(topicID)); require(pollExists(topicID));
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
uint totalVotes = 0; uint totalVotes = 0;
@ -102,12 +102,12 @@ contract Voting {
// Gets voters for a specific option // Gets voters for a specific option
function getVoters(uint topicID, uint option) public view returns (address[] memory) { function getVoters(uint topicID, uint option) public view returns (address[] memory) {
require(isPollExistent(topicID)); require(pollExists(topicID));
return (polls[topicID].voters[option]); return (polls[topicID].voters[option]);
} }
function getVoterIndex(uint topicID, address voter) public view returns (uint) { function getVoterIndex(uint topicID, address voter) public view returns (uint) {
require(isPollExistent(topicID)); require(pollExists(topicID));
require(hasVoted(topicID, voter)); require(hasVoted(topicID, voter));
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
uint votedOption = getVote(topicID, voter); uint votedOption = getVote(topicID, voter);
@ -122,7 +122,7 @@ contract Voting {
function vote(uint topicID, uint option) public { function vote(uint topicID, uint option) public {
require(forum.hasUserSignedUp(msg.sender)); require(forum.hasUserSignedUp(msg.sender));
require(isPollExistent(topicID)); require(pollExists(topicID));
require(isOptionValid(topicID, option)); require(isOptionValid(topicID, option));
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
address voter = msg.sender; address voter = msg.sender;
@ -134,7 +134,7 @@ contract Voting {
if(prevOption == 0){ if(prevOption == 0){
poll.voters[option].push(voter); poll.voters[option].push(voter);
poll.votes[voter] = option; poll.votes[voter] = option;
emit UserVoted(voter); emit UserVotedPoll(voter, topicID, option);
} }
else if (poll.enableVoteChanges){ else if (poll.enableVoteChanges){
uint voterIndex = getVoterIndex(topicID, voter); uint voterIndex = getVoterIndex(topicID, voter);
@ -144,7 +144,7 @@ contract Voting {
if(option != 0) if(option != 0)
poll.voters[option].push(voter); poll.voters[option].push(voter);
poll.votes[voter] = option; poll.votes[voter] = option;
emit UserVoted(voter); emit UserVotedPoll(voter, topicID, option);
} }
} }
} }

6
packages/concordia-contracts/migrations/2_deploy_contracts.js

@ -1,7 +1,11 @@
const Forum = artifacts.require('Forum'); const Forum = artifacts.require('Forum');
const Voting = artifacts.require('Voting'); const Voting = artifacts.require('Voting');
const PostVoting = artifacts.require('PostVoting');
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
module.exports = function (deployer) { module.exports = function (deployer) {
deployer.deploy(Forum).then((forum) => deployer.deploy(Voting, forum.address)); deployer.deploy(Forum).then((forum) => {
deployer.deploy(Voting, forum.address);
deployer.deploy(PostVoting, forum.address);
});
}; };

Loading…
Cancel
Save