// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "./IWarperPresetFactory.sol"; import "../../contract-registry/ContractEntity.sol"; import "../../contract-registry/Contracts.sol"; import "../../acl/direct/AccessControlledUpgradeable.sol"; import "./WarperPresetFactoryStorage.sol"; import "../IWarperPreset.sol"; /** * @title Warper preset factory contract. */ contract WarperPresetFactory is IWarperPresetFactory, UUPSUpgradeable, ContractEntity, AccessControlledUpgradeable, WarperPresetFactoryStorage { using ClonesUpgradeable for address; using AddressUpgradeable for address; using ERC165CheckerUpgradeable for address; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set; /** * @dev WarperPresetFactory initialization params. * @param acl ACL contract address. * @param metahub Metahub contract address. */ struct WarperPresetFactoryInitParams { IACL acl; IMetahub metahub; } /** * @dev Modifier to check that the preset is currently enabled. */ modifier whenEnabled(bytes32 presetId) { if (!_presets[presetId].enabled) revert DisabledWarperPreset(presetId); _; } /** * @dev Modifier to check that the preset is currently disabled. */ modifier whenDisabled(bytes32 presetId) { if (_presets[presetId].enabled) revert EnabledWarperPreset(presetId); _; } /** * @dev Modifier to check that the preset is registered. */ modifier presetIsRegistered(bytes32 presetId) { if (_presets[presetId].implementation == address(0)) revert WarperPresetNotRegistered(presetId); _; } /** * @dev Constructor that gets called for the implementation contract. * @custom:oz-upgrades-unsafe-allow constructor */ constructor() initializer { // solhint-disable-previous-line no-empty-blocks } /** * @dev WarperPresetFactory initializer. * @param params Initialization params. */ function initialize(WarperPresetFactoryInitParams calldata params) external initializer { __UUPSUpgradeable_init(); _aclContract = IACL(params.acl); _metahub = IMetahub(params.metahub); } /** * @inheritdoc IWarperPresetFactory */ function addPreset(bytes32 presetId, address implementation) external onlySupervisor { // Check whether provided implementation address is a contract with the correct interface. if (!implementation.supportsInterface(type(IWarperPreset).interfaceId)) { revert InvalidWarperPresetInterface(); } if (_presetIds.add(presetId)) { _presets[presetId] = WarperPreset(presetId, implementation, true); emit WarperPresetAdded(presetId, implementation); } else { revert DuplicateWarperPresetId(presetId); } } /** * @inheritdoc IWarperPresetFactory */ function removePreset(bytes32 presetId) external onlySupervisor { if (_presetIds.remove(presetId)) { delete _presets[presetId]; emit WarperPresetRemoved(presetId); } } /** * @inheritdoc IWarperPresetFactory */ function enablePreset(bytes32 presetId) external presetIsRegistered(presetId) whenDisabled(presetId) onlySupervisor { _presets[presetId].enabled = true; emit WarperPresetEnabled(presetId); } /** * @inheritdoc IWarperPresetFactory */ function disablePreset(bytes32 presetId) external presetIsRegistered(presetId) whenEnabled(presetId) onlySupervisor { _presets[presetId].enabled = false; emit WarperPresetDisabled(presetId); } /** * @inheritdoc IWarperPresetFactory */ function deployPreset(bytes32 presetId, bytes calldata initData) external whenEnabled(presetId) returns (address) { // Init data must never be empty here, because all presets have mandatory init params. if (initData.length == 0) { revert EmptyPresetData(); } // Deploy warper preset implementation proxy. address warper = _presets[presetId].implementation.clone(); // Initialize warper. warper.functionCall(initData); emit WarperPresetDeployed(presetId, warper); return warper; } /** * @inheritdoc IWarperPresetFactory */ function presetEnabled(bytes32 presetId) external view presetIsRegistered(presetId) returns (bool) { return _presets[presetId].enabled; } /** * @inheritdoc IWarperPresetFactory */ function presets() external view returns (WarperPreset[] memory) { uint256 length = _presetIds.length(); WarperPreset[] memory warperPresets = new WarperPreset[](length); for (uint256 i = 0; i < length; i++) { warperPresets[i] = _presets[_presetIds.at(i)]; } return warperPresets; } /** * @inheritdoc IWarperPresetFactory */ function preset(bytes32 presetId) external view presetIsRegistered(presetId) returns (WarperPreset memory) { return _presets[presetId]; } /** * @inheritdoc IContractEntity */ function contractKey() external pure override returns (bytes4) { return Contracts.WARPER_PRESET_FACTORY; } /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view override(ContractEntity, IERC165) returns (bool) { return interfaceId == type(IWarperPresetFactory).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc UUPSUpgradeable */ function _authorizeUpgrade(address newImplementation) internal virtual override onlyAdmin { // solhint-disable-previous-line no-empty-blocks } /** * @inheritdoc AccessControlledUpgradeable */ function _acl() internal view virtual override returns (IACL) { return _aclContract; } }