Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OS-1462 feature/update dao facotry to return plugin and support no plugin daos #619

Merged
merged 17 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading