pragma solidity 0.5.17; import "@daostack/infra/contracts/votingMachines/IntVoteInterface.sol"; import "@daostack/infra/contracts/votingMachines/VotingMachineCallbacksInterface.sol"; import "./UniversalScheme.sol"; import "../votingMachines/VotingMachineCallbacks.sol"; /** * @title A registrar for Schemes for organizations * @dev The SchemeRegistrar is used for registering and unregistering schemes at organizations */ contract SchemeRegistrar is UniversalScheme, VotingMachineCallbacks, ProposalExecuteInterface { event NewSchemeProposal( address indexed _avatar, bytes32 indexed _proposalId, address indexed _intVoteInterface, address _scheme, bytes32 _parametersHash, bytes4 _permissions, string _descriptionHash ); event RemoveSchemeProposal(address indexed _avatar, bytes32 indexed _proposalId, address indexed _intVoteInterface, address _scheme, string _descriptionHash ); event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _param); event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId); // a SchemeProposal is a proposal to add or remove a scheme to/from the an organization struct SchemeProposal { address scheme; // bool addScheme; // true: add a scheme, false: remove a scheme. bytes32 parametersHash; bytes4 permissions; } // A mapping from the organization (Avatar) address to the saved data of the organization: mapping(address=>mapping(bytes32=>SchemeProposal)) public organizationsProposals; // A mapping from hashes to parameters (use to store a particular configuration on the controller) struct Parameters { bytes32 voteRegisterParams; bytes32 voteRemoveParams; IntVoteInterface intVote; } 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) { Avatar avatar = proposalsInfo[msg.sender][_proposalId].avatar; SchemeProposal memory proposal = organizationsProposals[address(avatar)][_proposalId]; require(proposal.scheme != address(0)); delete organizationsProposals[address(avatar)][_proposalId]; emit ProposalDeleted(address(avatar), _proposalId); if (_param == 1) { // Define controller and get the params: Controller controller = Controller(avatar.owner()); // Add a scheme: if (proposal.addScheme) { require(controller.registerScheme( proposal.scheme, proposal.parametersHash, proposal.permissions, address(avatar)) ); } // Remove a scheme: if (!proposal.addScheme) { require(controller.unregisterScheme(proposal.scheme, address(avatar))); } } emit ProposalExecuted(address(avatar), _proposalId, _param); return true; } /** * @dev hash the parameters, save them if necessary, and return the hash value */ function setParameters( bytes32 _voteRegisterParams, bytes32 _voteRemoveParams, IntVoteInterface _intVote ) public returns(bytes32) { bytes32 paramsHash = getParametersHash(_voteRegisterParams, _voteRemoveParams, _intVote); parameters[paramsHash].voteRegisterParams = _voteRegisterParams; parameters[paramsHash].voteRemoveParams = _voteRemoveParams; parameters[paramsHash].intVote = _intVote; return paramsHash; } /** * @dev create a proposal to register a scheme * @param _avatar the address of the organization the scheme will be registered for * @param _scheme the address of the scheme to be registered * @param _parametersHash a hash of the configuration of the _scheme * @param _permissions the permission of the scheme to be registered * @param _descriptionHash proposal's description hash * @return a proposal Id * @dev NB: not only proposes the vote, but also votes for it */ function proposeScheme( Avatar _avatar, address _scheme, bytes32 _parametersHash, bytes4 _permissions, string memory _descriptionHash ) public returns(bytes32) { // propose require(_scheme != address(0), "scheme cannot be zero"); Parameters memory controllerParams = parameters[getParametersFromController(_avatar)]; bytes32 proposalId = controllerParams.intVote.propose( 2, controllerParams.voteRegisterParams, msg.sender, address(_avatar) ); SchemeProposal memory proposal = SchemeProposal({ scheme: _scheme, parametersHash: _parametersHash, addScheme: true, permissions: _permissions }); emit NewSchemeProposal( address(_avatar), proposalId, address(controllerParams.intVote), _scheme, _parametersHash, _permissions, _descriptionHash ); organizationsProposals[address(_avatar)][proposalId] = proposal; proposalsInfo[address(controllerParams.intVote)][proposalId] = ProposalInfo({ blockNumber:block.number, avatar:_avatar }); return proposalId; } /** * @dev propose to remove a scheme for a controller * @param _avatar the address of the controller from which we want to remove a scheme * @param _scheme the address of the scheme we want to remove * @param _descriptionHash proposal description hash * NB: not only registers the proposal, but also votes for it */ function proposeToRemoveScheme(Avatar _avatar, address _scheme, string memory _descriptionHash) public returns(bytes32) { require(_scheme != address(0), "scheme cannot be zero"); bytes32 paramsHash = getParametersFromController(_avatar); Parameters memory params = parameters[paramsHash]; IntVoteInterface intVote = params.intVote; bytes32 proposalId = intVote.propose(2, params.voteRemoveParams, msg.sender, address(_avatar)); organizationsProposals[address(_avatar)][proposalId].scheme = _scheme; emit RemoveSchemeProposal(address(_avatar), proposalId, address(intVote), _scheme, _descriptionHash); proposalsInfo[address(params.intVote)][proposalId] = ProposalInfo({ blockNumber:block.number, avatar:_avatar }); return proposalId; } function getParametersHash( bytes32 _voteRegisterParams, bytes32 _voteRemoveParams, IntVoteInterface _intVote ) public pure returns(bytes32) { return keccak256(abi.encodePacked(_voteRegisterParams, _voteRemoveParams, _intVote)); } }