Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Address
Compiler Version
v0.5.17+commit.d19bba13
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2022-10-23 */ // Sources flattened with hardhat v2.11.2 https://hardhat.org // File contracts/daostack/votingMachines/IntVoteInterface.sol // SPDX-License-Identifier: AGPL-3.0 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/token/ERC20/[email protected] pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File contracts/daostack/votingMachines/VotingMachineCallbacksInterface.sol pragma solidity 0.5.17; 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 openzeppelin-solidity/contracts/GSN/[email protected] pragma solidity ^0.5.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ contract Context { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File openzeppelin-solidity/contracts/ownership/[email protected] pragma solidity ^0.5.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = _msgSender(); emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return _msgSender() == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: 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 Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File contracts/daostack/controller/Reputation.sol pragma solidity 0.5.17; // Copied from @daostack/infra/contracts/Reputation.sol and added the MintMultiple function /** * @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[]) private balances; // Tracks the history of the `totalSupply` of the reputation Checkpoint[] private totalSupplyHistory; // @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 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 mintMultiple(address[] memory _user, uint256[] memory _amount) public onlyOwner returns (bool) { for (uint256 i = 0; i < _user.length; i++) { uint256 curTotalSupply = totalSupply(); require(curTotalSupply + _amount[i] >= curTotalSupply); // Check for overflow uint256 previousBalanceTo = balanceOf(_user[i]); require(previousBalanceTo + _amount[i] >= previousBalanceTo); // Check for overflow updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount[i]); updateValueAtNow(balances[_user[i]], previousBalanceTo + _amount[i]); emit Mint(_user[i], _amount[i]); } 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; } // @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); } // @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); } } // @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); } } //////////////// // 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 openzeppelin-solidity/contracts/math/[email protected] pragma solidity ^0.5.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. * * _Available since v2.4.0._ */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot 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-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File openzeppelin-solidity/contracts/token/ERC20/[email protected] pragma solidity ^0.5.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20Mintable}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for `sender`'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal { require(account != address(0), "ERC20: burn from the zero address"); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Destroys `amount` tokens from `account`.`amount` is then deducted * from the caller's allowance. * * See {_burn} and {_approve}. */ function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); } } // File openzeppelin-solidity/contracts/token/ERC20/[email protected] pragma solidity ^0.5.0; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ contract ERC20Burnable is Context, ERC20 { /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public { _burn(_msgSender(), amount); } /** * @dev See {ERC20-_burnFrom}. */ function burnFrom(address account, uint256 amount) public { _burnFrom(account, amount); } } // File contracts/daostack/controller/DAOToken.sol pragma solidity ^0.5.4; /** * @title DAOToken, base on zeppelin contract. * @dev ERC20 compatible token. It is a mintable, burnable token. */ contract DAOToken is ERC20, ERC20Burnable, Ownable { string public name; string public symbol; // solhint-disable-next-line const-name-snakecase uint8 public constant decimals = 18; uint256 public cap; /** * @dev Constructor * @param _name - token name * @param _symbol - token symbol * @param _cap - token cap - 0 value means no cap */ constructor( string memory _name, string memory _symbol, uint256 _cap ) public { name = _name; symbol = _symbol; cap = _cap; } /** * @dev Function to mint tokens * @param _to The address that will receive the minted tokens. * @param _amount The amount of tokens to mint. */ function mint(address _to, uint256 _amount) public onlyOwner returns (bool) { if (cap > 0) require(totalSupply().add(_amount) <= cap); _mint(_to, _amount); return true; } } // File openzeppelin-solidity/contracts/utils/[email protected] pragma solidity ^0.5.5; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * IMPORTANT: It is unsafe to assume that an address for which this * function returns false is an externally-owned account (EOA) and not a * contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != 0x0 && codehash != accountHash); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } // File contracts/daostack/libs/SafeERC20.sol /* SafeERC20 by daostack. The code is based on a fix by SECBIT Team. USE WITH CAUTION & NO WARRANTY REFERENCE & RELATED READING - https://github.com/ethereum/solidity/issues/4116 - https://medium.com/@chris_77367/explaining-unexpected-reverts-starting-with-solidity-0-4-22-3ada6e82308c - https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca - https://gist.github.com/BrendanChou/88a2eeb80947ff00bcf58ffdafeaeb61 */ pragma solidity ^0.5.4; library SafeERC20 { using Address for address; bytes4 private constant TRANSFER_SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)"))); bytes4 private constant TRANSFERFROM_SELECTOR = bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); bytes4 private constant APPROVE_SELECTOR = bytes4(keccak256(bytes("approve(address,uint256)"))); function safeTransfer( address _erc20Addr, address _to, uint256 _value ) internal { // Must be a contract addr first! require(_erc20Addr.isContract()); ( bool success, bytes memory returnValue // solhint-disable-next-line avoid-low-level-calls ) = _erc20Addr.call(abi.encodeWithSelector(TRANSFER_SELECTOR, _to, _value)); // call return false when something wrong require(success); //check return value require(returnValue.length == 0 || (returnValue.length == 32 && (returnValue[31] != 0))); } function safeTransferFrom( address _erc20Addr, address _from, address _to, uint256 _value ) internal { // Must be a contract addr first! require(_erc20Addr.isContract()); ( bool success, bytes memory returnValue // solhint-disable-next-line avoid-low-level-calls ) = _erc20Addr.call(abi.encodeWithSelector(TRANSFERFROM_SELECTOR, _from, _to, _value)); // call return false when something wrong require(success); //check return value require(returnValue.length == 0 || (returnValue.length == 32 && (returnValue[31] != 0))); } function safeApprove( address _erc20Addr, address _spender, uint256 _value ) internal { // Must be a contract addr first! require(_erc20Addr.isContract()); // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. require((_value == 0) || (IERC20(_erc20Addr).allowance(address(this), _spender) == 0)); ( bool success, bytes memory returnValue // solhint-disable-next-line avoid-low-level-calls ) = _erc20Addr.call(abi.encodeWithSelector(APPROVE_SELECTOR, _spender, _value)); // call return false when something wrong require(success); //check return value require(returnValue.length == 0 || (returnValue.length == 32 && (returnValue[31] != 0))); } } // File contracts/daostack/controller/Avatar.sol pragma solidity ^0.5.4; /** * @title An Avatar holds tokens, reputation and ether for a controller */ contract Avatar is Ownable { using SafeERC20 for address; string public orgName; DAOToken public nativeToken; Reputation public nativeReputation; event GenericCall(address indexed _contract, bytes _data, uint256 _value, bool _success); event SendEther(uint256 _amountInWei, address indexed _to); event ExternalTokenTransfer(address indexed _externalToken, address indexed _to, uint256 _value); event ExternalTokenTransferFrom(address indexed _externalToken, address _from, address _to, uint256 _value); event ExternalTokenApproval(address indexed _externalToken, address _spender, uint256 _value); event ReceiveEther(address indexed _sender, uint256 _value); event MetaData(string _metaData); /** * @dev the constructor takes organization name, native token and reputation system and creates an avatar for a controller */ constructor( string memory _orgName, DAOToken _nativeToken, Reputation _nativeReputation ) public { orgName = _orgName; nativeToken = _nativeToken; nativeReputation = _nativeReputation; } /** * @dev enables an avatar to receive ethers */ function() external payable { emit ReceiveEther(msg.sender, msg.value); } /** * @dev perform a generic call to an arbitrary contract * @param _contract the contract's address to call * @param _data ABI-encoded contract call to call `_contract` address. * @param _value value (ETH) to transfer with the transaction * @return bool success or fail * bytes - the return bytes of the called contract's function. */ function genericCall( address _contract, bytes memory _data, uint256 _value ) public onlyOwner returns (bool success, bytes memory returnValue) { // solhint-disable-next-line avoid-call-value (success, returnValue) = _contract.call.value(_value)(_data); emit GenericCall(_contract, _data, _value, success); } /** * @dev send ethers from the avatar's wallet * @param _amountInWei amount to send in Wei units * @param _to send the ethers to this address * @return bool which represents success */ function sendEther(uint256 _amountInWei, address payable _to) public onlyOwner returns (bool) { _to.transfer(_amountInWei); emit SendEther(_amountInWei, _to); return true; } /** * @dev external token transfer * @param _externalToken the token contract * @param _to the destination address * @param _value the amount of tokens to transfer * @return bool which represents success */ function externalTokenTransfer( IERC20 _externalToken, address _to, uint256 _value ) public onlyOwner returns (bool) { address(_externalToken).safeTransfer(_to, _value); emit ExternalTokenTransfer(address(_externalToken), _to, _value); return true; } /** * @dev external token transfer from a specific account * @param _externalToken the token contract * @param _from the account to spend token from * @param _to the destination address * @param _value the amount of tokens to transfer * @return bool which represents success */ function externalTokenTransferFrom( IERC20 _externalToken, address _from, address _to, uint256 _value ) public onlyOwner returns (bool) { address(_externalToken).safeTransferFrom(_from, _to, _value); emit ExternalTokenTransferFrom(address(_externalToken), _from, _to, _value); return true; } /** * @dev externalTokenApproval approve the spender address to spend a specified amount of tokens * on behalf of msg.sender. * @param _externalToken the address of the Token Contract * @param _spender address * @param _value the amount of ether (in Wei) which the approval is referring to. * @return bool which represents a success */ function externalTokenApproval( IERC20 _externalToken, address _spender, uint256 _value ) public onlyOwner returns (bool) { address(_externalToken).safeApprove(_spender, _value); emit ExternalTokenApproval(address(_externalToken), _spender, _value); return true; } /** * @dev metaData emits an event with a string, should contain the hash of some meta data. * @param _metaData a string representing a hash of the meta data * @return bool which represents a success */ function metaData(string memory _metaData) public onlyOwner returns (bool) { emit MetaData(_metaData); return true; } } // File contracts/daostack/universalSchemes/UniversalSchemeInterface.sol pragma solidity ^0.5.4; contract UniversalSchemeInterface { function getParametersFromController(Avatar _avatar) internal view returns (bytes32); } // File contracts/daostack/globalConstraints/GlobalConstraintInterface.sol pragma solidity ^0.5.4; contract GlobalConstraintInterface { enum CallPhase { Pre, Post, PreAndPost } function pre( address _scheme, bytes32 _params, bytes32 _method ) public returns (bool); function post( address _scheme, bytes32 _params, bytes32 _method ) public returns (bool); /** * @dev when return if this globalConstraints is pre, post or both. * @return CallPhase enum indication Pre, Post or PreAndPost. */ function when() public returns (CallPhase); } // File contracts/daostack/controller/ControllerInterface.sol pragma solidity ^0.5.4; /** * @title Controller contract * @dev A controller controls the organizations tokens ,reputation and avatar. * It is subject to a set of schemes and constraints that determine its behavior. * Each scheme has it own parameters and operation permissions. */ interface ControllerInterface { /** * @dev Mint `_amount` of reputation that are assigned to `_to` . * @param _amount amount of reputation to mint * @param _to beneficiary address * @return bool which represents a success */ function mintReputation( uint256 _amount, address _to, address _avatar ) external returns (bool); /** * @dev Burns `_amount` of reputation from `_from` * @param _amount amount of reputation to burn * @param _from The address that will lose the reputation * @return bool which represents a success */ function burnReputation( uint256 _amount, address _from, address _avatar ) external returns (bool); /** * @dev mint tokens . * @param _amount amount of token to mint * @param _beneficiary beneficiary address * @param _avatar address * @return bool which represents a success */ function mintTokens( uint256 _amount, address _beneficiary, address _avatar ) external returns (bool); /** * @dev register or update a scheme * @param _scheme the address of the scheme * @param _paramsHash a hashed configuration of the usage of the scheme * @param _permissions the permissions the new scheme will have * @param _avatar address * @return bool which represents a success */ function registerScheme( address _scheme, bytes32 _paramsHash, bytes4 _permissions, address _avatar ) external returns (bool); /** * @dev unregister a scheme * @param _avatar address * @param _scheme the address of the scheme * @return bool which represents a success */ function unregisterScheme(address _scheme, address _avatar) external returns (bool); /** * @dev unregister the caller's scheme * @param _avatar address * @return bool which represents a success */ function unregisterSelf(address _avatar) external returns (bool); /** * @dev add or update Global Constraint * @param _globalConstraint the address of the global constraint to be added. * @param _params the constraint parameters hash. * @param _avatar the avatar of the organization * @return bool which represents a success */ function addGlobalConstraint( address _globalConstraint, bytes32 _params, address _avatar ) external returns (bool); /** * @dev remove Global Constraint * @param _globalConstraint the address of the global constraint to be remove. * @param _avatar the organization avatar. * @return bool which represents a success */ function removeGlobalConstraint(address _globalConstraint, address _avatar) external returns (bool); /** * @dev upgrade the Controller * The function will trigger an event 'UpgradeController'. * @param _newController the address of the new controller. * @param _avatar address * @return bool which represents a success */ function upgradeController(address _newController, Avatar _avatar) external returns (bool); /** * @dev perform a generic call to an arbitrary contract * @param _contract the contract's address to call * @param _data ABI-encoded contract call to call `_contract` address. * @param _avatar the controller's avatar address * @param _value value (ETH) to transfer with the transaction * @return bool -success * bytes - the return value of the called _contract's function. */ function genericCall( address _contract, bytes calldata _data, Avatar _avatar, uint256 _value ) external returns (bool, bytes memory); /** * @dev send some ether * @param _amountInWei the amount of ether (in Wei) to send * @param _to address of the beneficiary * @param _avatar address * @return bool which represents a success */ function sendEther( uint256 _amountInWei, address payable _to, Avatar _avatar ) external returns (bool); /** * @dev send some amount of arbitrary ERC20 Tokens * @param _externalToken the address of the Token Contract * @param _to address of the beneficiary * @param _value the amount of ether (in Wei) to send * @param _avatar address * @return bool which represents a success */ function externalTokenTransfer( IERC20 _externalToken, address _to, uint256 _value, Avatar _avatar ) external returns (bool); /** * @dev transfer token "from" address "to" address * One must to approve the amount of tokens which can be spend from the * "from" account.This can be done using externalTokenApprove. * @param _externalToken the address of the Token Contract * @param _from address of the account to send from * @param _to address of the beneficiary * @param _value the amount of ether (in Wei) to send * @param _avatar address * @return bool which represents a success */ function externalTokenTransferFrom( IERC20 _externalToken, address _from, address _to, uint256 _value, Avatar _avatar ) external returns (bool); /** * @dev externalTokenApproval approve the spender address to spend a specified amount of tokens * on behalf of msg.sender. * @param _externalToken the address of the Token Contract * @param _spender address * @param _value the amount of ether (in Wei) which the approval is referring to. * @return bool which represents a success */ function externalTokenApproval( IERC20 _externalToken, address _spender, uint256 _value, Avatar _avatar ) external returns (bool); /** * @dev metaData emits an event with a string, should contain the hash of some meta data. * @param _metaData a string representing a hash of the meta data * @param _avatar Avatar * @return bool which represents a success */ function metaData(string calldata _metaData, Avatar _avatar) external returns (bool); /** * @dev getNativeReputation * @param _avatar the organization avatar. * @return organization native reputation */ function getNativeReputation(address _avatar) external view returns (address); function isSchemeRegistered(address _scheme, address _avatar) external view returns (bool); function getSchemeParameters(address _scheme, address _avatar) external view returns (bytes32); function getGlobalConstraintParameters(address _globalConstraint, address _avatar) external view returns (bytes32); function getSchemePermissions(address _scheme, address _avatar) external view returns (bytes4); /** * @dev globalConstraintsCount return the global constraint pre and post count * @return uint256 globalConstraintsPre count. * @return uint256 globalConstraintsPost count. */ function globalConstraintsCount(address _avatar) external view returns (uint256, uint256); function isGlobalConstraintRegistered(address _globalConstraint, address _avatar) external view returns (bool); } // File contracts/daostack/universalSchemes/UniversalScheme.sol pragma solidity ^0.5.4; contract UniversalScheme is UniversalSchemeInterface { /** * @dev get the parameters for the current scheme from the controller */ function getParametersFromController(Avatar _avatar) internal view returns (bytes32) { require( ControllerInterface(_avatar.owner()).isSchemeRegistered(address(this), address(_avatar)), "scheme is not registered" ); return ControllerInterface(_avatar.owner()).getSchemeParameters(address(this), address(_avatar)); } } // File contracts/daostack/libs/RealMath.sol pragma solidity ^0.5.11; /** * RealMath: fixed-point math library, based on fractional and integer parts. * Using uint256 as real216x40, which isn't in Solidity yet. * Internally uses the wider uint256 for some math. * * Note that for addition, subtraction, and mod (%), you should just use the * built-in Solidity operators. Functions for these operations are not provided. * */ library RealMath { /** * How many total bits are there? */ uint256 private constant REAL_BITS = 256; /** * How many fractional bits are there? */ uint256 private constant REAL_FBITS = 40; /** * What's the first non-fractional bit */ uint256 private constant REAL_ONE = uint256(1) << REAL_FBITS; /** * Raise a real number to any positive integer power */ function pow(uint256 realBase, uint256 exponent) internal pure returns (uint256) { uint256 tempRealBase = realBase; uint256 tempExponent = exponent; // Start with the 0th power uint256 realResult = REAL_ONE; while (tempExponent != 0) { // While there are still bits set if ((tempExponent & 0x1) == 0x1) { // If the low bit is set, multiply in the (many-times-squared) base realResult = mul(realResult, tempRealBase); } // Shift off the low bit tempExponent = tempExponent >> 1; if (tempExponent != 0) { // Do the squaring tempRealBase = mul(tempRealBase, tempRealBase); } } // Return the final result. return realResult; } /** * Create a real from a rational fraction. */ function fraction(uint216 numerator, uint216 denominator) internal pure returns (uint256) { return div(uint256(numerator) * REAL_ONE, uint256(denominator) * REAL_ONE); } /** * Multiply one real by another. Truncates overflows. */ function mul(uint256 realA, uint256 realB) private pure returns (uint256) { // When multiplying fixed point in x.y and z.w formats we get (x+z).(y+w) format. // So we just have to clip off the extra REAL_FBITS fractional bits. uint256 res = realA * realB; require(res / realA == realB, "RealMath mul overflow"); return (res >> REAL_FBITS); } /** * Divide one real by another real. Truncates overflows. */ function div(uint256 realNumerator, uint256 realDenominator) private pure returns (uint256) { // We use the reverse of the multiplication trick: convert numerator from // x.y to (x+z).(y+w) fixed point, then divide by denom in z.w fixed point. return uint256((uint256(realNumerator) * REAL_ONE) / uint256(realDenominator)); } } // File contracts/daostack/votingMachines/ProposalExecuteInterface.sol pragma solidity 0.5.17; interface ProposalExecuteInterface { function executeProposal(bytes32 _proposalId, int256 _decision) external returns (bool); } // File openzeppelin-solidity/contracts/math/[email protected] pragma solidity ^0.5.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } // File contracts/daostack/votingMachines/GenesisProtocolLogic.sol pragma solidity 0.5.17; /** * @title GenesisProtocol implementation -an organization's voting machine scheme. */ contract GenesisProtocolLogic is IntVoteInterface { using SafeMath for uint256; using Math for uint256; using RealMath for uint216; using RealMath for uint256; using Address for address; enum ProposalState { None, ExpiredInQueue, Executed, Queued, PreBoosted, Boosted, QuietEndingPeriod } enum ExecutionState { None, QueueBarCrossed, QueueTimeOut, PreBoostedBarCrossed, BoostedTimeOut, BoostedBarCrossed } //Organization's parameters struct Parameters { uint256 queuedVoteRequiredPercentage; // the absolute vote percentages bar. uint256 queuedVotePeriodLimit; //the time limit for a proposal to be in an absolute voting mode. uint256 boostedVotePeriodLimit; //the time limit for a proposal to be in boost mode. uint256 preBoostedVotePeriodLimit; //the time limit for a proposal //to be in an preparation state (stable) before boosted. uint256 thresholdConst; //constant for threshold calculation . //threshold =thresholdConst ** (numberOfBoostedProposals) uint256 limitExponentValue; // an upper limit for numberOfBoostedProposals //in the threshold calculation to prevent overflow uint256 quietEndingPeriod; //quite ending period uint256 proposingRepReward; //proposer reputation reward. uint256 votersReputationLossRatio; //Unsuccessful pre booster //voters lose votersReputationLossRatio% of their reputation. uint256 minimumDaoBounty; uint256 daoBountyConst; //The DAO downstake for each proposal is calculate according to the formula //(daoBountyConst * averageBoostDownstakes)/100 . uint256 activationTime; //the point in time after which proposals can be created. //if this address is set so only this address is allowed to vote of behalf of someone else. address voteOnBehalf; } struct Voter { uint256 vote; // YES(1) ,NO(2) uint256 reputation; // amount of voter's reputation bool preBoosted; } struct Staker { uint256 vote; // YES(1) ,NO(2) uint256 amount; // amount of staker's stake uint256 amount4Bounty; // amount of staker's stake used for bounty reward calculation. } struct Proposal { bytes32 organizationId; // the organization unique identifier the proposal is target to. address callbacks; // should fulfill voting callbacks interface. ProposalState state; uint256 winningVote; //the winning vote. address proposer; //the proposal boosted period limit . it is updated for the case of quiteWindow mode. uint256 currentBoostedVotePeriodLimit; bytes32 paramsHash; uint256 daoBountyRemain; //use for checking sum zero bounty claims.it is set at the proposing time. uint256 daoBounty; uint256 totalStakes; // Total number of tokens staked which can be redeemable by stakers. uint256 confidenceThreshold; uint256 secondsFromTimeOutTillExecuteBoosted; uint256[3] times; //times[0] - submittedTime //times[1] - boostedPhaseTime //times[2] -preBoostedPhaseTime; bool daoRedeemItsWinnings; // vote reputation mapping(uint256 => uint256) votes; // vote reputation mapping(uint256 => uint256) preBoostedVotes; // address voter mapping(address => Voter) voters; // vote stakes mapping(uint256 => uint256) stakes; // address staker mapping(address => Staker) stakers; } event Stake( bytes32 indexed _proposalId, address indexed _organization, address indexed _staker, uint256 _vote, uint256 _amount ); event Redeem( bytes32 indexed _proposalId, address indexed _organization, address indexed _beneficiary, uint256 _amount ); event RedeemDaoBounty( bytes32 indexed _proposalId, address indexed _organization, address indexed _beneficiary, uint256 _amount ); event RedeemReputation( bytes32 indexed _proposalId, address indexed _organization, address indexed _beneficiary, uint256 _amount ); event StateChange(bytes32 indexed _proposalId, ProposalState _proposalState); event GPExecuteProposal(bytes32 indexed _proposalId, ExecutionState _executionState); event ExpirationCallBounty(bytes32 indexed _proposalId, address indexed _beneficiary, uint256 _amount); event ConfidenceLevelChange(bytes32 indexed _proposalId, uint256 _confidenceThreshold); 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 => uint256) public orgBoostedProposalsCnt; //organizationId => organization mapping(bytes32 => address) public organizations; //organizationId => averageBoostDownstakes mapping(bytes32 => uint256) public averagesDownstakesOfBoosted; uint256 public constant NUM_OF_CHOICES = 2; uint256 public constant NO = 2; uint256 public constant YES = 1; uint256 public proposalsCnt; // Total number of proposals IERC20 public stakingToken; address private constant GEN_TOKEN_ADDRESS = 0x543Ff227F64Aa17eA132Bf9886cAb5DB55DCAddf; uint256 private constant MAX_BOOSTED_PROPOSALS = 4096; /** * @dev Constructor */ constructor(IERC20 _stakingToken) public { //The GEN token (staking token) address is hard coded in the contract by GEN_TOKEN_ADDRESS . //This will work for a network which already hosted the GEN token on this address (e.g mainnet). //If such contract address does not exist in the network (e.g ganache) //the contract will use the _stakingToken param as the //staking token address. if (address(GEN_TOKEN_ADDRESS).isContract()) { stakingToken = IERC20(GEN_TOKEN_ADDRESS); } else { stakingToken = _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) { require(_isVotable(_proposalId)); _; } /** * @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 _paramsHash parameters hash * @param _proposer address * @param _organization address */ function propose( uint256, bytes32 _paramsHash, address _proposer, address _organization ) external returns (bytes32) { // solhint-disable-next-line not-rely-on-time require(now > parameters[_paramsHash].activationTime, "not active yet"); //Check parameters existence. require(parameters[_paramsHash].queuedVoteRequiredPercentage >= 50); // Generate a unique ID: bytes32 proposalId = keccak256(abi.encodePacked(this, proposalsCnt)); proposalsCnt = proposalsCnt.add(1); // Open proposal: Proposal memory proposal; proposal.callbacks = msg.sender; proposal.organizationId = keccak256(abi.encodePacked(msg.sender, _organization)); proposal.state = ProposalState.Queued; // solhint-disable-next-line not-rely-on-time proposal.times[0] = now; //submitted time proposal.currentBoostedVotePeriodLimit = parameters[_paramsHash].boostedVotePeriodLimit; proposal.proposer = _proposer; proposal.winningVote = NO; proposal.paramsHash = _paramsHash; if (organizations[proposal.organizationId] == address(0)) { if (_organization == address(0)) { organizations[proposal.organizationId] = msg.sender; } else { organizations[proposal.organizationId] = _organization; } } //calc dao bounty uint256 daoBounty = parameters[_paramsHash] .daoBountyConst .mul(averagesDownstakesOfBoosted[proposal.organizationId]) .div(100); proposal.daoBountyRemain = daoBounty.max(parameters[_paramsHash].minimumDaoBounty); proposals[proposalId] = proposal; proposals[proposalId].stakes[NO] = proposal.daoBountyRemain; //dao downstake on the proposal emit NewProposal(proposalId, organizations[proposal.organizationId], NUM_OF_CHOICES, _proposer, _paramsHash); return proposalId; } /** * @dev executeBoosted try to execute a boosted or QuietEndingPeriod proposal if it is expired * it rewards the msg.sender with P % of the proposal's upstakes upon a successful call to this function. * P = t/150, where t is the number of seconds passed since the the proposal's timeout. * P is capped by 10%. * @param _proposalId the id of the proposal * @return uint256 expirationCallBounty the bounty amount for the expiration call */ function executeBoosted(bytes32 _proposalId) external returns (uint256 expirationCallBounty) { Proposal storage proposal = proposals[_proposalId]; require( proposal.state == ProposalState.Boosted || proposal.state == ProposalState.QuietEndingPeriod, "proposal state in not Boosted nor QuietEndingPeriod" ); require(_execute(_proposalId), "proposal need to expire"); proposal.secondsFromTimeOutTillExecuteBoosted = now.sub( // solhint-disable-next-line not-rely-on-time proposal.currentBoostedVotePeriodLimit.add(proposal.times[1]) ); expirationCallBounty = calcExecuteCallBounty(_proposalId); proposal.totalStakes = proposal.totalStakes.sub(expirationCallBounty); require(stakingToken.transfer(msg.sender, expirationCallBounty), "transfer to msg.sender failed"); emit ExpirationCallBounty(_proposalId, msg.sender, expirationCallBounty); } /** * @dev hash the parameters, save them if necessary, and return the hash value * @param _params a parameters array * _params[0] - _queuedVoteRequiredPercentage, * _params[1] - _queuedVotePeriodLimit, //the time limit for a proposal to be in an absolute voting mode. * _params[2] - _boostedVotePeriodLimit, //the time limit for a proposal to be in an relative voting mode. * _params[3] - _preBoostedVotePeriodLimit, //the time limit for a proposal to be in an preparation * state (stable) before boosted. * _params[4] -_thresholdConst * _params[5] -_quietEndingPeriod * _params[6] -_proposingRepReward * _params[7] -_votersReputationLossRatio * _params[8] -_minimumDaoBounty * _params[9] -_daoBountyConst * _params[10] -_activationTime * @param _voteOnBehalf - authorized to vote on behalf of others. */ function setParameters( uint256[11] calldata _params, //use array here due to stack too deep issue. address _voteOnBehalf ) external returns (bytes32) { require(_params[0] <= 100 && _params[0] >= 50, "50 <= queuedVoteRequiredPercentage <= 100"); require(_params[4] <= 16000 && _params[4] > 1000, "1000 < thresholdConst <= 16000"); require(_params[7] <= 100, "votersReputationLossRatio <= 100"); require(_params[2] >= _params[5], "boostedVotePeriodLimit >= quietEndingPeriod"); require(_params[8] > 0, "minimumDaoBounty should be > 0"); require(_params[9] > 0, "daoBountyConst should be > 0"); bytes32 paramsHash = getParametersHash(_params, _voteOnBehalf); //set a limit for power for a given alpha to prevent overflow uint256 limitExponent = 172; //for alpha less or equal 2 uint256 j = 2; for (uint256 i = 2000; i < 16000; i = i * 2) { if ((_params[4] > i) && (_params[4] <= i * 2)) { limitExponent = limitExponent / j; break; } j++; } parameters[paramsHash] = Parameters({ queuedVoteRequiredPercentage: _params[0], queuedVotePeriodLimit: _params[1], boostedVotePeriodLimit: _params[2], preBoostedVotePeriodLimit: _params[3], thresholdConst: uint216(_params[4]).fraction(uint216(1000)), limitExponentValue: limitExponent, quietEndingPeriod: _params[5], proposingRepReward: _params[6], votersReputationLossRatio: _params[7], minimumDaoBounty: _params[8], daoBountyConst: _params[9], activationTime: _params[10], voteOnBehalf: _voteOnBehalf }); return paramsHash; } /** * @dev redeem a reward for a successful stake, vote or proposing. * The function use a beneficiary address as a parameter (and not msg.sender) to enable * users to redeem on behalf of someone else. * @param _proposalId the ID of the proposal * @param _beneficiary - the beneficiary address * @return rewards - * [0] stakerTokenReward * [1] voterReputationReward * [2] proposerReputationReward */ // solhint-disable-next-line function-max-lines,code-complexity function redeem(bytes32 _proposalId, address _beneficiary) public returns (uint256[3] memory rewards) { Proposal storage proposal = proposals[_proposalId]; require( (proposal.state == ProposalState.Executed) || (proposal.state == ProposalState.ExpiredInQueue), "Proposal should be Executed or ExpiredInQueue" ); Parameters memory params = parameters[proposal.paramsHash]; //as staker Staker storage staker = proposal.stakers[_beneficiary]; uint256 totalWinningStakes = proposal.stakes[proposal.winningVote]; uint256 totalStakesLeftAfterCallBounty = proposal.stakes[NO].add(proposal.stakes[YES]).sub( calcExecuteCallBounty(_proposalId) ); if (staker.amount > 0) { if (proposal.state == ProposalState.ExpiredInQueue) { //Stakes of a proposal that expires in Queue are sent back to stakers rewards[0] = staker.amount; } else if (staker.vote == proposal.winningVote) { if (staker.vote == YES) { if (proposal.daoBounty < totalStakesLeftAfterCallBounty) { uint256 _totalStakes = totalStakesLeftAfterCallBounty.sub(proposal.daoBounty); rewards[0] = (staker.amount.mul(_totalStakes)) / totalWinningStakes; } } else { rewards[0] = (staker.amount.mul(totalStakesLeftAfterCallBounty)) / totalWinningStakes; } } staker.amount = 0; } //dao redeem its winnings if ( proposal.daoRedeemItsWinnings == false && _beneficiary == organizations[proposal.organizationId] && proposal.state != ProposalState.ExpiredInQueue && proposal.winningVote == NO ) { rewards[0] = rewards[0] .add((proposal.daoBounty.mul(totalStakesLeftAfterCallBounty)) / totalWinningStakes) .sub(proposal.daoBounty); proposal.daoRedeemItsWinnings = true; } //as voter Voter storage voter = proposal.voters[_beneficiary]; if ((voter.reputation != 0) && (voter.preBoosted)) { if (proposal.state == ProposalState.ExpiredInQueue) { //give back reputation for the voter rewards[1] = ((voter.reputation.mul(params.votersReputationLossRatio)) / 100); } else if (proposal.winningVote == voter.vote) { uint256 lostReputation; if (proposal.winningVote == YES) { lostReputation = proposal.preBoostedVotes[NO]; } else { lostReputation = proposal.preBoostedVotes[YES]; } lostReputation = (lostReputation.mul(params.votersReputationLossRatio)) / 100; rewards[1] = ((voter.reputation.mul(params.votersReputationLossRatio)) / 100).add( (voter.reputation.mul(lostReputation)) / proposal.preBoostedVotes[proposal.winningVote] ); } voter.reputation = 0; } //as proposer if ((proposal.proposer == _beneficiary) && (proposal.winningVote == YES) && (proposal.proposer != address(0))) { rewards[2] = params.proposingRepReward; proposal.proposer = address(0); } if (rewards[0] != 0) { proposal.totalStakes = proposal.totalStakes.sub(rewards[0]); require(stakingToken.transfer(_beneficiary, rewards[0]), "transfer to beneficiary failed"); emit Redeem(_proposalId, organizations[proposal.organizationId], _beneficiary, rewards[0]); } if (rewards[1].add(rewards[2]) != 0) { VotingMachineCallbacksInterface(proposal.callbacks).mintReputation( rewards[1].add(rewards[2]), _beneficiary, _proposalId ); emit RedeemReputation( _proposalId, organizations[proposal.organizationId], _beneficiary, rewards[1].add(rewards[2]) ); } } /** * @dev redeemDaoBounty a reward for a successful stake. * The function use a beneficiary address as a parameter (and not msg.sender) to enable * users to redeem on behalf of someone else. * @param _proposalId the ID of the proposal * @param _beneficiary - the beneficiary address * @return redeemedAmount - redeem token amount * @return potentialAmount - potential redeem token amount(if there is enough tokens bounty at the organization ) */ function redeemDaoBounty(bytes32 _proposalId, address _beneficiary) public returns (uint256 redeemedAmount, uint256 potentialAmount) { Proposal storage proposal = proposals[_proposalId]; require(proposal.state == ProposalState.Executed); uint256 totalWinningStakes = proposal.stakes[proposal.winningVote]; Staker storage staker = proposal.stakers[_beneficiary]; if ( (staker.amount4Bounty > 0) && (staker.vote == proposal.winningVote) && (proposal.winningVote == YES) && (totalWinningStakes != 0) ) { //as staker potentialAmount = (staker.amount4Bounty * proposal.daoBounty) / totalWinningStakes; } if ( (potentialAmount != 0) && (VotingMachineCallbacksInterface(proposal.callbacks).balanceOfStakingToken(stakingToken, _proposalId) >= potentialAmount) ) { staker.amount4Bounty = 0; proposal.daoBountyRemain = proposal.daoBountyRemain.sub(potentialAmount); require( VotingMachineCallbacksInterface(proposal.callbacks).stakingTokenTransfer( stakingToken, _beneficiary, potentialAmount, _proposalId ) ); redeemedAmount = potentialAmount; emit RedeemDaoBounty(_proposalId, organizations[proposal.organizationId], _beneficiary, redeemedAmount); } } /** * @dev calcExecuteCallBounty calculate the execute boosted call bounty * @param _proposalId the ID of the proposal * @return uint256 executeCallBounty */ function calcExecuteCallBounty(bytes32 _proposalId) public view returns (uint256) { uint256 maxRewardSeconds = 1500; uint256 rewardSeconds = uint256(maxRewardSeconds).min( proposals[_proposalId].secondsFromTimeOutTillExecuteBoosted ); return rewardSeconds.mul(proposals[_proposalId].stakes[YES]).div(maxRewardSeconds * 10); } /** * @dev shouldBoost check if a proposal should be shifted to boosted phase. * @param _proposalId the ID of the proposal * @return bool true or false. */ function shouldBoost(bytes32 _proposalId) public view returns (bool) { Proposal memory proposal = proposals[_proposalId]; return (_score(_proposalId) > threshold(proposal.paramsHash, proposal.organizationId)); } /** * @dev threshold return the organization's score threshold which required by * a proposal to shift to boosted state. * This threshold is dynamically set and it depend on the number of boosted proposal. * @param _organizationId the organization identifier * @param _paramsHash the organization parameters hash * @return uint256 organization's score threshold as real number. */ function threshold(bytes32 _paramsHash, bytes32 _organizationId) public view returns (uint256) { uint256 power = orgBoostedProposalsCnt[_organizationId]; Parameters storage params = parameters[_paramsHash]; if (power > params.limitExponentValue) { power = params.limitExponentValue; } return params.thresholdConst.pow(power); } /** * @dev hashParameters returns a hash of the given parameters */ function getParametersHash( uint256[11] memory _params, //use array here due to stack too deep issue. address _voteOnBehalf ) public pure returns (bytes32) { //double call to keccak256 to avoid deep stack issue when call with too many params. return keccak256( abi.encodePacked( keccak256( abi.encodePacked( _params[0], _params[1], _params[2], _params[3], _params[4], _params[5], _params[6], _params[7], _params[8], _params[9], _params[10] ) ), _voteOnBehalf ) ); } /** * @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. */ // solhint-disable-next-line function-max-lines,code-complexity function _execute(bytes32 _proposalId) internal votable(_proposalId) returns (bool) { Proposal storage proposal = proposals[_proposalId]; Parameters memory params = parameters[proposal.paramsHash]; Proposal memory tmpProposal = proposal; uint256 totalReputation = VotingMachineCallbacksInterface(proposal.callbacks).getTotalReputationSupply( _proposalId ); //first divide by 100 to prevent overflow uint256 executionBar = (totalReputation / 100) * params.queuedVoteRequiredPercentage; ExecutionState executionState = ExecutionState.None; uint256 averageDownstakesOfBoosted; uint256 confidenceThreshold; if (proposal.votes[proposal.winningVote] > executionBar) { // someone crossed the absolute vote execution bar. if (proposal.state == ProposalState.Queued) { executionState = ExecutionState.QueueBarCrossed; } else if (proposal.state == ProposalState.PreBoosted) { executionState = ExecutionState.PreBoostedBarCrossed; } else { executionState = ExecutionState.BoostedBarCrossed; } proposal.state = ProposalState.Executed; } else { if (proposal.state == ProposalState.Queued) { // solhint-disable-next-line not-rely-on-time if ((now - proposal.times[0]) >= params.queuedVotePeriodLimit) { proposal.state = ProposalState.ExpiredInQueue; proposal.winningVote = NO; executionState = ExecutionState.QueueTimeOut; } else { confidenceThreshold = threshold(proposal.paramsHash, proposal.organizationId); if (_score(_proposalId) > confidenceThreshold) { //change proposal mode to PreBoosted mode. proposal.state = ProposalState.PreBoosted; // solhint-disable-next-line not-rely-on-time proposal.times[2] = now; proposal.confidenceThreshold = confidenceThreshold; } } } if (proposal.state == ProposalState.PreBoosted) { confidenceThreshold = threshold(proposal.paramsHash, proposal.organizationId); // solhint-disable-next-line not-rely-on-time if ((now - proposal.times[2]) >= params.preBoostedVotePeriodLimit) { if (_score(_proposalId) > confidenceThreshold) { if (orgBoostedProposalsCnt[proposal.organizationId] < MAX_BOOSTED_PROPOSALS) { //change proposal mode to Boosted mode. proposal.state = ProposalState.Boosted; // solhint-disable-next-line not-rely-on-time proposal.times[1] = now; orgBoostedProposalsCnt[proposal.organizationId]++; //add a value to average -> average = average + ((value - average) / nbValues) averageDownstakesOfBoosted = averagesDownstakesOfBoosted[proposal.organizationId]; // solium-disable-next-line indentation averagesDownstakesOfBoosted[proposal.organizationId] = uint256( int256(averageDownstakesOfBoosted) + ((int256(proposal.stakes[NO]) - int256(averageDownstakesOfBoosted)) / int256(orgBoostedProposalsCnt[proposal.organizationId])) ); } } else { proposal.state = ProposalState.Queued; } } else { //check the Confidence level is stable uint256 proposalScore = _score(_proposalId); if (proposalScore <= proposal.confidenceThreshold.min(confidenceThreshold)) { proposal.state = ProposalState.Queued; } else if (proposal.confidenceThreshold > proposalScore) { proposal.confidenceThreshold = confidenceThreshold; emit ConfidenceLevelChange(_proposalId, confidenceThreshold); } } } } if ((proposal.state == ProposalState.Boosted) || (proposal.state == ProposalState.QuietEndingPeriod)) { // solhint-disable-next-line not-rely-on-time if ((now - proposal.times[1]) >= proposal.currentBoostedVotePeriodLimit) { proposal.state = ProposalState.Executed; executionState = ExecutionState.BoostedTimeOut; } } if (executionState != ExecutionState.None) { if ( (executionState == ExecutionState.BoostedTimeOut) || (executionState == ExecutionState.BoostedBarCrossed) ) { orgBoostedProposalsCnt[tmpProposal.organizationId] = orgBoostedProposalsCnt[tmpProposal.organizationId] .sub(1); //remove a value from average = ((average * nbValues) - value) / (nbValues - 1); uint256 boostedProposals = orgBoostedProposalsCnt[tmpProposal.organizationId]; if (boostedProposals == 0) { averagesDownstakesOfBoosted[proposal.organizationId] = 0; } else { averageDownstakesOfBoosted = averagesDownstakesOfBoosted[proposal.organizationId]; averagesDownstakesOfBoosted[proposal.organizationId] = (averageDownstakesOfBoosted.mul(boostedProposals + 1).sub(proposal.stakes[NO])) / boostedProposals; } } emit ExecuteProposal( _proposalId, organizations[proposal.organizationId], proposal.winningVote, totalReputation ); emit GPExecuteProposal(_proposalId, executionState); proposal.daoBounty = proposal.daoBountyRemain; ProposalExecuteInterface(proposal.callbacks).executeProposal(_proposalId, int256(proposal.winningVote)); } if (tmpProposal.state != proposal.state) { emit StateChange(_proposalId, proposal.state); } return (executionState != ExecutionState.None); } /** * @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, address _staker ) internal returns (bool) { // 0 is not a valid vote. require(_vote <= NUM_OF_CHOICES && _vote > 0, "wrong vote value"); require(_amount > 0, "staking amount should be >0"); if (_execute(_proposalId)) { return true; } Proposal storage proposal = proposals[_proposalId]; if ((proposal.state != ProposalState.PreBoosted) && (proposal.state != ProposalState.Queued)) { return false; } // enable to increase stake only on the previous stake vote Staker storage staker = proposal.stakers[_staker]; if ((staker.amount > 0) && (staker.vote != _vote)) { return false; } uint256 amount = _amount; require(stakingToken.transferFrom(_staker, address(this), amount), "fail transfer from staker"); proposal.totalStakes = proposal.totalStakes.add(amount); //update totalRedeemableStakes staker.amount = staker.amount.add(amount); //This is to prevent average downstakes calculation overflow //Note that any how GEN cap is 100000000 ether. require(staker.amount <= 0x100000000000000000000000000000000, "staking amount is too high"); require( proposal.totalStakes <= uint256(0x100000000000000000000000000000000).sub(proposal.daoBountyRemain), "total stakes is too high" ); if (_vote == YES) { staker.amount4Bounty = staker.amount4Bounty.add(amount); } staker.vote = _vote; proposal.stakes[_vote] = amount.add(proposal.stakes[_vote]); emit Stake(_proposalId, organizations[proposal.organizationId], _staker, _vote, _amount); return _execute(_proposalId); } /** * @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. * @param _rep how many reputation the voter would like to stake for this vote. * if _rep==0 so the voter full reputation will be use. * @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 */ // solhint-disable-next-line function-max-lines,code-complexity function internalVote( bytes32 _proposalId, address _voter, uint256 _vote, uint256 _rep ) internal returns (bool) { require(_vote <= NUM_OF_CHOICES && _vote > 0, "0 < _vote <= 2"); if (_execute(_proposalId)) { return true; } Parameters memory params = parameters[proposals[_proposalId].paramsHash]; Proposal storage proposal = proposals[_proposalId]; // Check voter has enough reputation: uint256 reputation = VotingMachineCallbacksInterface(proposal.callbacks).reputationOf(_voter, _proposalId); require(reputation > 0, "_voter must have reputation"); require(reputation >= _rep, "reputation >= _rep"); uint256 rep = _rep; if (rep == 0) { rep = reputation; } // If this voter has already voted, return false. if (proposal.voters[_voter].reputation != 0) { return false; } // The voting itself: proposal.votes[_vote] = rep.add(proposal.votes[_vote]); //check if the current winningVote changed or there is a tie. //for the case there is a tie the current winningVote set to NO. if ( (proposal.votes[_vote] > proposal.votes[proposal.winningVote]) || ((proposal.votes[NO] == proposal.votes[proposal.winningVote]) && proposal.winningVote == YES) ) { if ( (proposal.state == ProposalState.Boosted && ((now - proposal.times[1]) >= (params.boostedVotePeriodLimit - params.quietEndingPeriod))) || // solhint-disable-next-line not-rely-on-time proposal.state == ProposalState.QuietEndingPeriod ) { //quietEndingPeriod if (proposal.state != ProposalState.QuietEndingPeriod) { proposal.currentBoostedVotePeriodLimit = params.quietEndingPeriod; proposal.state = ProposalState.QuietEndingPeriod; emit StateChange(_proposalId, proposal.state); } // solhint-disable-next-line not-rely-on-time proposal.times[1] = now; } proposal.winningVote = _vote; } proposal.voters[_voter] = Voter({ reputation: rep, vote: _vote, preBoosted: ((proposal.state == ProposalState.PreBoosted) || (proposal.state == ProposalState.Queued)) }); if ((proposal.state == ProposalState.PreBoosted) || (proposal.state == ProposalState.Queued)) { proposal.preBoostedVotes[_vote] = rep.add(proposal.preBoostedVotes[_vote]); uint256 reputationDeposit = (params.votersReputationLossRatio.mul(rep)) / 100; VotingMachineCallbacksInterface(proposal.callbacks).burnReputation(reputationDeposit, _voter, _proposalId); } emit VoteProposal(_proposalId, organizations[proposal.organizationId], _voter, _vote, rep); return _execute(_proposalId); } /** * @dev _score return the proposal score (Confidence level) * For dual choice proposal S = (S+)/(S-) * @param _proposalId the ID of the proposal * @return uint256 proposal score as real number. */ function _score(bytes32 _proposalId) internal view returns (uint256) { Proposal storage proposal = proposals[_proposalId]; //proposal.stakes[NO] cannot be zero as the dao downstake > 0 for each proposal. return uint216(proposal.stakes[YES]).fraction(uint216(proposal.stakes[NO])); } /** * @dev _isVotable check if the proposal is votable * @param _proposalId the ID of the proposal * @return bool true or false */ function _isVotable(bytes32 _proposalId) internal view returns (bool) { ProposalState pState = proposals[_proposalId].state; return ((pState == ProposalState.PreBoosted) || (pState == ProposalState.Boosted) || (pState == ProposalState.QuietEndingPeriod) || (pState == ProposalState.Queued)); } } // File openzeppelin-solidity/contracts/cryptography/[email protected] pragma solidity ^0.5.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * NOTE: This call _does not revert_ if the signature is invalid, or * if the signer is otherwise unable to be retrieved. In those scenarios, * the zero address is returned. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { return (address(0)); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } if (v != 27 && v != 28) { return address(0); } // If the signature is valid (and not malleable), return the signer address return ecrecover(hash, v, r, s); } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } } // File contracts/daostack/votingMachines/GenesisProtocol.sol pragma solidity 0.5.17; /** * @title GenesisProtocol implementation -an organization's voting machine scheme. */ 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 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 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 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); 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) 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. * 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) { //this is not allowed return; } /** * @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 returns (uint256) { return NUM_OF_CHOICES; } /** * @dev getProposalTimes returns proposals times variables. * @param _proposalId id of the proposal * @return proposals times array */ function getProposalTimes(bytes32 _proposalId) external view returns (uint256[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 (uint256, uint256) { 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 _isVotable(_proposalId); } /** * @dev proposalStatus return the total votes and stakes for a given proposal * @param _proposalId the ID of the proposal * @return uint256 preBoostedVotes YES * @return uint256 preBoostedVotes 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].preBoostedVotes[YES], proposals[_proposalId].preBoostedVotes[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) { return (proposals[_proposalId].stakers[_staker].vote, proposals[_proposalId].stakers[_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 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 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); } } // File contracts/daostack/votingMachines/VotingMachineCallbacks.sol pragma solidity ^0.5.4; contract VotingMachineCallbacks is VotingMachineCallbacksInterface { struct ProposalInfo { uint256 blockNumber; // the proposal's block number Avatar avatar; // the proposal's avatar } modifier onlyVotingMachine(bytes32 _proposalId) { require(proposalsInfo[msg.sender][_proposalId].avatar != Avatar(address(0)), "only VotingMachine"); _; } // VotingMaching -> proposalId -> ProposalInfo mapping(address => mapping(bytes32 => ProposalInfo)) public proposalsInfo; function mintReputation( uint256 _amount, address _beneficiary, bytes32 _proposalId ) external onlyVotingMachine(_proposalId) returns (bool) { Avatar avatar = proposalsInfo[msg.sender][_proposalId].avatar; if (avatar == Avatar(0)) { return false; } return ControllerInterface(avatar.owner()).mintReputation(_amount, _beneficiary, address(avatar)); } function burnReputation( uint256 _amount, address _beneficiary, bytes32 _proposalId ) external onlyVotingMachine(_proposalId) returns (bool) { Avatar avatar = proposalsInfo[msg.sender][_proposalId].avatar; if (avatar == Avatar(0)) { return false; } return ControllerInterface(avatar.owner()).burnReputation(_amount, _beneficiary, address(avatar)); } function stakingTokenTransfer( IERC20 _stakingToken, address _beneficiary, uint256 _amount, bytes32 _proposalId ) external onlyVotingMachine(_proposalId) returns (bool) { Avatar avatar = proposalsInfo[msg.sender][_proposalId].avatar; if (avatar == Avatar(0)) { return false; } return ControllerInterface(avatar.owner()).externalTokenTransfer(_stakingToken, _beneficiary, _amount, avatar); } function balanceOfStakingToken(IERC20 _stakingToken, bytes32 _proposalId) external view returns (uint256) { Avatar avatar = proposalsInfo[msg.sender][_proposalId].avatar; if (proposalsInfo[msg.sender][_proposalId].avatar == Avatar(0)) { return 0; } return _stakingToken.balanceOf(address(avatar)); } function getTotalReputationSupply(bytes32 _proposalId) external view returns (uint256) { ProposalInfo memory proposal = proposalsInfo[msg.sender][_proposalId]; if (proposal.avatar == Avatar(0)) { return 0; } return proposal.avatar.nativeReputation().totalSupplyAt(proposal.blockNumber); } function reputationOf(address _owner, bytes32 _proposalId) external view returns (uint256) { ProposalInfo memory proposal = proposalsInfo[msg.sender][_proposalId]; if (proposal.avatar == Avatar(0)) { return 0; } return proposal.avatar.nativeReputation().balanceOfAt(_owner, proposal.blockNumber); } } // File contracts/daostack/universalSchemes/ContributionReward.sol pragma solidity ^0.5.4; /** * @title A scheme for proposing and rewarding contributions to an organization * @dev An agent can ask an organization to recognize a contribution and reward * him with token, reputation, ether or any combination. */ contract ContributionReward is UniversalScheme, VotingMachineCallbacks, ProposalExecuteInterface { using SafeMath for uint256; event NewContributionProposal( address indexed _avatar, bytes32 indexed _proposalId, address indexed _intVoteInterface, string _descriptionHash, int256 _reputationChange, uint256[5] _rewards, IERC20 _externalToken, address _beneficiary ); event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _param); event RedeemReputation( address indexed _avatar, bytes32 indexed _proposalId, address indexed _beneficiary, int256 _amount ); event RedeemEther( address indexed _avatar, bytes32 indexed _proposalId, address indexed _beneficiary, uint256 _amount ); event RedeemNativeToken( address indexed _avatar, bytes32 indexed _proposalId, address indexed _beneficiary, uint256 _amount ); event RedeemExternalToken( address indexed _avatar, bytes32 indexed _proposalId, address indexed _beneficiary, uint256 _amount ); // A struct holding the data for a contribution proposal struct ContributionProposal { uint256 nativeTokenReward; // Reward asked in the native token of the organization. int256 reputationChange; // Organization reputation reward requested. uint256 ethReward; IERC20 externalToken; uint256 externalTokenReward; address payable beneficiary; uint256 periodLength; uint256 numberOfPeriods; uint256 executionTime; uint256[4] redeemedPeriods; } // A mapping from the organization (Avatar) address to the saved data of the organization: mapping(address => mapping(bytes32 => ContributionProposal)) public organizationsProposals; // A mapping from hashes to parameters (use to store a particular configuration on the controller) struct Parameters { bytes32 voteApproveParams; IntVoteInterface intVote; } // A mapping from hashes to parameters (use to store a particular configuration on the controller) mapping(bytes32 => Parameters) public parameters; /** * @dev execution of proposals, can only be called by the voting machine in which the vote is held. * @param _proposalId the ID of the voting in the voting machine * @param _param a parameter of the voting result, 1 yes and 2 is no. */ function executeProposal(bytes32 _proposalId, int256 _param) external onlyVotingMachine(_proposalId) returns (bool) { ProposalInfo memory proposal = proposalsInfo[msg.sender][_proposalId]; require(organizationsProposals[address(proposal.avatar)][_proposalId].executionTime == 0); require(organizationsProposals[address(proposal.avatar)][_proposalId].beneficiary != address(0)); // Check if vote was successful: if (_param == 1) { // solhint-disable-next-line not-rely-on-time organizationsProposals[address(proposal.avatar)][_proposalId].executionTime = now; } emit ProposalExecuted(address(proposal.avatar), _proposalId, _param); return true; } /** * @dev hash the parameters, save them if necessary, and return the hash value */ function setParameters(bytes32 _voteApproveParams, IntVoteInterface _intVote) public returns (bytes32) { bytes32 paramsHash = getParametersHash(_voteApproveParams, _intVote); parameters[paramsHash].voteApproveParams = _voteApproveParams; parameters[paramsHash].intVote = _intVote; return paramsHash; } /** * @dev return a hash of the given parameters * @param _voteApproveParams parameters for the voting machine used to approve a contribution * @param _intVote the voting machine used to approve a contribution * @return a hash of the parameters */ function getParametersHash(bytes32 _voteApproveParams, IntVoteInterface _intVote) public pure returns (bytes32) { return (keccak256(abi.encodePacked(_voteApproveParams, _intVote))); } /** * @dev Submit a proposal for a reward for a contribution: * @param _avatar Avatar of the organization that the contribution was made for * @param _descriptionHash A hash of the proposal's description * @param _reputationChange - Amount of reputation change requested .Can be negative. * @param _rewards rewards array: * rewards[0] - Amount of tokens requested per period * rewards[1] - Amount of ETH requested per period * rewards[2] - Amount of external tokens requested per period * rewards[3] - Period length - if set to zero it allows immediate redeeming after execution. * rewards[4] - Number of periods * @param _externalToken Address of external token, if reward is requested there * @param _beneficiary Who gets the rewards */ function proposeContributionReward( Avatar _avatar, string memory _descriptionHash, int256 _reputationChange, uint256[5] memory _rewards, IERC20 _externalToken, address payable _beneficiary ) public returns (bytes32) { validateProposalParams(_reputationChange, _rewards); Parameters memory controllerParams = parameters[getParametersFromController(_avatar)]; bytes32 contributionId = controllerParams.intVote.propose( 2, controllerParams.voteApproveParams, msg.sender, address(_avatar) ); address payable beneficiary = _beneficiary; if (beneficiary == address(0)) { beneficiary = msg.sender; } ContributionProposal memory proposal = ContributionProposal({ nativeTokenReward: _rewards[0], reputationChange: _reputationChange, ethReward: _rewards[1], externalToken: _externalToken, externalTokenReward: _rewards[2], beneficiary: beneficiary, periodLength: _rewards[3], numberOfPeriods: _rewards[4], executionTime: 0, redeemedPeriods: [uint256(0), uint256(0), uint256(0), uint256(0)] }); organizationsProposals[address(_avatar)][contributionId] = proposal; emit NewContributionProposal( address(_avatar), contributionId, address(controllerParams.intVote), _descriptionHash, _reputationChange, _rewards, _externalToken, beneficiary ); proposalsInfo[address(controllerParams.intVote)][contributionId] = ProposalInfo({ blockNumber: block.number, avatar: _avatar }); return contributionId; } /** * @dev RedeemReputation reward for proposal * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @return reputation the redeemed reputation. */ function redeemReputation(bytes32 _proposalId, Avatar _avatar) public returns (int256 reputation) { ContributionProposal memory _proposal = organizationsProposals[address(_avatar)][_proposalId]; ContributionProposal storage proposal = organizationsProposals[address(_avatar)][_proposalId]; require(proposal.executionTime != 0); uint256 periodsToPay = getPeriodsToPay(_proposalId, address(_avatar), 0); //set proposal reward to zero to prevent reentrancy attack. proposal.reputationChange = 0; reputation = int256(periodsToPay) * _proposal.reputationChange; if (reputation > 0) { require( ControllerInterface(_avatar.owner()).mintReputation( uint256(reputation), _proposal.beneficiary, address(_avatar) ) ); } else if (reputation < 0) { require( ControllerInterface(_avatar.owner()).burnReputation( uint256(reputation * (-1)), _proposal.beneficiary, address(_avatar) ) ); } if (reputation != 0) { proposal.redeemedPeriods[0] = proposal.redeemedPeriods[0].add(periodsToPay); emit RedeemReputation(address(_avatar), _proposalId, _proposal.beneficiary, reputation); } //restore proposal reward. proposal.reputationChange = _proposal.reputationChange; } /** * @dev RedeemNativeToken reward for proposal * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @return amount the redeemed nativeToken. */ function redeemNativeToken(bytes32 _proposalId, Avatar _avatar) public returns (uint256 amount) { ContributionProposal memory _proposal = organizationsProposals[address(_avatar)][_proposalId]; ContributionProposal storage proposal = organizationsProposals[address(_avatar)][_proposalId]; require(proposal.executionTime != 0); uint256 periodsToPay = getPeriodsToPay(_proposalId, address(_avatar), 1); //set proposal rewards to zero to prevent reentrancy attack. proposal.nativeTokenReward = 0; amount = periodsToPay.mul(_proposal.nativeTokenReward); if (amount > 0) { require(ControllerInterface(_avatar.owner()).mintTokens(amount, _proposal.beneficiary, address(_avatar))); proposal.redeemedPeriods[1] = proposal.redeemedPeriods[1].add(periodsToPay); emit RedeemNativeToken(address(_avatar), _proposalId, _proposal.beneficiary, amount); } //restore proposal reward. proposal.nativeTokenReward = _proposal.nativeTokenReward; } /** * @dev RedeemEther reward for proposal * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @return amount ether redeemed amount */ function redeemEther(bytes32 _proposalId, Avatar _avatar) public returns (uint256 amount) { ContributionProposal memory _proposal = organizationsProposals[address(_avatar)][_proposalId]; ContributionProposal storage proposal = organizationsProposals[address(_avatar)][_proposalId]; require(proposal.executionTime != 0); uint256 periodsToPay = getPeriodsToPay(_proposalId, address(_avatar), 2); //set proposal rewards to zero to prevent reentrancy attack. proposal.ethReward = 0; amount = periodsToPay.mul(_proposal.ethReward); if (amount > 0) { require(ControllerInterface(_avatar.owner()).sendEther(amount, _proposal.beneficiary, _avatar)); proposal.redeemedPeriods[2] = proposal.redeemedPeriods[2].add(periodsToPay); emit RedeemEther(address(_avatar), _proposalId, _proposal.beneficiary, amount); } //restore proposal reward. proposal.ethReward = _proposal.ethReward; } /** * @dev RedeemNativeToken reward for proposal * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @return amount the external token redeemed amount */ function redeemExternalToken(bytes32 _proposalId, Avatar _avatar) public returns (uint256 amount) { ContributionProposal memory _proposal = organizationsProposals[address(_avatar)][_proposalId]; ContributionProposal storage proposal = organizationsProposals[address(_avatar)][_proposalId]; require(proposal.executionTime != 0); uint256 periodsToPay = getPeriodsToPay(_proposalId, address(_avatar), 3); //set proposal rewards to zero to prevent reentrancy attack. proposal.externalTokenReward = 0; if (proposal.externalToken != IERC20(0) && _proposal.externalTokenReward > 0) { amount = periodsToPay.mul(_proposal.externalTokenReward); if (amount > 0) { require( ControllerInterface(_avatar.owner()).externalTokenTransfer( _proposal.externalToken, _proposal.beneficiary, amount, _avatar ) ); proposal.redeemedPeriods[3] = proposal.redeemedPeriods[3].add(periodsToPay); emit RedeemExternalToken(address(_avatar), _proposalId, _proposal.beneficiary, amount); } } //restore proposal reward. proposal.externalTokenReward = _proposal.externalTokenReward; } /** * @dev redeem rewards for proposal * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @param _whatToRedeem whatToRedeem array: * whatToRedeem[0] - reputation * whatToRedeem[1] - nativeTokenReward * whatToRedeem[2] - Ether * whatToRedeem[3] - ExternalToken * @return result boolean array for each redeem type. */ function redeem( bytes32 _proposalId, Avatar _avatar, bool[4] memory _whatToRedeem ) public returns ( int256 reputationReward, uint256 nativeTokenReward, uint256 etherReward, uint256 externalTokenReward ) { if (_whatToRedeem[0]) { reputationReward = redeemReputation(_proposalId, _avatar); } if (_whatToRedeem[1]) { nativeTokenReward = redeemNativeToken(_proposalId, _avatar); } if (_whatToRedeem[2]) { etherReward = redeemEther(_proposalId, _avatar); } if (_whatToRedeem[3]) { externalTokenReward = redeemExternalToken(_proposalId, _avatar); } } /** * @dev getPeriodsToPay return the periods left to be paid for reputation,nativeToken,ether or externalToken. * The function ignore the reward amount to be paid (which can be zero). * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @param _redeemType - the type of the reward : * 0 - reputation * 1 - nativeTokenReward * 2 - Ether * 3 - ExternalToken * @return periods left to be paid. */ function getPeriodsToPay( bytes32 _proposalId, address _avatar, uint256 _redeemType ) public view returns (uint256) { require(_redeemType <= 3, "should be in the redeemedPeriods range"); ContributionProposal memory _proposal = organizationsProposals[_avatar][_proposalId]; if (_proposal.executionTime == 0) return 0; uint256 periodsFromExecution; if (_proposal.periodLength > 0) { // solhint-disable-next-line not-rely-on-time periodsFromExecution = (now.sub(_proposal.executionTime)) / (_proposal.periodLength); } uint256 periodsToPay; if ((_proposal.periodLength == 0) || (periodsFromExecution >= _proposal.numberOfPeriods)) { periodsToPay = _proposal.numberOfPeriods.sub(_proposal.redeemedPeriods[_redeemType]); } else { periodsToPay = periodsFromExecution.sub(_proposal.redeemedPeriods[_redeemType]); } return periodsToPay; } /** * @dev getRedeemedPeriods return the already redeemed periods for reputation, nativeToken, ether or externalToken. * @param _proposalId the ID of the voting in the voting machine * @param _avatar address of the controller * @param _redeemType - the type of the reward : * 0 - reputation * 1 - nativeTokenReward * 2 - Ether * 3 - ExternalToken * @return redeemed period. */ function getRedeemedPeriods( bytes32 _proposalId, address _avatar, uint256 _redeemType ) public view returns (uint256) { return organizationsProposals[_avatar][_proposalId].redeemedPeriods[_redeemType]; } function getProposalEthReward(bytes32 _proposalId, address _avatar) public view returns (uint256) { return organizationsProposals[_avatar][_proposalId].ethReward; } function getProposalExternalTokenReward(bytes32 _proposalId, address _avatar) public view returns (uint256) { return organizationsProposals[_avatar][_proposalId].externalTokenReward; } function getProposalExternalToken(bytes32 _proposalId, address _avatar) public view returns (address) { return address(organizationsProposals[_avatar][_proposalId].externalToken); } function getProposalExecutionTime(bytes32 _proposalId, address _avatar) public view returns (uint256) { return organizationsProposals[_avatar][_proposalId].executionTime; } /** * @dev validateProposalParams validate proposal's rewards parameters. * The function check for potential overflow upon proposal's redeem. * The function reverts if the params are not valid. * @param _reputationChange - Amount of reputation change requested .Can be negative. * @param _rewards rewards array: * rewards[0] - Amount of tokens requested per period * rewards[1] - Amount of ETH requested per period * rewards[2] - Amount of external tokens requested per period * rewards[3] - Period length - if set to zero it allows immediate redeeming after execution. * rewards[4] - Number of periods */ function validateProposalParams(int256 _reputationChange, uint256[5] memory _rewards) private pure { require(_rewards[3] == 0, "periodLength should be 0"); require(_rewards[4] == 1, "numberOfPeriods should be 1"); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract Creation Code
60556023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a72315820512f15d5019bd0ddf634f8ad116b688005593b3e93caf3fda7f48664d23de0b864736f6c63430005110032
Deployed Bytecode
0x73988c9f5d00dd2f054a92e1a315c4be5c0084c95d30146080604052600080fdfea265627a7a72315820512f15d5019bd0ddf634f8ad116b688005593b3e93caf3fda7f48664d23de0b864736f6c63430005110032
Deployed Bytecode Sourcemap
35255:2981:0:-;;;;;;;;
Swarm Source
bzzr://512f15d5019bd0ddf634f8ad116b688005593b3e93caf3fda7f48664d23de0b8
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.