// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "../../../contract-registry/ContractEntity.sol"; import "../../../acl/direct/AccessControlledUpgradeable.sol"; import "./ListingConfiguratorPresetFactoryStorage.sol"; import "../IListingConfigurator.sol"; contract ListingConfiguratorPresetFactory is IListingConfiguratorPresetFactory, UUPSUpgradeable, ContractEntity, AccessControlledUpgradeable, ListingConfiguratorPresetFactoryStorage { using ClonesUpgradeable for address; using AddressUpgradeable for address; using ERC165CheckerUpgradeable for address; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set; error InvalidZeroAddress(); /** * @dev ListingConfiguratorPresetFactory initialization params. * @param listingConfiguratorController ListingConfiguratorController contract address * @param acl */ struct ListingConfiguratorPresetFactoryInitParams { IListingConfiguratorRegistry listingConfiguratorRegistry; IACL acl; } /** * @dev Modifier to check that the preset is currently enabled. */ modifier whenEnabled(bytes32 presetId) { if (!_presets[presetId].enabled) revert DisabledListingConfiguratorPreset(presetId); _; } /** * @dev Modifier to check that the preset is currently disabled. */ modifier whenDisabled(bytes32 presetId) { if (_presets[presetId].enabled) revert EnabledListingConfiguratorPreset(presetId); _; } /** * @dev Modifier to check that the preset is registered. */ modifier whenRegistered(bytes32 presetId) { if (_presets[presetId].implementation == address(0)) revert ListingConfiguratorPresetNotRegistered(presetId); _; } /** * @dev Constructor that gets called for the implementation contract. * @custom:oz-upgrades-unsafe-allow constructor */ constructor() { _disableInitializers(); } /** * @dev ListingConfiguratorPresetFactory initializer. * @param initParams initialization parameters */ function initialize(ListingConfiguratorPresetFactoryInitParams calldata initParams) external initializer { __UUPSUpgradeable_init(); if (address(initParams.acl) == address(0)) revert InvalidZeroAddress(); if (address(initParams.listingConfiguratorRegistry) == address(0)) revert InvalidZeroAddress(); _aclContract = initParams.acl; _registry = initParams.listingConfiguratorRegistry; } /** * @inheritdoc IListingConfiguratorPresetFactory */ function addPreset(bytes32 presetId, address implementation) external onlySupervisor { // Check whether provided implementation address is a contract with the correct interface. if (!implementation.supportsInterface(type(IListingConfigurator).interfaceId)) { revert InvalidListingConfiguratorInterface(); } if (!_presetIds.add(presetId)) { revert DuplicateListingConfiguratorPresetId(presetId); } _presets[presetId] = ListingConfiguratorPreset(presetId, implementation, true); emit ListingConfiguratorPresetAdded(presetId, implementation); } /** * @inheritdoc IListingConfiguratorPresetFactory */ function removePreset(bytes32 presetId) external onlySupervisor { if (!_presetIds.remove(presetId)) return; delete _presets[presetId]; emit ListingConfiguratorPresetRemoved(presetId); } /** * @inheritdoc IListingConfiguratorPresetFactory */ function enablePreset(bytes32 presetId) external whenRegistered(presetId) whenDisabled(presetId) onlySupervisor { _presets[presetId].enabled = true; emit ListingConfiguratorPresetEnabled(presetId); } /** * @inheritdoc IListingConfiguratorPresetFactory */ function disablePreset(bytes32 presetId) external whenRegistered(presetId) whenEnabled(presetId) onlySupervisor { _presets[presetId].enabled = false; emit ListingConfiguratorPresetDisabled(presetId); } /** * @inheritdoc IListingConfiguratorPresetFactory */ 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 listing configurator preset implementation proxy. address configurator = _presets[presetId].implementation.clone(); configurator.functionCall(initData); _registry.registerListingConfigurator(configurator, _msgSender()); emit ListingConfiguratorPresetDeployed(presetId, configurator); return configurator; } /** * @inheritdoc IListingConfiguratorPresetFactory */ function presetEnabled(bytes32 presetId) external view whenRegistered(presetId) returns (bool) { return _presets[presetId].enabled; } /** * @inheritdoc IListingConfiguratorPresetFactory */ function presets() external view returns (ListingConfiguratorPreset[] memory configuratorPresets) { uint256 length = _presetIds.length(); configuratorPresets = new ListingConfiguratorPreset[](length); for (uint256 i = 0; i < length; i++) { configuratorPresets[i] = _presets[_presetIds.at(i)]; } } /** * @inheritdoc IListingConfiguratorPresetFactory */ function preset(bytes32 presetId) external view whenRegistered(presetId) returns (ListingConfiguratorPreset memory) { return _presets[presetId]; } /** * @inheritdoc IContractEntity */ function contractKey() external pure override returns (bytes4) { return Contracts.LISTING_CONFIGURATOR_PRESET_FACTORY; } /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view override(ContractEntity, IERC165) returns (bool) { return interfaceId == type(IListingConfiguratorPresetFactory).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; } }