Implementation
Writing the Implementation Logic
Since we took care of the initialization of our plugin, we can now implement the implementation logic by adding an external execute
function to the contract receiving the actions to be executed.
/// @notice Executes actions in the associated DAO.
function execute(IDAO.Action[] calldata _actions) external {
revert('Unsafe and not implemented.');
}
This is unsafe, because everybody would be able to call this function. Accordingly, the first thing we want to do is to add a permission to the execute
function because otherwise, everybody would be able to execute actions on the DAO. Because we have initialized the PluginClonable
base contract, we can now use its features, i.e., the auth
modifier it provides through the DaoAuthorizable
base class.
Later, we will also use the dao()
getter function returning the associated DAO.
For this, we define a permission identifier bytes32
constant at the top of the contract obtained from the hash keccak256
hash of the permission name we want to choose. Here, we called it the ADMIN_EXECUTE_PERMISSION
and created the associated permission ID.
SimpleAdmin
: Adding modifier auth
to function execute
contract SimpleAdmin is PluginCloneable {
/// @notice The ID of the permission required to call the `execute` function.
bytes32 public constant ADMIN_EXECUTE_PERMISSION_ID = keccak256('ADMIN_EXECUTE_PERMISSION');
address public admin;
/// @notice Initializes the contract.
/// @param _dao The associated DAO.
/// @param _admin The address of the admin.
function initialize(IDAO _dao, address _admin) external initializer {
__PluginCloneable_init(_dao);
admin = _admin;
}
/// @notice Executes actions in the associated DAO.
function execute(IDAO.Action[] calldata _actions) external auth(ADMIN_EXECUTE_PERMISSION_ID) {
revert('Not implemented.');
}
}
You are free to choose the permission name however you like. For example, you could also have used keccak256('SIMPLE_ADMIN_PLUGIN:PERMISSION_1')
. However, it is important that the permission names are descriptive and cannot be confused with each other.
Since the external function can now only be called by authorized callers holding the ADMIN_EXECUTE_PERMISSION
, we can complete the implementation.
SimpleAdmin
: Implementing function execute
contract SimpleAdmin is PluginCloneable {
/// @notice The ID of the permission required to call the `execute` function.
bytes32 public constant ADMIN_EXECUTE_PERMISSION_ID = keccak256('ADMIN_EXECUTE_PERMISSION');
address public admin;
/// @notice Initializes the contract.
/// @param _dao The associated DAO.
/// @param _admin The address of the admin.
function initialize(IDAO _dao, address _admin) external initializer {
__PluginCloneable_init(_dao);
admin = _admin;
}
/// @notice Executes actions in the associated DAO.
/// @param _actions The actions to be executed by the DAO.
function execute(IDAO.Action[] calldata _actions) external auth(ADMIN_EXECUTE_PERMISSION_ID) {
dao().execute({callId: 0x0, actions: _actions, allowFailureMap: 0});
}
}
For now, we used default values for the callId
and allowFailureMap
parameters required by the DAO's execute
function. With this, the plugin implementation could be used and deployed already.
However, since this is a governance plugin, we have to tidy it up a bit more and make sure that the actions proposed and taken as well as the DAO members introduced by the plugin can be properly indexed and displayed on the aragonApp frontend. For this, we describe how to complete the governance plugin by implementing the IProposal
and IMembership
interfaces.
Optionally, you can also allow certain actions to fail by using the failure map feature of the DAO executor.
In the next step, we'll write the PluginSetup
contract.