// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.6.12; import "./IntVoteInterface.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/cryptography/ECDSA.sol"; import "./GenesisProtocolLogic.sol"; /** * @title GenesisProtocol implementation -an organization's voting machine scheme. * Note : Reputation flow is deprected */ contract GenesisProtocol is IntVoteInterface, GenesisProtocolLogic { using ECDSA for bytes32; // Digest describing the data the user signs according EIP 712. // Needs to match what is passed to Metamask. bytes32 public constant DELEGATION_HASH_EIP712 = keccak256(abi.encodePacked( "address GenesisProtocolAddress", "bytes32 ProposalId", "uint256 Vote", "uint256 AmountToStake", "uint256 Nonce" )); mapping(address=>uint256) public stakesNonce; //stakes Nonce /** * @dev Constructor */ constructor(IERC20 _stakingToken) public // solhint-disable-next-line no-empty-blocks GenesisProtocolLogic(_stakingToken) { } /** * @dev Check that the proposal is votable * a proposal is votable if it is in one of the following states: * PreBoosted,Boosted,QuietEndingPeriod or Queued */ modifier votable(bytes32 _proposalId) override { require(_isVotable(_proposalId), "proposal is not votable"); _; } /** * @dev staking function * @param _proposalId id of the proposal * @param _vote NO(2) or YES(1). * @param _amount the betting amount * @return bool true - the proposal has been executed * false - otherwise. */ function stake(bytes32 _proposalId, uint256 _vote, uint256 _amount) external votable(_proposalId) returns(bool) { return _stake(_proposalId, _vote, _amount, msg.sender); } /** * @dev stakeWithSignature function * @param _proposalId id of the proposal * @param _vote NO(2) or YES(1). * @param _amount the betting amount * @param _nonce nonce value ,it is part of the signature to ensure that a signature can be received only once. * @param _signatureType signature type 1 - for web3.eth.sign 2 - for eth_signTypedData according to EIP #712. * @param _signature - signed data by the staker * @return bool true - the proposal has been executed * false - otherwise. */ function stakeWithSignature( bytes32 _proposalId, uint256 _vote, uint256 _amount, uint256 _nonce, uint256 _signatureType, bytes calldata _signature ) external votable(_proposalId) returns(bool) { // Recreate the digest the user signed bytes32 delegationDigest; if (_signatureType == 2) { delegationDigest = keccak256( abi.encodePacked( DELEGATION_HASH_EIP712, keccak256( abi.encodePacked( address(this), _proposalId, _vote, _amount, _nonce) ) ) ); } else { delegationDigest = keccak256( abi.encodePacked( address(this), _proposalId, _vote, _amount, _nonce) ).toEthSignedMessageHash(); } address staker = delegationDigest.recover(_signature); //a garbage staker address due to wrong signature will revert due to lack of approval and funds. require(staker != address(0), "staker address cannot be 0"); require(stakesNonce[staker] == _nonce, "wrong nonce"); stakesNonce[staker] = stakesNonce[staker].add(1); return _stake(_proposalId, _vote, _amount, staker); } /** * @dev voting function * @param _proposalId id of the proposal * @param _vote NO(2) or YES(1). * @param _amount the reputation amount to vote with . if _amount == 0 it will use all voter reputation. * @param _voter voter address * @return bool true - the proposal has been executed * false - otherwise. */ function vote(bytes32 _proposalId, uint256 _vote, uint256 _amount, address _voter) external votable(_proposalId) override returns(bool) { Proposal storage proposal = proposals[_proposalId]; Parameters memory params = parameters[proposal.paramsHash]; address voter; if (params.voteOnBehalf != address(0)) { require(msg.sender == params.voteOnBehalf, "voter is not authorized"); voter = _voter; } else { voter = msg.sender; } return internalVote(_proposalId, voter, _vote, _amount); } /** * @dev Cancel the vote of the msg.sender. * cancel vote is not allow in genesisProtocol so this function doing nothing. * This function is here in order to comply to the IntVoteInterface . */ function cancelVote(bytes32 _proposalId) external votable(_proposalId) override { //this is not allowed return; } /** * @dev register a new proposal with the given parameters. Every proposal has a unique ID which is being * generated by calculating keccak256 of a incremented counter. * @param _proposer address */ function propose(uint256, bytes32 _paramsHash, address _proposer, address _organization) external override returns(bytes32) { return _propose(_paramsHash, _proposer, _organization); } /** * @dev execute check if the proposal has been decided, and if so, execute the proposal * @param _proposalId the id of the proposal * @return bool true - the proposal has been executed * false - otherwise. */ function execute(bytes32 _proposalId) external votable(_proposalId) returns(bool) { return _execute(_proposalId); } /** * @dev getNumberOfChoices returns the number of choices possible in this proposal * @return uint256 that contains number of choices */ function getNumberOfChoices(bytes32) external view override returns(uint256) { return NUM_OF_CHOICES; } /** * @dev getProposalTimes returns proposals times variables. * @param _proposalId id of the proposal * @return times array */ function getProposalTimes(bytes32 _proposalId) external view returns(uint[3] memory times) { return proposals[_proposalId].times; } /** * @dev voteInfo returns the vote and the amount of reputation of the user committed to this proposal * @param _proposalId the ID of the proposal * @param _voter the address of the voter * @return uint256 vote - the voters vote * uint256 reputation - amount of reputation committed by _voter to _proposalId */ function voteInfo(bytes32 _proposalId, address _voter) external view returns(uint, uint) { uint256 voter = proposals[_proposalId].voters[_voter]; return (voter >> VOTE_BIT_INDEX, uint256(uint128(voter))); } /** * @dev voteStatus returns the reputation voted for a proposal for a specific voting choice. * @param _proposalId the ID of the proposal * @param _choice the index in the * @return voted reputation for the given choice */ function voteStatus(bytes32 _proposalId, uint256 _choice) external view override returns(uint256) { return proposals[_proposalId].votes[_choice]; } /** * @dev isVotable check if the proposal is votable * @param _proposalId the ID of the proposal * @return bool true or false */ function isVotable(bytes32 _proposalId) external view override returns(bool) { return _isVotable(_proposalId); } /** * @dev proposalStatus return the total votes and stakes for a given proposal * @param _proposalId the ID of the proposal * @return uint256 votes YES * @return uint256 votes NO * @return uint256 total stakes YES * @return uint256 total stakes NO */ function proposalStatus(bytes32 _proposalId) external view returns(uint256, uint256, uint256, uint256) { return ( proposals[_proposalId].votes[YES], proposals[_proposalId].votes[NO], proposals[_proposalId].stakes[YES], proposals[_proposalId].stakes[NO] ); } /** * @dev getProposalOrganization return the organizationId for a given proposal * @param _proposalId the ID of the proposal * @return bytes32 organization identifier */ function getProposalOrganization(bytes32 _proposalId) external view returns(bytes32) { return (proposals[_proposalId].organizationId); } /** * @dev getStaker return the vote and stake amount for a given proposal and staker * @param _proposalId the ID of the proposal * @param _staker staker address * @return uint256 vote * @return uint256 amount */ function getStaker(bytes32 _proposalId, address _staker) external view returns(uint256, uint256) { Staker memory staker = proposals[_proposalId].stakers[_staker]; return (uint256(uint8(staker.amount4BountyAndVote >> VOTE_BIT_INDEX)), staker.amount); } /** * @dev voteStake return the amount stakes for a given proposal and vote * @param _proposalId the ID of the proposal * @param _vote vote number * @return uint256 stake amount */ function voteStake(bytes32 _proposalId, uint256 _vote) external view returns(uint256) { return proposals[_proposalId].stakes[_vote]; } /** * @dev voteStake return the winningVote for a given proposal * @param _proposalId the ID of the proposal * @return uint256 winningVote */ function winningVote(bytes32 _proposalId) external view returns(uint256) { return proposals[_proposalId].winningVote; } /** * @dev voteStake return the state for a given proposal * @param _proposalId the ID of the proposal * @return ProposalState proposal state */ function state(bytes32 _proposalId) external view returns(ProposalState) { return proposals[_proposalId].state; } /** * @dev isAbstainAllow returns if the voting machine allow abstain (0) * @return bool true or false */ function isAbstainAllow() external pure override returns(bool) { return false; } /** * @dev getAllowedRangeOfChoices returns the allowed range of choices for a voting machine. * @return min - minimum number of choices max - maximum number of choices */ function getAllowedRangeOfChoices() external pure override returns(uint256 min, uint256 max) { return (YES, NO); } /** * @dev score return the proposal score * @param _proposalId the ID of the proposal * @return uint256 proposal score. */ function score(bytes32 _proposalId) public view returns(uint256) { return _score(_proposalId); } }