/** * Source Code first verified at https://etherscan.io on Monday, April 1, 2019 (UTC) */ // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: @daostack/infra/contracts/Reputation.sol pragma solidity ^0.5.4; /** * @title Reputation system * @dev A DAO has Reputation System which allows peers to rate other peers in order to build trust . * A reputation is use to assign influence measure to a DAO'S peers. * Reputation is similar to regular tokens but with one crucial difference: It is non-transferable. * The Reputation contract maintain a map of address to reputation value. * It provides an onlyOwner functions to mint and burn reputation _to (or _from) a specific address. */ contract Reputation is Ownable { uint8 public decimals = 18; //Number of decimals of the smallest unit // Event indicating minting of reputation to an address. event Mint(address indexed _to, uint256 _amount); // Event indicating burning of reputation for an address. event Burn(address indexed _from, uint256 _amount); /// @dev `Checkpoint` is the structure that attaches a block number to a /// given value, the block number attached is the one that last changed the /// value struct Checkpoint { // `fromBlock` is the block number that the value was generated from uint128 fromBlock; // `value` is the amount of reputation at a specific block number uint128 value; } // `balances` is the map that tracks the balance of each address, in this // contract when the balance changes the block number that the change // occurred is also included in the map mapping (address => Checkpoint[]) balances; // Tracks the history of the `totalSupply` of the reputation Checkpoint[] totalSupplyHistory; /// @notice Constructor to create a Reputation constructor( ) public { } /// @dev This function makes it easy to get the total number of reputation /// @return The total number of reputation function totalSupply() public view returns (uint256) { return totalSupplyAt(block.number); } //////////////// // Query balance and totalSupply in History //////////////// /** * @dev return the reputation amount of a given owner * @param _owner an address of the owner which we want to get his reputation */ function balanceOf(address _owner) public view returns (uint256 balance) { return balanceOfAt(_owner, block.number); } /// @dev Queries the balance of `_owner` at a specific `_blockNumber` /// @param _owner The address from which the balance will be retrieved /// @param _blockNumber The block number when the balance is queried /// @return The balance at `_blockNumber` function balanceOfAt(address _owner, uint256 _blockNumber) public view returns (uint256) { if ((balances[_owner].length == 0) || (balances[_owner][0].fromBlock > _blockNumber)) { return 0; // This will return the expected balance during normal situations } else { return getValueAt(balances[_owner], _blockNumber); } } /// @notice Total amount of reputation at a specific `_blockNumber`. /// @param _blockNumber The block number when the totalSupply is queried /// @return The total amount of reputation at `_blockNumber` function totalSupplyAt(uint256 _blockNumber) public view returns(uint256) { if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) { return 0; // This will return the expected totalSupply during normal situations } else { return getValueAt(totalSupplyHistory, _blockNumber); } } /// @notice Generates `_amount` reputation that are assigned to `_owner` /// @param _user The address that will be assigned the new reputation /// @param _amount The quantity of reputation generated /// @return True if the reputation are generated correctly function mint(address _user, uint256 _amount) public onlyOwner returns (bool) { uint256 curTotalSupply = totalSupply(); require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow uint256 previousBalanceTo = balanceOf(_user); require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); updateValueAtNow(balances[_user], previousBalanceTo + _amount); emit Mint(_user, _amount); return true; } /// @notice Burns `_amount` reputation from `_owner` /// @param _user The address that will lose the reputation /// @param _amount The quantity of reputation to burn /// @return True if the reputation are burned correctly function burn(address _user, uint256 _amount) public onlyOwner returns (bool) { uint256 curTotalSupply = totalSupply(); uint256 amountBurned = _amount; uint256 previousBalanceFrom = balanceOf(_user); if (previousBalanceFrom < amountBurned) { amountBurned = previousBalanceFrom; } updateValueAtNow(totalSupplyHistory, curTotalSupply - amountBurned); updateValueAtNow(balances[_user], previousBalanceFrom - amountBurned); emit Burn(_user, amountBurned); return true; } //////////////// // Internal helper functions to query and set a value in a snapshot array //////////////// /// @dev `getValueAt` retrieves the number of reputation at a given block number /// @param checkpoints The history of values being queried /// @param _block The block number to retrieve the value at /// @return The number of reputation being queried function getValueAt(Checkpoint[] storage checkpoints, uint256 _block) internal view returns (uint256) { if (checkpoints.length == 0) { return 0; } // Shortcut for the actual value if (_block >= checkpoints[checkpoints.length-1].fromBlock) { return checkpoints[checkpoints.length-1].value; } if (_block < checkpoints[0].fromBlock) { return 0; } // Binary search of the value in the array uint256 min = 0; uint256 max = checkpoints.length-1; while (max > min) { uint256 mid = (max + min + 1) / 2; if (checkpoints[mid].fromBlock<=_block) { min = mid; } else { max = mid-1; } } return checkpoints[min].value; } /// @dev `updateValueAtNow` used to update the `balances` map and the /// `totalSupplyHistory` /// @param checkpoints The history of data being updated /// @param _value The new number of reputation function updateValueAtNow(Checkpoint[] storage checkpoints, uint256 _value) internal { require(uint128(_value) == _value); //check value is in the 128 bits bounderies if ((checkpoints.length == 0) || (checkpoints[checkpoints.length - 1].fromBlock < block.number)) { Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++]; newCheckPoint.fromBlock = uint128(block.number); newCheckPoint.value = uint128(_value); } else { Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1]; oldCheckPoint.value = uint128(_value); } } } // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.2; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.2; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol pragma solidity ^0.5.2; /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://eips.ethereum.org/EIPS/eip-20 * Originally based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for * all accounts just by listening to said events. Note that this isn't required by the specification, and other * compliant implementations may not do it. */ contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return A uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token to a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * 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 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { _approve(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another. * Note that while this function emits an Approval event, this is not required as per the specification, * and other compliant implementations may not emit the event. * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 value) public returns (bool) { _transfer(from, to, value); _approve(from, msg.sender, _allowed[from][msg.sender].sub(value)); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when _allowed[msg.sender][spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue)); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when _allowed[msg.sender][spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue)); return true; } /** * @dev Transfer token for a specified addresses * @param from The address to transfer from. * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address from, address to, uint256 value) internal { require(to != address(0)); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param value The amount that will be created. */ function _mint(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.add(value); _balances[account] = _balances[account].add(value); emit Transfer(address(0), account, value); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @dev Approve an address to spend another addresses' tokens. * @param owner The address that owns the tokens. * @param spender The address that will spend the tokens. * @param value The number of tokens that can be spent. */ function _approve(address owner, address spender, uint256 value) internal { require(spender != address(0)); require(owner != address(0)); _allowed[owner][spender] = value; emit Approval(owner, spender, value); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * Emits an Approval event (reflecting the reduced allowance). * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { _burn(account, value); _approve(account, msg.sender, _allowed[account][msg.sender].sub(value)); } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol pragma solidity ^0.5.2; /** * @title Burnable Token * @dev Token that can be irreversibly burned (destroyed). */ contract ERC20Burnable is ERC20 { /** * @dev Burns a specific amount of tokens. * @param value The amount of token to be burned. */ function burn(uint256 value) public { _burn(msg.sender, value); } /** * @dev Burns a specific amount of tokens from the target address and decrements allowance * @param from address The account whose tokens will be burned. * @param value uint256 The amount of token to be burned. */ function burnFrom(address from, uint256 value) public { _burnFrom(from, value); } } // File: contracts/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/Address.sol pragma solidity ^0.5.2; /** * Utility library of inline functions on addresses */ library Address { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param account address of the account to check * @return whether the target address is a contract */ function isContract(address account) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: contracts/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 constant private TRANSFER_SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)"))); bytes4 constant private TRANSFERFROM_SELECTOR = bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); bytes4 constant private 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/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, uint _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/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/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(uint, uint); function isGlobalConstraintRegistered(address _globalConstraint, address _avatar) external view returns(bool); } // File: contracts/controller/UController.sol pragma solidity ^0.5.4; /** * @title Universal Controller contract * @dev A universal controller hold organizations and controls their tokens ,reputations * 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. */ contract UController is ControllerInterface { struct Scheme { bytes32 paramsHash; // a hash "configuration" of the scheme bytes4 permissions; // A bitwise flags of permissions, // All 0: Not registered, // 1st bit: Flag if the scheme is registered, // 2nd bit: Scheme can register other schemes // 3th bit: Scheme can add/remove global constraints // 4rd bit: Scheme can upgrade the controller // 5th bit: Scheme can call delegatecall } struct GlobalConstraint { address gcAddress; bytes32 params; } struct GlobalConstraintRegister { bool isRegistered; //is registered uint256 index; //index at globalConstraints } struct Organization { DAOToken nativeToken; Reputation nativeReputation; mapping(address=>Scheme) schemes; // globalConstraintsPre that determine pre- conditions for all actions on the controller GlobalConstraint[] globalConstraintsPre; // globalConstraintsPost that determine post-conditions for all actions on the controller GlobalConstraint[] globalConstraintsPost; // globalConstraintsRegisterPre indicate if a globalConstraints is registered as a Pre global constraint. mapping(address=>GlobalConstraintRegister) globalConstraintsRegisterPre; // globalConstraintsRegisterPost indicate if a globalConstraints is registered as a Post global constraint. mapping(address=>GlobalConstraintRegister) globalConstraintsRegisterPost; } //mapping between organization's avatar address to Organization mapping(address=>Organization) public organizations; // newController will point to the new controller after the present controller is upgraded // address external newController; mapping(address=>address) public newControllers;//mapping between avatar address and newController address //mapping for all reputation system and tokens addresses registered. mapping(address=>bool) public actors; event MintReputation (address indexed _sender, address indexed _to, uint256 _amount, address indexed _avatar); event BurnReputation (address indexed _sender, address indexed _from, uint256 _amount, address indexed _avatar); event MintTokens (address indexed _sender, address indexed _beneficiary, uint256 _amount, address indexed _avatar); event RegisterScheme (address indexed _sender, address indexed _scheme, address indexed _avatar); event UnregisterScheme (address indexed _sender, address indexed _scheme, address indexed _avatar); event UpgradeController(address indexed _oldController, address _newController, address _avatar); event AddGlobalConstraint( address indexed _globalConstraint, bytes32 _params, GlobalConstraintInterface.CallPhase _when, address indexed _avatar ); event RemoveGlobalConstraint( address indexed _globalConstraint, uint256 _index, bool _isPre, address indexed _avatar ); /** * @dev newOrganization set up a new organization with default daoCreator. * @param _avatar the organization avatar */ function newOrganization( Avatar _avatar ) external { require(!actors[address(_avatar)]); actors[address(_avatar)] = true; require(_avatar.owner() == address(this)); DAOToken nativeToken = _avatar.nativeToken(); Reputation nativeReputation = _avatar.nativeReputation(); require(nativeToken.owner() == address(this)); require(nativeReputation.owner() == address(this)); //To guaranty uniqueness for the reputation systems. require(!actors[address(nativeReputation)]); actors[address(nativeReputation)] = true; //To guaranty uniqueness for the nativeToken. require(!actors[address(nativeToken)]); actors[address(nativeToken)] = true; organizations[address(_avatar)].nativeToken = nativeToken; organizations[address(_avatar)].nativeReputation = nativeReputation; organizations[address(_avatar)].schemes[msg.sender] = Scheme({paramsHash: bytes32(0), permissions: bytes4(0x0000001f)}); emit RegisterScheme(msg.sender, msg.sender, address(_avatar)); } // Modifiers: modifier onlyRegisteredScheme(address avatar) { require(organizations[avatar].schemes[msg.sender].permissions&bytes4(0x00000001) == bytes4(0x00000001)); _; } modifier onlyRegisteringSchemes(address avatar) { require(organizations[avatar].schemes[msg.sender].permissions&bytes4(0x00000002) == bytes4(0x00000002)); _; } modifier onlyGlobalConstraintsScheme(address avatar) { require(organizations[avatar].schemes[msg.sender].permissions&bytes4(0x00000004) == bytes4(0x00000004)); _; } modifier onlyUpgradingScheme(address _avatar) { require(organizations[_avatar].schemes[msg.sender].permissions&bytes4(0x00000008) == bytes4(0x00000008)); _; } modifier onlyGenericCallScheme(address _avatar) { require(organizations[_avatar].schemes[msg.sender].permissions&bytes4(0x00000010) == bytes4(0x00000010)); _; } modifier onlyMetaDataScheme(address _avatar) { require(organizations[_avatar].schemes[msg.sender].permissions&bytes4(0x00000010) == bytes4(0x00000010)); _; } modifier onlySubjectToConstraint(bytes32 func, address _avatar) { uint256 idx; GlobalConstraint[] memory globalConstraintsPre = organizations[_avatar].globalConstraintsPre; GlobalConstraint[] memory globalConstraintsPost = organizations[_avatar].globalConstraintsPost; for (idx = 0; idx < globalConstraintsPre.length; idx++) { require( (GlobalConstraintInterface(globalConstraintsPre[idx].gcAddress)) .pre(msg.sender, globalConstraintsPre[idx].params, func)); } _; for (idx = 0; idx < globalConstraintsPost.length; idx++) { require( (GlobalConstraintInterface(globalConstraintsPost[idx].gcAddress)) .post(msg.sender, globalConstraintsPost[idx].params, func)); } } /** * @dev Mint `_amount` of reputation that are assigned to `_to` . * @param _amount amount of reputation to mint * @param _to beneficiary address * @param _avatar the address of the organization's avatar * @return bool which represents a success */ function mintReputation(uint256 _amount, address _to, address _avatar) external onlyRegisteredScheme(_avatar) onlySubjectToConstraint("mintReputation", _avatar) returns(bool) { emit MintReputation(msg.sender, _to, _amount, _avatar); return organizations[_avatar].nativeReputation.mint(_to, _amount); } /** * @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 onlyRegisteredScheme(_avatar) onlySubjectToConstraint("burnReputation", _avatar) returns(bool) { emit BurnReputation(msg.sender, _from, _amount, _avatar); return organizations[_avatar].nativeReputation.burn(_from, _amount); } /** * @dev mint tokens . * @param _amount amount of token to mint * @param _beneficiary beneficiary address * @param _avatar the organization avatar. * @return bool which represents a success */ function mintTokens(uint256 _amount, address _beneficiary, address _avatar) external onlyRegisteredScheme(_avatar) onlySubjectToConstraint("mintTokens", _avatar) returns(bool) { emit MintTokens(msg.sender, _beneficiary, _amount, _avatar); return organizations[_avatar].nativeToken.mint(_beneficiary, _amount); } /** * @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 the organization avatar. * @return bool which represents a success */ function registerScheme(address _scheme, bytes32 _paramsHash, bytes4 _permissions, address _avatar) external onlyRegisteringSchemes(_avatar) onlySubjectToConstraint("registerScheme", _avatar) returns(bool) { bytes4 schemePermission = organizations[_avatar].schemes[_scheme].permissions; bytes4 senderPermission = organizations[_avatar].schemes[msg.sender].permissions; // Check scheme has at least the permissions it is changing, and at least the current permissions: // Implementation is a bit messy. One must recall logic-circuits ^^ // produces non-zero if sender does not have all of the perms that are changing between old and new require(bytes4(0x0000001f)&(_permissions^schemePermission)&(~senderPermission) == bytes4(0)); // produces non-zero if sender does not have all of the perms in the old scheme require(bytes4(0x0000001f)&(schemePermission&(~senderPermission)) == bytes4(0)); // Add or change the scheme: organizations[_avatar].schemes[_scheme] = Scheme({paramsHash:_paramsHash, permissions:_permissions|bytes4(0x00000001)}); emit RegisterScheme(msg.sender, _scheme, _avatar); return true; } /** * @dev unregister a scheme * @param _scheme the address of the scheme * @param _avatar the organization avatar. * @return bool which represents a success */ function unregisterScheme(address _scheme, address _avatar) external onlyRegisteringSchemes(_avatar) onlySubjectToConstraint("unregisterScheme", _avatar) returns(bool) { bytes4 schemePermission = organizations[_avatar].schemes[_scheme].permissions; //check if the scheme is registered if (schemePermission&bytes4(0x00000001) == bytes4(0)) { return false; } // Check the unregistering scheme has enough permissions: require( bytes4(0x0000001f)&(schemePermission&(~organizations[_avatar].schemes[msg.sender].permissions)) == bytes4(0)); // Unregister: emit UnregisterScheme(msg.sender, _scheme, _avatar); delete organizations[_avatar].schemes[_scheme]; return true; } /** * @dev unregister the caller's scheme * @param _avatar the organization avatar. * @return bool which represents a success */ function unregisterSelf(address _avatar) external returns(bool) { if (_isSchemeRegistered(msg.sender, _avatar) == false) { return false; } delete organizations[_avatar].schemes[msg.sender]; emit UnregisterScheme(msg.sender, msg.sender, _avatar); return true; } /** * @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 onlyGlobalConstraintsScheme(_avatar) returns(bool) { Organization storage organization = organizations[_avatar]; GlobalConstraintInterface.CallPhase when = GlobalConstraintInterface(_globalConstraint).when(); if ((when == GlobalConstraintInterface.CallPhase.Pre)|| (when == GlobalConstraintInterface.CallPhase.PreAndPost)) { if (!organization.globalConstraintsRegisterPre[_globalConstraint].isRegistered) { organization.globalConstraintsPre.push(GlobalConstraint(_globalConstraint, _params)); organization.globalConstraintsRegisterPre[_globalConstraint] = GlobalConstraintRegister(true, organization.globalConstraintsPre.length-1); }else { organization .globalConstraintsPre[organization.globalConstraintsRegisterPre[_globalConstraint].index] .params = _params; } } if ((when == GlobalConstraintInterface.CallPhase.Post)|| (when == GlobalConstraintInterface.CallPhase.PreAndPost)) { if (!organization.globalConstraintsRegisterPost[_globalConstraint].isRegistered) { organization.globalConstraintsPost.push(GlobalConstraint(_globalConstraint, _params)); organization.globalConstraintsRegisterPost[_globalConstraint] = GlobalConstraintRegister(true, organization.globalConstraintsPost.length-1); } else { organization .globalConstraintsPost[organization.globalConstraintsRegisterPost[_globalConstraint].index] .params = _params; } } emit AddGlobalConstraint(_globalConstraint, _params, when, _avatar); return true; } /** * @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 onlyGlobalConstraintsScheme(_avatar) returns(bool) { GlobalConstraintInterface.CallPhase when = GlobalConstraintInterface(_globalConstraint).when(); if ((when == GlobalConstraintInterface.CallPhase.Pre)|| (when == GlobalConstraintInterface.CallPhase.PreAndPost)) { removeGlobalConstraintPre(_globalConstraint, _avatar); } if ((when == GlobalConstraintInterface.CallPhase.Post)|| (when == GlobalConstraintInterface.CallPhase.PreAndPost)) { removeGlobalConstraintPost(_globalConstraint, _avatar); } return true; } /** * @dev upgrade the Controller * The function will trigger an event 'UpgradeController'. * @param _newController the address of the new controller. * @param _avatar the organization avatar. * @return bool which represents a success */ function upgradeController(address _newController, Avatar _avatar) external onlyUpgradingScheme(address(_avatar)) returns(bool) { require(newControllers[address(_avatar)] == address(0)); // so the upgrade could be done once for a contract. require(_newController != address(0)); newControllers[address(_avatar)] = _newController; _avatar.transferOwnership(_newController); require(_avatar.owner() == _newController); if (organizations[address(_avatar)].nativeToken.owner() == address(this)) { organizations[address(_avatar)].nativeToken.transferOwnership(_newController); require(organizations[address(_avatar)].nativeToken.owner() == _newController); } if (organizations[address(_avatar)].nativeReputation.owner() == address(this)) { organizations[address(_avatar)].nativeReputation.transferOwnership(_newController); require(organizations[address(_avatar)].nativeReputation.owner() == _newController); } emit UpgradeController(address(this), _newController, address(_avatar)); return true; } /** * @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 onlyGenericCallScheme(address(_avatar)) onlySubjectToConstraint("genericCall", address(_avatar)) returns (bool, bytes memory) { return _avatar.genericCall(_contract, _data, _value); } /** * @dev send some ether * @param _amountInWei the amount of ether (in Wei) to send * @param _to address of the beneficiary * @param _avatar the organization avatar. * @return bool which represents a success */ function sendEther(uint256 _amountInWei, address payable _to, Avatar _avatar) external onlyRegisteredScheme(address(_avatar)) onlySubjectToConstraint("sendEther", address(_avatar)) returns(bool) { return _avatar.sendEther(_amountInWei, _to); } /** * @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 the organization avatar. * @return bool which represents a success */ function externalTokenTransfer(IERC20 _externalToken, address _to, uint256 _value, Avatar _avatar) external onlyRegisteredScheme(address(_avatar)) onlySubjectToConstraint("externalTokenTransfer", address(_avatar)) returns(bool) { return _avatar.externalTokenTransfer(_externalToken, _to, _value); } /** * @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 the organization avatar. * @return bool which represents a success */ function externalTokenTransferFrom( IERC20 _externalToken, address _from, address _to, uint256 _value, Avatar _avatar) external onlyRegisteredScheme(address(_avatar)) onlySubjectToConstraint("externalTokenTransferFrom", address(_avatar)) returns(bool) { return _avatar.externalTokenTransferFrom(_externalToken, _from, _to, _value); } /** * @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 onlyRegisteredScheme(address(_avatar)) onlySubjectToConstraint("externalTokenApproval", address(_avatar)) returns(bool) { return _avatar.externalTokenApproval(_externalToken, _spender, _value); } /** * @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 onlyMetaDataScheme(address(_avatar)) returns(bool) { return _avatar.metaData(_metaData); } function isSchemeRegistered( address _scheme, address _avatar) external view returns(bool) { return _isSchemeRegistered(_scheme, _avatar); } function getSchemeParameters(address _scheme, address _avatar) external view returns(bytes32) { return organizations[_avatar].schemes[_scheme].paramsHash; } function getSchemePermissions(address _scheme, address _avatar) external view returns(bytes4) { return organizations[_avatar].schemes[_scheme].permissions; } function getGlobalConstraintParameters(address _globalConstraint, address _avatar) external view returns(bytes32) { Organization storage organization = organizations[_avatar]; GlobalConstraintRegister memory register = organization.globalConstraintsRegisterPre[_globalConstraint]; if (register.isRegistered) { return organization.globalConstraintsPre[register.index].params; } register = organization.globalConstraintsRegisterPost[_globalConstraint]; if (register.isRegistered) { return organization.globalConstraintsPost[register.index].params; } } /** * @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(uint, uint) { return ( organizations[_avatar].globalConstraintsPre.length, organizations[_avatar].globalConstraintsPost.length ); } function isGlobalConstraintRegistered(address _globalConstraint, address _avatar) external view returns(bool) { return (organizations[_avatar].globalConstraintsRegisterPre[_globalConstraint].isRegistered || organizations[_avatar].globalConstraintsRegisterPost[_globalConstraint].isRegistered); } /** * @dev getNativeReputation * @param _avatar the organization avatar. * @return organization native reputation */ function getNativeReputation(address _avatar) external view returns(address) { return address(organizations[_avatar].nativeReputation); } /** * @dev removeGlobalConstraintPre * @param _globalConstraint the address of the global constraint to be remove. * @param _avatar the organization avatar. * @return bool which represents a success */ function removeGlobalConstraintPre(address _globalConstraint, address _avatar) private returns(bool) { GlobalConstraintRegister memory globalConstraintRegister = organizations[_avatar].globalConstraintsRegisterPre[_globalConstraint]; GlobalConstraint[] storage globalConstraints = organizations[_avatar].globalConstraintsPre; if (globalConstraintRegister.isRegistered) { if (globalConstraintRegister.index < globalConstraints.length-1) { GlobalConstraint memory globalConstraint = globalConstraints[globalConstraints.length-1]; globalConstraints[globalConstraintRegister.index] = globalConstraint; organizations[_avatar].globalConstraintsRegisterPre[globalConstraint.gcAddress].index = globalConstraintRegister.index; } globalConstraints.length--; delete organizations[_avatar].globalConstraintsRegisterPre[_globalConstraint]; emit RemoveGlobalConstraint(_globalConstraint, globalConstraintRegister.index, true, _avatar); return true; } return false; } /** * @dev removeGlobalConstraintPost * @param _globalConstraint the address of the global constraint to be remove. * @param _avatar the organization avatar. * @return bool which represents a success */ function removeGlobalConstraintPost(address _globalConstraint, address _avatar) private returns(bool) { GlobalConstraintRegister memory globalConstraintRegister = organizations[_avatar].globalConstraintsRegisterPost[_globalConstraint]; GlobalConstraint[] storage globalConstraints = organizations[_avatar].globalConstraintsPost; if (globalConstraintRegister.isRegistered) { if (globalConstraintRegister.index < globalConstraints.length-1) { GlobalConstraint memory globalConstraint = globalConstraints[globalConstraints.length-1]; globalConstraints[globalConstraintRegister.index] = globalConstraint; organizations[_avatar].globalConstraintsRegisterPost[globalConstraint.gcAddress].index = globalConstraintRegister.index; } globalConstraints.length--; delete organizations[_avatar].globalConstraintsRegisterPost[_globalConstraint]; emit RemoveGlobalConstraint(_globalConstraint, globalConstraintRegister.index, false, _avatar); return true; } return false; } function _isSchemeRegistered( address _scheme, address _avatar) private view returns(bool) { return (organizations[_avatar].schemes[_scheme].permissions&bytes4(0x00000001) != bytes4(0)); } }