/** * Source Code first verified at https://etherscan.io on Monday, April 1, 2019 (UTC) */ // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/Reputation.sol pragma solidity ^0.5.4; /** * @title Reputation system * @dev A DAO has Reputation System which allows peers to rate other peers in order to build trust . * A reputation is use to assign influence measure to a DAO'S peers. * Reputation is similar to regular tokens but with one crucial difference: It is non-transferable. * The Reputation contract maintain a map of address to reputation value. * It provides an onlyOwner functions to mint and burn reputation _to (or _from) a specific address. */ contract Reputation is Ownable { uint8 public decimals = 18; //Number of decimals of the smallest unit // Event indicating minting of reputation to an address. event Mint(address indexed _to, uint256 _amount); // Event indicating burning of reputation for an address. event Burn(address indexed _from, uint256 _amount); /// @dev `Checkpoint` is the structure that attaches a block number to a /// given value, the block number attached is the one that last changed the /// value struct Checkpoint { // `fromBlock` is the block number that the value was generated from uint128 fromBlock; // `value` is the amount of reputation at a specific block number uint128 value; } // `balances` is the map that tracks the balance of each address, in this // contract when the balance changes the block number that the change // occurred is also included in the map mapping (address => Checkpoint[]) balances; // Tracks the history of the `totalSupply` of the reputation Checkpoint[] totalSupplyHistory; /// @notice Constructor to create a Reputation constructor( ) public { } /// @dev This function makes it easy to get the total number of reputation /// @return The total number of reputation function totalSupply() public view returns (uint256) { return totalSupplyAt(block.number); } //////////////// // Query balance and totalSupply in History //////////////// /** * @dev return the reputation amount of a given owner * @param _owner an address of the owner which we want to get his reputation */ function balanceOf(address _owner) public view returns (uint256 balance) { return balanceOfAt(_owner, block.number); } /// @dev Queries the balance of `_owner` at a specific `_blockNumber` /// @param _owner The address from which the balance will be retrieved /// @param _blockNumber The block number when the balance is queried /// @return The balance at `_blockNumber` function balanceOfAt(address _owner, uint256 _blockNumber) public view returns (uint256) { if ((balances[_owner].length == 0) || (balances[_owner][0].fromBlock > _blockNumber)) { return 0; // This will return the expected balance during normal situations } else { return getValueAt(balances[_owner], _blockNumber); } } /// @notice Total amount of reputation at a specific `_blockNumber`. /// @param _blockNumber The block number when the totalSupply is queried /// @return The total amount of reputation at `_blockNumber` function totalSupplyAt(uint256 _blockNumber) public view returns(uint256) { if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) { return 0; // This will return the expected totalSupply during normal situations } else { return getValueAt(totalSupplyHistory, _blockNumber); } } /// @notice Generates `_amount` reputation that are assigned to `_owner` /// @param _user The address that will be assigned the new reputation /// @param _amount The quantity of reputation generated /// @return True if the reputation are generated correctly function mint(address _user, uint256 _amount) public onlyOwner returns (bool) { uint256 curTotalSupply = totalSupply(); require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow uint256 previousBalanceTo = balanceOf(_user); require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); updateValueAtNow(balances[_user], previousBalanceTo + _amount); emit Mint(_user, _amount); return true; } /// @notice Burns `_amount` reputation from `_owner` /// @param _user The address that will lose the reputation /// @param _amount The quantity of reputation to burn /// @return True if the reputation are burned correctly function burn(address _user, uint256 _amount) public onlyOwner returns (bool) { uint256 curTotalSupply = totalSupply(); uint256 amountBurned = _amount; uint256 previousBalanceFrom = balanceOf(_user); if (previousBalanceFrom < amountBurned) { amountBurned = previousBalanceFrom; } updateValueAtNow(totalSupplyHistory, curTotalSupply - amountBurned); updateValueAtNow(balances[_user], previousBalanceFrom - amountBurned); emit Burn(_user, amountBurned); return true; } //////////////// // Internal helper functions to query and set a value in a snapshot array //////////////// /// @dev `getValueAt` retrieves the number of reputation at a given block number /// @param checkpoints The history of values being queried /// @param _block The block number to retrieve the value at /// @return The number of reputation being queried function getValueAt(Checkpoint[] storage checkpoints, uint256 _block) internal view returns (uint256) { if (checkpoints.length == 0) { return 0; } // Shortcut for the actual value if (_block >= checkpoints[checkpoints.length-1].fromBlock) { return checkpoints[checkpoints.length-1].value; } if (_block < checkpoints[0].fromBlock) { return 0; } // Binary search of the value in the array uint256 min = 0; uint256 max = checkpoints.length-1; while (max > min) { uint256 mid = (max + min + 1) / 2; if (checkpoints[mid].fromBlock<=_block) { min = mid; } else { max = mid-1; } } return checkpoints[min].value; } /// @dev `updateValueAtNow` used to update the `balances` map and the /// `totalSupplyHistory` /// @param checkpoints The history of data being updated /// @param _value The new number of reputation function updateValueAtNow(Checkpoint[] storage checkpoints, uint256 _value) internal { require(uint128(_value) == _value); //check value is in the 128 bits bounderies if ((checkpoints.length == 0) || (checkpoints[checkpoints.length - 1].fromBlock < block.number)) { Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++]; newCheckPoint.fromBlock = uint128(block.number); newCheckPoint.value = uint128(_value); } else { Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1]; oldCheckPoint.value = uint128(_value); } } } // File: contracts/votingMachines/IntVoteInterface.sol pragma solidity ^0.5.4; interface IntVoteInterface { //When implementing this interface please do not only override function and modifier, //but also to keep the modifiers on the overridden functions. modifier onlyProposalOwner(bytes32 _proposalId) {revert(); _;} modifier votable(bytes32 _proposalId) {revert(); _;} event NewProposal( bytes32 indexed _proposalId, address indexed _organization, uint256 _numOfChoices, address _proposer, bytes32 _paramsHash ); event ExecuteProposal(bytes32 indexed _proposalId, address indexed _organization, uint256 _decision, uint256 _totalReputation ); event VoteProposal( bytes32 indexed _proposalId, address indexed _organization, address indexed _voter, uint256 _vote, uint256 _reputation ); event CancelProposal(bytes32 indexed _proposalId, address indexed _organization ); event CancelVoting(bytes32 indexed _proposalId, address indexed _organization, address indexed _voter); /** * @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 _numOfChoices number of voting choices * @param _proposalParameters defines the parameters of the voting machine used for this proposal * @param _proposer address * @param _organization address - if this address is zero the msg.sender will be used as the organization address. * @return proposal's id. */ function propose( uint256 _numOfChoices, bytes32 _proposalParameters, address _proposer, address _organization ) external returns(bytes32); function vote( bytes32 _proposalId, uint256 _vote, uint256 _rep, address _voter ) external returns(bool); function cancelVote(bytes32 _proposalId) external; function getNumberOfChoices(bytes32 _proposalId) external view returns(uint256); function isVotable(bytes32 _proposalId) external view returns(bool); /** * @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 returns(uint256); /** * @dev isAbstainAllow returns if the voting machine allow abstain (0) * @return bool true or false */ function isAbstainAllow() external pure returns(bool); /** * @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 returns(uint256 min, uint256 max); } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.2; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.2; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // File: contracts/votingMachines/VotingMachineCallbacksInterface.sol pragma solidity ^0.5.4; interface VotingMachineCallbacksInterface { function mintReputation(uint256 _amount, address _beneficiary, bytes32 _proposalId) external returns(bool); function burnReputation(uint256 _amount, address _owner, bytes32 _proposalId) external returns(bool); function stakingTokenTransfer(IERC20 _stakingToken, address _beneficiary, uint256 _amount, bytes32 _proposalId) external returns(bool); function getTotalReputationSupply(bytes32 _proposalId) external view returns(uint256); function reputationOf(address _owner, bytes32 _proposalId) external view returns(uint256); function balanceOfStakingToken(IERC20 _stakingToken, bytes32 _proposalId) external view returns(uint256); } // File: contracts/votingMachines/ProposalExecuteInterface.sol pragma solidity ^0.5.4; interface ProposalExecuteInterface { function executeProposal(bytes32 _proposalId, int _decision) external returns(bool); } // File: contracts/votingMachines/AbsoluteVote.sol pragma solidity ^0.5.4; contract AbsoluteVote is IntVoteInterface { using SafeMath for uint; struct Parameters { uint256 precReq; // how many percentages required for the proposal to be passed address voteOnBehalf; //if this address is set so only this address is allowed // to vote of behalf of someone else. } struct Voter { uint256 vote; // 0 - 'abstain' uint256 reputation; // amount of voter's reputation } struct Proposal { bytes32 organizationId; // the organization Id bool open; // voting open flag address callbacks; uint256 numOfChoices; bytes32 paramsHash; // the hash of the parameters of the proposal uint256 totalVotes; mapping(uint=>uint) votes; mapping(address=>Voter) voters; } event AVVoteProposal(bytes32 indexed _proposalId, bool _isProxyVote); mapping(bytes32=>Parameters) public parameters; // A mapping from hashes to parameters mapping(bytes32=>Proposal) public proposals; // Mapping from the ID of the proposal to the proposal itself. mapping(bytes32=>address) public organizations; uint256 public constant MAX_NUM_OF_CHOICES = 10; uint256 public proposalsCnt; // Total amount of proposals /** * @dev Check that the proposal is votable (open and not executed yet) */ modifier votable(bytes32 _proposalId) { require(proposals[_proposalId].open); _; } /** * @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 _numOfChoices number of voting choices * @param _paramsHash defined the parameters of the voting machine used for this proposal * @param _organization address * @return proposal's id. */ function propose(uint256 _numOfChoices, bytes32 _paramsHash, address, address _organization) external returns(bytes32) { // Check valid params and number of choices: require(parameters[_paramsHash].precReq > 0); require(_numOfChoices > 0 && _numOfChoices <= MAX_NUM_OF_CHOICES); // Generate a unique ID: bytes32 proposalId = keccak256(abi.encodePacked(this, proposalsCnt)); proposalsCnt = proposalsCnt.add(1); // Open proposal: Proposal memory proposal; proposal.numOfChoices = _numOfChoices; proposal.paramsHash = _paramsHash; proposal.callbacks = msg.sender; proposal.organizationId = keccak256(abi.encodePacked(msg.sender, _organization)); proposal.open = true; proposals[proposalId] = proposal; if (organizations[proposal.organizationId] == address(0)) { if (_organization == address(0)) { organizations[proposal.organizationId] = msg.sender; } else { organizations[proposal.organizationId] = _organization; } } emit NewProposal(proposalId, organizations[proposal.organizationId], _numOfChoices, msg.sender, _paramsHash); return proposalId; } /** * @dev voting function * @param _proposalId id of the proposal * @param _vote a value between 0 to and the proposal number of choices. * @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) 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 = _voter; } else { voter = msg.sender; } return internalVote(_proposalId, voter, _vote, _amount); } /** * @dev Cancel the vote of the msg.sender: subtract the reputation amount from the votes * and delete the voter from the proposal struct * @param _proposalId id of the proposal */ function cancelVote(bytes32 _proposalId) external votable(_proposalId) { cancelVoteInternal(_proposalId, msg.sender); } /** * @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 * excluding the abstain vote (0) * @param _proposalId the ID of the proposal * @return uint256 that contains number of choices */ function getNumberOfChoices(bytes32 _proposalId) external view returns(uint256) { return proposals[_proposalId].numOfChoices; } /** * @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) { Voter memory voter = proposals[_proposalId].voters[_voter]; return (voter.vote, voter.reputation); } /** * @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 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 returns(bool) { return proposals[_proposalId].open; } /** * @dev isAbstainAllow returns if the voting machine allow abstain (0) * @return bool true or false */ function isAbstainAllow() external pure returns(bool) { return true; } /** * @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 returns(uint256 min, uint256 max) { return (0, MAX_NUM_OF_CHOICES); } /** * @dev hash the parameters, save them if necessary, and return the hash value */ function setParameters(uint256 _precReq, address _voteOnBehalf) public returns(bytes32) { require(_precReq <= 100 && _precReq > 0); bytes32 hashedParameters = getParametersHash(_precReq, _voteOnBehalf); parameters[hashedParameters] = Parameters({ precReq: _precReq, voteOnBehalf: _voteOnBehalf }); return hashedParameters; } /** * @dev hashParameters returns a hash of the given parameters */ function getParametersHash(uint256 _precReq, address _voteOnBehalf) public pure returns(bytes32) { return keccak256(abi.encodePacked(_precReq, _voteOnBehalf)); } function cancelVoteInternal(bytes32 _proposalId, address _voter) internal { Proposal storage proposal = proposals[_proposalId]; Voter memory voter = proposal.voters[_voter]; proposal.votes[voter.vote] = (proposal.votes[voter.vote]).sub(voter.reputation); proposal.totalVotes = (proposal.totalVotes).sub(voter.reputation); delete proposal.voters[_voter]; emit CancelVoting(_proposalId, organizations[proposal.organizationId], _voter); } function deleteProposal(bytes32 _proposalId) internal { Proposal storage proposal = proposals[_proposalId]; for (uint256 cnt = 0; cnt <= proposal.numOfChoices; cnt++) { delete proposal.votes[cnt]; } delete proposals[_proposalId]; } /** * @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) internal votable(_proposalId) returns(bool) { Proposal storage proposal = proposals[_proposalId]; uint256 totalReputation = VotingMachineCallbacksInterface(proposal.callbacks).getTotalReputationSupply(_proposalId); uint256 precReq = parameters[proposal.paramsHash].precReq; // Check if someone crossed the bar: for (uint256 cnt = 0; cnt <= proposal.numOfChoices; cnt++) { if (proposal.votes[cnt] > (totalReputation/100)*precReq) { Proposal memory tmpProposal = proposal; deleteProposal(_proposalId); emit ExecuteProposal(_proposalId, organizations[tmpProposal.organizationId], cnt, totalReputation); return ProposalExecuteInterface(tmpProposal.callbacks).executeProposal(_proposalId, int(cnt)); } } return false; } /** * @dev Vote for a proposal, if the voter already voted, cancel the last vote and set a new one instead * @param _proposalId id of the proposal * @param _voter used in case the vote is cast for someone else * @param _vote a value between 0 to and the proposal's number of choices. * @return true in case of proposal execution otherwise false * throws if proposal is not open or if it has been executed * NB: executes the proposal if a decision has been reached */ function internalVote(bytes32 _proposalId, address _voter, uint256 _vote, uint256 _rep) internal returns(bool) { Proposal storage proposal = proposals[_proposalId]; // Check valid vote: require(_vote <= proposal.numOfChoices); // Check voter has enough reputation: uint256 reputation = VotingMachineCallbacksInterface(proposal.callbacks).reputationOf(_voter, _proposalId); require(reputation > 0, "_voter must have reputation"); require(reputation >= _rep); uint256 rep = _rep; if (rep == 0) { rep = reputation; } // If this voter has already voted, first cancel the vote: if (proposal.voters[_voter].reputation != 0) { cancelVoteInternal(_proposalId, _voter); } // The voting itself: proposal.votes[_vote] = rep.add(proposal.votes[_vote]); proposal.totalVotes = rep.add(proposal.totalVotes); proposal.voters[_voter] = Voter({ reputation: rep, vote: _vote }); // Event: emit VoteProposal(_proposalId, organizations[proposal.organizationId], _voter, _vote, rep); emit AVVoteProposal(_proposalId, (_voter != msg.sender)); // execute the proposal if this vote was decisive: return _execute(_proposalId); } }