pragma solidity 0.4.24; import "@conflux-/aragon-apps-agent/contracts/Agent.sol"; import "@conflux-/aragon-apps-vault/contracts/Vault.sol"; import "@conflux-/aragon-apps-voting/contracts/Voting.sol"; import "@conflux-/aragon-apps-payroll/contracts/Payroll.sol"; import "@conflux-/aragon-apps-finance/contracts/Finance.sol"; import "@conflux-/aragon-apps-token-manager/contracts/TokenManager.sol"; import "@conflux-/aragon-apps-survey/contracts/Survey.sol"; import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol"; import "@conflux-/aragon-os/contracts/acl/ACL.sol"; import "@conflux-/aragon-os/contracts/apm/Repo.sol"; import "@conflux-/aragon-os/contracts/apm/APMNamehash.sol"; import "@conflux-/aragon-os/contracts/kernel/Kernel.sol"; import "@conflux-/aragon-os/contracts/lib/ens/ENS.sol"; import "@conflux-/aragon-os/contracts/lib/ens/PublicResolver.sol"; import "@conflux-/aragon-os/contracts/factory/DAOFactory.sol"; import "@conflux-/aragon-os/contracts/common/IsContract.sol"; import "@conflux-/aragon-os/contracts/common/Uint256Helpers.sol"; import "@conflux-/aragon-id/contracts/IFIFSResolvingRegistrar.sol"; contract BaseTemplate is APMNamehash, IsContract { using Uint256Helpers for uint256; /* Hardcoded constant to save gas * bytes32 constant internal AGENT_APP_ID = apmNamehash("agent"); // agent.aragonpm.eth * bytes32 constant internal VAULT_APP_ID = apmNamehash("vault"); // vault.aragonpm.eth * bytes32 constant internal VOTING_APP_ID = apmNamehash("voting"); // voting.aragonpm.eth * bytes32 constant internal SURVEY_APP_ID = apmNamehash("survey"); // survey.aragonpm.eth * bytes32 constant internal PAYROLL_APP_ID = apmNamehash("payroll"); // payroll.aragonpm.eth * bytes32 constant internal FINANCE_APP_ID = apmNamehash("finance"); // finance.aragonpm.eth * bytes32 constant internal TOKEN_MANAGER_APP_ID = apmNamehash("token-manager"); // token-manager.aragonpm.eth */ bytes32 constant internal AGENT_APP_ID = 0x9ac98dc5f995bf0211ed589ef022719d1487e5cb2bab505676f0d084c07cf89a; bytes32 constant internal VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1; bytes32 constant internal VOTING_APP_ID = 0x9fa3927f639745e587912d4b0fea7ef9013bf93fb907d29faeab57417ba6e1d4; bytes32 constant internal PAYROLL_APP_ID = 0x463f596a96d808cb28b5d080181e4a398bc793df2c222f6445189eb801001991; bytes32 constant internal FINANCE_APP_ID = 0xbf8491150dafc5dcaee5b861414dca922de09ccffa344964ae167212e8c673ae; bytes32 constant internal TOKEN_MANAGER_APP_ID = 0x6b20a3010614eeebf2138ccec99f028a61c811b3b1a3343b6ff635985c75c91f; bytes32 constant internal SURVEY_APP_ID = 0x030b2ab880b88e228f2da5a3d19a2a31bc10dbf91fb1143776a6de489389471e; string constant private ERROR_ENS_NOT_CONTRACT = "TEMPLATE_ENS_NOT_CONTRACT"; string constant private ERROR_DAO_FACTORY_NOT_CONTRACT = "TEMPLATE_DAO_FAC_NOT_CONTRACT"; string constant private ERROR_ARAGON_ID_NOT_PROVIDED = "TEMPLATE_ARAGON_ID_NOT_PROVIDED"; string constant private ERROR_ARAGON_ID_NOT_CONTRACT = "TEMPLATE_ARAGON_ID_NOT_CONTRACT"; string constant private ERROR_MINIME_FACTORY_NOT_PROVIDED = "TEMPLATE_MINIME_FAC_NOT_PROVIDED"; string constant private ERROR_MINIME_FACTORY_NOT_CONTRACT = "TEMPLATE_MINIME_FAC_NOT_CONTRACT"; string constant private ERROR_CANNOT_CAST_VALUE_TO_ADDRESS = "TEMPLATE_CANNOT_CAST_VALUE_TO_ADDRESS"; string constant private ERROR_INVALID_ID = "TEMPLATE_INVALID_ID"; ENS internal ens; DAOFactory internal daoFactory; MiniMeTokenFactory internal miniMeFactory; IFIFSResolvingRegistrar internal aragonID; event DeployDao(address dao); event SetupDao(address dao); event DeployToken(address token); event InstalledApp(address appProxy, bytes32 appId); constructor(DAOFactory _daoFactory, ENS _ens, MiniMeTokenFactory _miniMeFactory, IFIFSResolvingRegistrar _aragonID) public { require(isContract(address(_ens)), ERROR_ENS_NOT_CONTRACT); require(isContract(address(_daoFactory)), ERROR_DAO_FACTORY_NOT_CONTRACT); ens = _ens; aragonID = _aragonID; daoFactory = _daoFactory; miniMeFactory = _miniMeFactory; } /** * @dev Create a DAO using the DAO Factory and grant the template root permissions so it has full * control during setup. Once the DAO setup has finished, it is recommended to call the * `_transferRootPermissionsFromTemplateAndFinalizeDAO()` helper to transfer the root * permissions to the end entity in control of the organization. */ function _createDAO(uint256 epoch) internal returns (Kernel dao, ACL acl) { dao = daoFactory.newDAO(this, epoch); emit DeployDao(address(dao)); acl = ACL(dao.acl()); _createPermissionForTemplate(acl, dao, dao.APP_MANAGER_ROLE()); } /* ACL */ function _createPermissions(ACL _acl, address[] memory _grantees, address _app, bytes32 _permission, address _manager) internal { _acl.createPermission(_grantees[0], _app, _permission, address(this)); for (uint256 i = 1; i < _grantees.length; i++) { _acl.grantPermission(_grantees[i], _app, _permission); } _acl.revokePermission(address(this), _app, _permission); _acl.setPermissionManager(_manager, _app, _permission); } function _createPermissionForTemplate(ACL _acl, address _app, bytes32 _permission) internal { _acl.createPermission(address(this), _app, _permission, address(this)); } function _removePermissionFromTemplate(ACL _acl, address _app, bytes32 _permission) internal { _acl.revokePermission(address(this), _app, _permission); _acl.removePermissionManager(_app, _permission); } function _transferRootPermissionsFromTemplateAndFinalizeDAO(Kernel _dao, address _to) internal { _transferRootPermissionsFromTemplateAndFinalizeDAO(_dao, _to, _to); } function _transferRootPermissionsFromTemplateAndFinalizeDAO(Kernel _dao, address _to, address _manager) internal { ACL _acl = ACL(_dao.acl()); _transferPermissionFromTemplate(_acl, _dao, _to, _dao.APP_MANAGER_ROLE(), _manager); _transferPermissionFromTemplate(_acl, _acl, _to, _acl.CREATE_PERMISSIONS_ROLE(), _manager); emit SetupDao(_dao); } function _transferPermissionFromTemplate(ACL _acl, address _app, address _to, bytes32 _permission, address _manager) internal { _acl.grantPermission(_to, _app, _permission); _acl.revokePermission(address(this), _app, _permission); _acl.setPermissionManager(_manager, _app, _permission); } /* AGENT */ function _installDefaultAgentApp(Kernel _dao, uint256 _epoch) internal returns (Agent) { bytes memory initializeData = abi.encodeWithSelector(Agent(0).initialize.selector, _epoch); Agent agent = Agent(_installDefaultApp(_dao, AGENT_APP_ID, initializeData)); // We assume that installing the Agent app as a default app means the DAO should have its // Vault replaced by the Agent. Thus, we also set the DAO's recovery app to the Agent. _dao.setRecoveryVaultAppId(AGENT_APP_ID); return agent; } function _installNonDefaultAgentApp(Kernel _dao, uint256 _epoch) internal returns (Agent) { bytes memory initializeData = abi.encodeWithSelector(Agent(0).initialize.selector, _epoch); return Agent(_installNonDefaultApp(_dao, AGENT_APP_ID, initializeData)); } function _createAgentPermissions(ACL _acl, Agent _agent, address _grantee, address _manager) internal { _acl.createPermission(_grantee, _agent, _agent.EXECUTE_ROLE(), _manager); _acl.createPermission(_grantee, _agent, _agent.RUN_SCRIPT_ROLE(), _manager); } /* VAULT */ function _installVaultApp(Kernel _dao, uint256 _epoch) internal returns (Vault) { bytes memory initializeData = abi.encodeWithSelector(Vault(0).initialize.selector, _epoch); return Vault(_installDefaultApp(_dao, VAULT_APP_ID, initializeData)); } function _createVaultPermissions(ACL _acl, Vault _vault, address _grantee, address _manager) internal { _acl.createPermission(_grantee, _vault, _vault.TRANSFER_ROLE(), _manager); } /* VOTING */ function _installVotingApp(Kernel _dao, MiniMeToken _token, uint64[3] memory _votingSettings, uint256 _epoch) internal returns (Voting) { return _installVotingApp(_dao, _token, _votingSettings[0], _votingSettings[1], _votingSettings[2], _epoch); } function _installVotingApp( Kernel _dao, MiniMeToken _token, uint64 _support, uint64 _acceptance, uint64 _duration, uint256 _epoch ) internal returns (Voting) { bytes memory initializeData = abi.encodeWithSelector(Voting(0).initialize.selector, _token, _support, _acceptance, _duration, _epoch); return Voting(_installNonDefaultApp(_dao, VOTING_APP_ID, initializeData)); } function _createVotingPermissions( ACL _acl, Voting _voting, address _settingsGrantee, address _createVotesGrantee, address _manager ) internal { _acl.createPermission(_settingsGrantee, _voting, _voting.MODIFY_QUORUM_ROLE(), _manager); _acl.createPermission(_settingsGrantee, _voting, _voting.MODIFY_SUPPORT_ROLE(), _manager); _acl.createPermission(_createVotesGrantee, _voting, _voting.CREATE_VOTES_ROLE(), _manager); } /* SURVEY */ function _installSurveyApp(Kernel _dao, MiniMeToken _token, uint64 _minParticipationPct, uint64 _surveyTime, uint256 _epoch) internal returns (Survey) { bytes memory initializeData = abi.encodeWithSelector(Survey(0).initialize.selector, _token, _minParticipationPct, _surveyTime, _epoch); return Survey(_installNonDefaultApp(_dao, SURVEY_APP_ID, initializeData)); } function _createSurveyPermissions(ACL _acl, Survey _survey, address _grantee, address _manager) internal { _acl.createPermission(_grantee, _survey, _survey.CREATE_SURVEYS_ROLE(), _manager); _acl.createPermission(_grantee, _survey, _survey.MODIFY_PARTICIPATION_ROLE(), _manager); } /* PAYROLL */ function _installPayrollApp( Kernel _dao, Finance _finance, address _denominationToken, IFeed _priceFeed, uint64 _rateExpiryTime, uint256 _epoch ) internal returns (Payroll) { bytes memory initializeData = abi.encodeWithSelector( Payroll(0).initialize.selector, _finance, _denominationToken, _priceFeed, _rateExpiryTime, _epoch ); return Payroll(_installNonDefaultApp(_dao, PAYROLL_APP_ID, initializeData)); } /** * @dev Internal function to configure payroll permissions. Note that we allow defining different managers for * payroll since it may be useful to have one control the payroll settings (rate expiration, price feed, * and allowed tokens), and another one to control the employee functionality (bonuses, salaries, * reimbursements, employees, etc). * @param _acl ACL instance being configured * @param _acl Payroll app being configured * @param _employeeManager Address that will receive permissions to handle employee payroll functionality * @param _settingsManager Address that will receive permissions to manage payroll settings * @param _permissionsManager Address that will be the ACL manager for the payroll permissions */ function _createPayrollPermissions( ACL _acl, Payroll _payroll, address _employeeManager, address _settingsManager, address _permissionsManager ) internal { _acl.createPermission(_employeeManager, _payroll, _payroll.ADD_BONUS_ROLE(), _permissionsManager); _acl.createPermission(_employeeManager, _payroll, _payroll.ADD_EMPLOYEE_ROLE(), _permissionsManager); _acl.createPermission(_employeeManager, _payroll, _payroll.ADD_REIMBURSEMENT_ROLE(), _permissionsManager); _acl.createPermission(_employeeManager, _payroll, _payroll.TERMINATE_EMPLOYEE_ROLE(), _permissionsManager); _acl.createPermission(_employeeManager, _payroll, _payroll.SET_EMPLOYEE_SALARY_ROLE(), _permissionsManager); _acl.createPermission(_settingsManager, _payroll, _payroll.MODIFY_PRICE_FEED_ROLE(), _permissionsManager); _acl.createPermission(_settingsManager, _payroll, _payroll.MODIFY_RATE_EXPIRY_ROLE(), _permissionsManager); _acl.createPermission(_settingsManager, _payroll, _payroll.MANAGE_ALLOWED_TOKENS_ROLE(), _permissionsManager); } function _unwrapPayrollSettings( uint256[4] memory _payrollSettings ) internal pure returns (address denominationToken, IFeed priceFeed, uint64 rateExpiryTime, address employeeManager) { denominationToken = _toAddress(_payrollSettings[0]); priceFeed = IFeed(_toAddress(_payrollSettings[1])); rateExpiryTime = _payrollSettings[2].toUint64(); employeeManager = _toAddress(_payrollSettings[3]); } /* FINANCE */ function _installFinanceApp(Kernel _dao, Vault _vault, uint64 _periodDuration, uint256 _epoch) internal returns (Finance) { bytes memory initializeData = abi.encodeWithSelector(Finance(0).initialize.selector, _vault, _periodDuration, _epoch); return Finance(_installNonDefaultApp(_dao, FINANCE_APP_ID, initializeData)); } function _createFinancePermissions(ACL _acl, Finance _finance, address _grantee, address _manager) internal { _acl.createPermission(_grantee, _finance, _finance.EXECUTE_PAYMENTS_ROLE(), _manager); _acl.createPermission(_grantee, _finance, _finance.MANAGE_PAYMENTS_ROLE(), _manager); } function _createFinanceCreatePaymentsPermission(ACL _acl, Finance _finance, address _grantee, address _manager) internal { _acl.createPermission(_grantee, _finance, _finance.CREATE_PAYMENTS_ROLE(), _manager); } function _grantCreatePaymentPermission(ACL _acl, Finance _finance, address _to) internal { _acl.grantPermission(_to, _finance, _finance.CREATE_PAYMENTS_ROLE()); } function _transferCreatePaymentManagerFromTemplate(ACL _acl, Finance _finance, address _manager) internal { _acl.setPermissionManager(_manager, _finance, _finance.CREATE_PAYMENTS_ROLE()); } /* TOKEN MANAGER */ function _installTokenManagerApp( Kernel _dao, MiniMeToken _token, bool _transferable, uint256 _maxAccountTokens, uint256 _epoch ) internal returns (TokenManager) { TokenManager tokenManager = TokenManager(_installNonDefaultApp(_dao, TOKEN_MANAGER_APP_ID)); _token.changeController(tokenManager); tokenManager.initialize(_token, _transferable, _maxAccountTokens, _epoch); return tokenManager; } function _createTokenManagerPermissions(ACL _acl, TokenManager _tokenManager, address _grantee, address _manager) internal { _acl.createPermission(_grantee, _tokenManager, _tokenManager.MINT_ROLE(), _manager); _acl.createPermission(_grantee, _tokenManager, _tokenManager.BURN_ROLE(), _manager); } function _mintTokens(ACL _acl, TokenManager _tokenManager, address[] memory _holders, uint256[] memory _stakes) internal { _createPermissionForTemplate(_acl, _tokenManager, _tokenManager.MINT_ROLE()); for (uint256 i = 0; i < _holders.length; i++) { _tokenManager.mint(_holders[i], _stakes[i]); } _removePermissionFromTemplate(_acl, _tokenManager, _tokenManager.MINT_ROLE()); } function _mintTokens(ACL _acl, TokenManager _tokenManager, address[] memory _holders, uint256 _stake) internal { _createPermissionForTemplate(_acl, _tokenManager, _tokenManager.MINT_ROLE()); for (uint256 i = 0; i < _holders.length; i++) { _tokenManager.mint(_holders[i], _stake); } _removePermissionFromTemplate(_acl, _tokenManager, _tokenManager.MINT_ROLE()); } function _mintTokens(ACL _acl, TokenManager _tokenManager, address _holder, uint256 _stake) internal { _createPermissionForTemplate(_acl, _tokenManager, _tokenManager.MINT_ROLE()); _tokenManager.mint(_holder, _stake); _removePermissionFromTemplate(_acl, _tokenManager, _tokenManager.MINT_ROLE()); } /* EVM SCRIPTS */ function _createEvmScriptsRegistryPermissions(ACL _acl, address _grantee, address _manager) internal { EVMScriptRegistry registry = EVMScriptRegistry(_acl.getEVMScriptRegistry()); _acl.createPermission(_grantee, registry, registry.REGISTRY_MANAGER_ROLE(), _manager); _acl.createPermission(_grantee, registry, registry.REGISTRY_ADD_EXECUTOR_ROLE(), _manager); } /* APPS */ function _installNonDefaultApp(Kernel _dao, bytes32 _appId) internal returns (address) { return _installNonDefaultApp(_dao, _appId, new bytes(0)); } function _installNonDefaultApp(Kernel _dao, bytes32 _appId, bytes memory _initializeData) internal returns (address) { return _installApp(_dao, _appId, _initializeData, false); } function _installDefaultApp(Kernel _dao, bytes32 _appId) internal returns (address) { return _installDefaultApp(_dao, _appId, new bytes(0)); } function _installDefaultApp(Kernel _dao, bytes32 _appId, bytes memory _initializeData) internal returns (address) { return _installApp(_dao, _appId, _initializeData, true); } function _installApp(Kernel _dao, bytes32 _appId, bytes memory _initializeData, bool _setDefault) internal returns (address) { address latestBaseAppAddress = _latestVersionAppBase(_appId); address instance = address(_dao.newAppInstance(_appId, latestBaseAppAddress, _initializeData, _setDefault)); emit InstalledApp(instance, _appId); return instance; } function _latestVersionAppBase(bytes32 _appId) internal view returns (address base) { Repo repo = Repo(PublicResolver(ens.resolver(_appId)).addr(_appId)); (,base,) = repo.getLatest(); } /* TOKEN */ function _createToken(string memory _name, string memory _symbol, uint8 _decimals) internal returns (MiniMeToken) { require(address(miniMeFactory) != address(0), ERROR_MINIME_FACTORY_NOT_PROVIDED); MiniMeToken token = miniMeFactory.createCloneToken(MiniMeToken(address(0)), 0, _name, _decimals, _symbol, true); emit DeployToken(address(token)); return token; } function _ensureMiniMeFactoryIsValid(address _miniMeFactory) internal view { require(isContract(address(_miniMeFactory)), ERROR_MINIME_FACTORY_NOT_CONTRACT); } /* IDS */ function _validateId(string memory _id) internal pure { require(bytes(_id).length > 0, ERROR_INVALID_ID); } function _registerID(string memory _name, address _owner) internal { require(address(aragonID) != address(0), ERROR_ARAGON_ID_NOT_PROVIDED); aragonID.register(keccak256(abi.encodePacked(_name)), _owner); } function _ensureAragonIdIsValid(address _aragonID) internal view { require(isContract(address(_aragonID)), ERROR_ARAGON_ID_NOT_CONTRACT); } /* HELPERS */ function _toAddress(uint256 _value) private pure returns (address) { require(_value <= uint160(-1), ERROR_CANNOT_CAST_VALUE_TO_ADDRESS); return address(_value); } }