all files / contracts/examples/ UnirepVoting.sol

0% Statements 0/40
0% Branches 0/40
0% Functions 0/6
0% Lines 0/54
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168                                                                                                                                                                                                                                                                                                                                               
// SPDX-License-Identifier: MIT
 
pragma solidity ^0.8.0;
import {Unirep} from '.././Unirep.sol';
 
// Uncomment this line to use console.log
// import 'hardhat/console.sol';
import {ReputationVerifierHelper} from '../verifierHelpers/ReputationVerifierHelper.sol';
import {EpochKeyVerifierHelper} from '../verifierHelpers/EpochKeyVerifierHelper.sol';
import {VotingPrizeNFT} from './VotingPrizeNFT.sol';
 
enum Option {
    UP,
    DOWN
}
 
/// @title UnirepVoting
contract UnirepVoting {
    Unirep public unirep;
    ReputationVerifierHelper public repHelper;
    EpochKeyVerifierHelper public epochKeyHelper;
    VotingPrizeNFT public nft;
 
    uint public immutable numProjects;
 
    // A list of [upvote count, downvote count] for each project.
    uint[][] public projectData;
    // A list of scores for each project that project ID is the index.
    int[] public scores;
 
    int public winnerScore;
    bool foundWinner = false;
 
    // A mapping from project ID to a list of epoch keys of joined hackers.
    mapping(uint256 => uint256[]) public participants;
    // A mapping from project ID to a count of joined hackers.
    mapping(uint256 => uint256) public counts;
    mapping(uint256 => uint256) public voted;
    mapping(uint256 => bool) claimed;
 
    constructor(
        Unirep _unirep,
        ReputationVerifierHelper _repHelper,
        EpochKeyVerifierHelper _epochKeyHelper,
        VotingPrizeNFT _nft,
        uint8 _numProjects,
        uint48 _epochLength
    ) {
        // set unirep address
        unirep = _unirep;
 
        // set reputation verifier
        repHelper = _repHelper;
 
        // set epoch key verifier helper
        epochKeyHelper = _epochKeyHelper;
 
        nft = _nft;
 
        unirep.attesterSignUp(_epochLength);
 
        // how many projects that hackers can join.
        numProjects = _numProjects;
        scores = new int[](numProjects);
        projectData = new uint[][](numProjects);
        for (uint i; i < numProjects; i++) {
            projectData[i] = new uint256[](2);
        }
    }
 
    // sign up users in this app
    function userSignUp(
        uint256[] calldata publicSignals,
        uint256[8] calldata proof
    ) public {
        unirep.userSignUp(publicSignals, proof);
    }
 
    function joinProject(
        uint256 projectID,
        uint256[] memory publicSignals,
        uint256[8] memory proof
    ) public {
        require(projectID < numProjects, 'projectID out of range');
        EpochKeyVerifierHelper.EpochKeySignals memory signals = epochKeyHelper
            .verifyAndCheck(publicSignals, proof);
 
        require(signals.revealNonce == true, 'Voteathon: should reveal nonce');
        require(signals.nonce == 0, 'Voteathon: invalid nonce');
        require(
            counts[projectID] < 10,
            'Voteathon: maximum participants in a project'
        );
        participants[projectID].push(signals.epochKey);
        // give user data if there is attestation before
        uint48 epoch = unirep.attesterCurrentEpoch(uint160(address(this)));
        require(epoch == 0, 'Voteathon: not join epoch');
        uint256[] memory data = projectData[projectID];
        for (uint256 i = 0; i < data.length; i++) {
            unirep.attest(signals.epochKey, epoch, i, data[i]);
        }
        counts[projectID] += 1;
    }
 
    function vote(
        uint256 projectID,
        Option option,
        uint256[] calldata publicSignals,
        uint256[8] calldata proof
    ) public {
        require(projectID < numProjects, 'projectID out of range');
 
        EpochKeyVerifierHelper.EpochKeySignals memory signals = epochKeyHelper
            .verifyAndCheck(publicSignals, proof);
 
        require(signals.revealNonce == true, 'reveal nonce wrong');
        require(signals.nonce == 1, 'nonce wrong');
        require(signals.epoch == 0, 'invalid epoch to vote');
 
        projectData[projectID][uint(option)] += 1;
 
        voted[signals.epochKey] += 1;
        if (option == Option.UP) scores[projectID] += 1;
        else if (option == Option.DOWN) scores[projectID] -= 1;
 
        uint[] memory members = participants[projectID];
        uint48 epoch = unirep.attesterCurrentEpoch(uint160(address(this)));
        require(epoch == 0, 'not voting epoch');
        for (uint256 i = 0; i < members.length; i++) {
            unirep.attest(members[i], epoch, uint(option), 1);
        }
    }
 
    function claimPrize(
        address receiver,
        uint256[] calldata publicSignals,
        uint256[8] calldata proof
    ) public {
        uint160 attesterId = uint160(address(this));
        require(unirep.attesterCurrentEpoch(attesterId) > 0);
        ReputationVerifierHelper.ReputationSignals memory signals = repHelper
            .verifyAndCheck(publicSignals, proof);
 
        require(signals.epoch > 0, 'invalid epoch to claim prize');
        require(signals.revealNonce == true, 'reveal nonce wrong');
        require(signals.nonce == 1, 'nonce wrong');
 
        require(!claimed[signals.epochKey], 'Already claimed');
        if (!foundWinner) {
            _findWinner();
        }
        require(int(signals.minRep) >= winnerScore, 'Insufficient score');
        nft.awardItem(receiver);
        claimed[signals.epochKey] = true;
    }
 
    function _findWinner() internal {
        int highest = 0;
        for (uint256 i = 0; i < numProjects; i++) {
            if (scores[i] > highest) {
                highest = scores[i];
            }
        }
        winnerScore = highest;
        foundWinner = true;
    }
}