Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
402 lines (335 sloc) 18.9 KB
pragma solidity 0.4.24;
import "@aragon/apps-agent/contracts/Agent.sol";
import "@aragon/apps-vault/contracts/Vault.sol";
import "@aragon/apps-voting/contracts/Voting.sol";
import "@aragon/apps-payroll/contracts/Payroll.sol";
import "@aragon/apps-finance/contracts/Finance.sol";
import "@aragon/apps-token-manager/contracts/TokenManager.sol";
import "@aragon/apps-survey/contracts/Survey.sol";
import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol";
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/apm/Repo.sol";
import "@aragon/os/contracts/apm/APMNamehash.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/lib/ens/ENS.sol";
import "@aragon/os/contracts/lib/ens/PublicResolver.sol";
import "@aragon/os/contracts/factory/DAOFactory.sol";
import "@aragon/os/contracts/common/IsContract.sol";
import "@aragon/os/contracts/common/Uint256Helpers.sol";
import "@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() internal returns (Kernel dao, ACL acl) {
dao = daoFactory.newDAO(this);
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) internal returns (Agent) {
bytes memory initializeData = abi.encodeWithSelector(Agent(0).initialize.selector);
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) internal returns (Agent) {
bytes memory initializeData = abi.encodeWithSelector(Agent(0).initialize.selector);
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) internal returns (Vault) {
bytes memory initializeData = abi.encodeWithSelector(Vault(0).initialize.selector);
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) internal returns (Voting) {
return _installVotingApp(_dao, _token, _votingSettings[0], _votingSettings[1], _votingSettings[2]);
}
function _installVotingApp(
Kernel _dao,
MiniMeToken _token,
uint64 _support,
uint64 _acceptance,
uint64 _duration
)
internal returns (Voting)
{
bytes memory initializeData = abi.encodeWithSelector(Voting(0).initialize.selector, _token, _support, _acceptance, _duration);
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) internal returns (Survey) {
bytes memory initializeData = abi.encodeWithSelector(Survey(0).initialize.selector, _token, _minParticipationPct, _surveyTime);
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
)
internal returns (Payroll)
{
bytes memory initializeData = abi.encodeWithSelector(
Payroll(0).initialize.selector,
_finance,
_denominationToken,
_priceFeed,
_rateExpiryTime
);
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) internal returns (Finance) {
bytes memory initializeData = abi.encodeWithSelector(Finance(0).initialize.selector, _vault, _periodDuration);
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
)
internal returns (TokenManager)
{
TokenManager tokenManager = TokenManager(_installNonDefaultApp(_dao, TOKEN_MANAGER_APP_ID));
_token.changeController(tokenManager);
tokenManager.initialize(_token, _transferable, _maxAccountTokens);
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);
}
}
You can’t perform that action at this time.