Skip to content

Commit

Permalink
OS-1462 feature/update dao facotry to return plugin and support no pl…
Browse files Browse the repository at this point in the history
…ugin daos (#619)

* deploying executor and always true condition

* condition restrictions (#617)

* condition restrictions

* fix isGranted (#618)

---------

Co-authored-by: Rekard0 <[email protected]>

* remove alwaystruecondition deployment

* remove .only

* feature/update dao facotry to return plugin and support no plugin dao creation

* fix comment

* feat: return permission and set all pemission and shadow createDao

* feat: improve gas efficiency

* feat add tests

* fix: comment

* feat: redesign with only one function

* fix: remove unncesary files and import

* update: changelog

* feat: cache apply permission

* feat: improve some test checks (#622)

* fix: formating

---------

Co-authored-by: Giorgi Lagidze <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
  • Loading branch information
3 people authored Nov 15, 2024
1 parent b0d8e3b commit 16c4d4c
Show file tree
Hide file tree
Showing 3 changed files with 469 additions and 287 deletions.
1 change: 1 addition & 0 deletions packages/contracts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Updated `createDao` of the `DAOFactory` to return installed plugins and enable DAO creation without requiring plugins.
- Changed the solidity compiler pragma from `0.8.17` to `^0.8.8` for all files.
- Improved type safety by using `abi.encodeCall` instead of `abi.encodeWithSelector` and the more explicit bracket syntax for permissions.
- Bumped OpenZeppelin dependencies to `4.9.6`.
Expand Down
178 changes: 109 additions & 69 deletions packages/contracts/src/framework/dao/DAOFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ contract DAOFactory is ERC165, ProtocolVersion {
/// @notice The plugin setup processor for installing plugins on the newly created `DAO`s.
PluginSetupProcessor public immutable pluginSetupProcessor;

// Cache permission IDs for optimized access
bytes32 internal immutable ROOT_PERMISSION_ID;
bytes32 internal immutable UPGRADE_DAO_PERMISSION_ID;
bytes32 internal immutable SET_TRUSTED_FORWARDER_PERMISSION_ID;
bytes32 internal immutable SET_METADATA_PERMISSION_ID;
bytes32 internal immutable REGISTER_STANDARD_CALLBACK_PERMISSION_ID;
bytes32 internal immutable EXECUTE_PERMISSION_ID;
bytes32 internal immutable APPLY_INSTALLATION_PERMISSION_ID;

/// @notice The container for the DAO settings to be set during the DAO initialization.
/// @param trustedForwarder The address of the trusted forwarder required for meta transactions.
/// @param daoURI The DAO uri used with [EIP-4824](https://eips.ethereum.org/EIPS/eip-4824).
Expand All @@ -53,6 +62,14 @@ contract DAOFactory is ERC165, ProtocolVersion {
bytes data;
}

/// @notice The container with the information about an installed plugin on a DAO.
/// @param plugin The address of the deployed plugin instance.
/// @param preparedSetupData The applied preparedSetupData which contains arrays of helpers and permissions.
struct InstalledPlugin {
address plugin;
IPluginSetup.PreparedSetupData preparedSetupData;
}

/// @notice Thrown if `PluginSettings` array is empty, and no plugin is provided.
error NoPluginProvided();

Expand All @@ -63,7 +80,17 @@ contract DAOFactory is ERC165, ProtocolVersion {
daoRegistry = _registry;
pluginSetupProcessor = _pluginSetupProcessor;

daoBase = address(new DAO());
DAO dao = new DAO();
daoBase = address(dao);

// Cache permission IDs for reduced gas usage in future function calls
ROOT_PERMISSION_ID = dao.ROOT_PERMISSION_ID();
UPGRADE_DAO_PERMISSION_ID = dao.UPGRADE_DAO_PERMISSION_ID();
SET_TRUSTED_FORWARDER_PERMISSION_ID = dao.SET_TRUSTED_FORWARDER_PERMISSION_ID();
SET_METADATA_PERMISSION_ID = dao.SET_METADATA_PERMISSION_ID();
REGISTER_STANDARD_CALLBACK_PERMISSION_ID = dao.REGISTER_STANDARD_CALLBACK_PERMISSION_ID();
EXECUTE_PERMISSION_ID = dao.EXECUTE_PERMISSION_ID();
APPLY_INSTALLATION_PERMISSION_ID = pluginSetupProcessor.APPLY_INSTALLATION_PERMISSION_ID();
}

/// @notice Checks if this or the parent contract supports an interface by its ID.
Expand All @@ -75,83 +102,96 @@ contract DAOFactory is ERC165, ProtocolVersion {
super.supportsInterface(_interfaceId);
}

/// @notice Creates a new DAO, registers it on the DAO registry, and installs a list of plugins via the plugin setup processor.
/// @param _daoSettings The DAO settings to be set during the DAO initialization.
/// @param _pluginSettings The array containing references to plugins and their settings to be installed after the DAO has been created.
/// @notice Creates a new DAO, registers it in the DAO registry, and optionally installs plugins via the plugin setup processor.
/// @dev If `_pluginSettings` is empty, the caller is granted `EXECUTE_PERMISSION` on the DAO.
/// @param _daoSettings The settings to configure during DAO initialization.
/// @param _pluginSettings An array containing plugin references and settings. If provided, each plugin is installed
/// after the DAO creation.
/// @return createdDao The address of the newly created DAO instance.
/// @return installedPlugins An array of `InstalledPlugin` structs, each containing the plugin address and associated
/// helper contracts and permissions, if plugins were installed; otherwise, an empty array.
function createDao(
DAOSettings calldata _daoSettings,
PluginSettings[] calldata _pluginSettings
) external returns (DAO createdDao) {
// Check if no plugin is provided.
if (_pluginSettings.length == 0) {
revert NoPluginProvided();
}

) external returns (DAO createdDao, InstalledPlugin[] memory installedPlugins) {
// Create DAO.
createdDao = _createDAO(_daoSettings);

// Register DAO.
daoRegistry.register(createdDao, msg.sender, _daoSettings.subdomain);

// Get Permission IDs
bytes32 rootPermissionID = createdDao.ROOT_PERMISSION_ID();
bytes32 applyInstallationPermissionID = pluginSetupProcessor
.APPLY_INSTALLATION_PERMISSION_ID();
// Cache address to save gas
address daoAddress = address(createdDao);

// Grant the temporary permissions.
// Grant Temporarily `ROOT_PERMISSION` to `pluginSetupProcessor`.
createdDao.grant(address(createdDao), address(pluginSetupProcessor), rootPermissionID);
// Install plugins if plugin settings are provided.
if (_pluginSettings.length != 0) {
installedPlugins = new InstalledPlugin[](_pluginSettings.length);

// Grant Temporarily `APPLY_INSTALLATION_PERMISSION` on `pluginSetupProcessor` to this `DAOFactory`.
createdDao.grant(
address(pluginSetupProcessor),
address(this),
applyInstallationPermissionID
);
// Cache address to save gas
address pluginSetupProcessorAddress = address(pluginSetupProcessor);

// Grant the temporary permissions.
// Grant Temporarily `ROOT_PERMISSION` to `pluginSetupProcessor`.
createdDao.grant(daoAddress, pluginSetupProcessorAddress, ROOT_PERMISSION_ID);

// Grant Temporarily `APPLY_INSTALLATION_PERMISSION` on `pluginSetupProcessor` to this `DAOFactory`.
createdDao.grant(
pluginSetupProcessorAddress,
address(this),
APPLY_INSTALLATION_PERMISSION_ID
);

// Install plugins on the newly created DAO.
for (uint256 i; i < _pluginSettings.length; ++i) {
// Prepare plugin.
(
address plugin,
IPluginSetup.PreparedSetupData memory preparedSetupData
) = pluginSetupProcessor.prepareInstallation(
address(createdDao),
PluginSetupProcessor.PrepareInstallationParams(
// Install plugins on the newly created DAO.
for (uint256 i; i < _pluginSettings.length; ++i) {
// Prepare plugin.
(
address plugin,
IPluginSetup.PreparedSetupData memory preparedSetupData
) = pluginSetupProcessor.prepareInstallation(
daoAddress,
PluginSetupProcessor.PrepareInstallationParams(
_pluginSettings[i].pluginSetupRef,
_pluginSettings[i].data
)
);

// Apply plugin.
pluginSetupProcessor.applyInstallation(
daoAddress,
PluginSetupProcessor.ApplyInstallationParams(
_pluginSettings[i].pluginSetupRef,
_pluginSettings[i].data
plugin,
preparedSetupData.permissions,
hashHelpers(preparedSetupData.helpers)
)
);

// Apply plugin.
pluginSetupProcessor.applyInstallation(
address(createdDao),
PluginSetupProcessor.ApplyInstallationParams(
_pluginSettings[i].pluginSetupRef,
plugin,
preparedSetupData.permissions,
hashHelpers(preparedSetupData.helpers)
)
installedPlugins[i] = InstalledPlugin(plugin, preparedSetupData);
}

// Revoke the temporarily granted permissions.
// Revoke Temporarily `ROOT_PERMISSION` from `pluginSetupProcessor`.
createdDao.revoke(daoAddress, pluginSetupProcessorAddress, ROOT_PERMISSION_ID);

// Revoke `APPLY_INSTALLATION_PERMISSION` on `pluginSetupProcessor` from this `DAOFactory` .
createdDao.revoke(
pluginSetupProcessorAddress,
address(this),
APPLY_INSTALLATION_PERMISSION_ID
);
} else {
// if no plugin setting is provided, grant EXECUTE_PERMISSION_ID to msg.sender
createdDao.grant(daoAddress, msg.sender, EXECUTE_PERMISSION_ID);
}

// Set the rest of DAO's permissions.
_setDAOPermissions(createdDao);
_setDAOPermissions(daoAddress);

// Revoke the temporarily granted permissions.
// Revoke Temporarily `ROOT_PERMISSION` from `pluginSetupProcessor`.
createdDao.revoke(address(createdDao), address(pluginSetupProcessor), rootPermissionID);

// Revoke `APPLY_INSTALLATION_PERMISSION` on `pluginSetupProcessor` from this `DAOFactory` .
createdDao.revoke(
address(pluginSetupProcessor),
address(this),
applyInstallationPermissionID
);

// Revoke Temporarily `ROOT_PERMISSION_ID` from `pluginSetupProcessor` that implicitly granted to this `DaoFactory`
// Revoke Temporarily `ROOT_PERMISSION_ID` that implicitly granted to this `DaoFactory`
// at the create dao step `address(this)` being the initial owner of the new created DAO.
createdDao.revoke(address(createdDao), address(this), rootPermissionID);
createdDao.revoke(daoAddress, address(this), ROOT_PERMISSION_ID);

return (createdDao, installedPlugins);
}

/// @notice Deploys a new DAO `ERC1967` proxy, and initialize it with this contract as the initial owner.
Expand All @@ -177,39 +217,39 @@ contract DAOFactory is ERC165, ProtocolVersion {
}

/// @notice Sets the required permissions for the new DAO.
/// @param _dao The DAO instance just created.
function _setDAOPermissions(DAO _dao) internal {
/// @param _daoAddress The address of the DAO just created.
function _setDAOPermissions(address _daoAddress) internal {
// set permissionIds on the dao itself.
PermissionLib.SingleTargetPermission[]
memory items = new PermissionLib.SingleTargetPermission[](5);

// Grant DAO all the permissions required
items[0] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant,
address(_dao),
_dao.ROOT_PERMISSION_ID()
_daoAddress,
ROOT_PERMISSION_ID
);
items[1] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant,
address(_dao),
_dao.UPGRADE_DAO_PERMISSION_ID()
_daoAddress,
UPGRADE_DAO_PERMISSION_ID
);
items[2] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant,
address(_dao),
_dao.SET_TRUSTED_FORWARDER_PERMISSION_ID()
_daoAddress,
SET_TRUSTED_FORWARDER_PERMISSION_ID
);
items[3] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant,
address(_dao),
_dao.SET_METADATA_PERMISSION_ID()
_daoAddress,
SET_METADATA_PERMISSION_ID
);
items[4] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant,
address(_dao),
_dao.REGISTER_STANDARD_CALLBACK_PERMISSION_ID()
_daoAddress,
REGISTER_STANDARD_CALLBACK_PERMISSION_ID
);

_dao.applySingleTargetPermissions(address(_dao), items);
DAO(payable(_daoAddress)).applySingleTargetPermissions(_daoAddress, items);
}
}
Loading

0 comments on commit 16c4d4c

Please sign in to comment.