// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import "./IListingConfiguratorRegistry.sol"; import "../../../contract-registry/ContractEntity.sol"; import "./ListingConfiguratorRegistryStorage.sol"; import "../../../acl/direct/AccessControlledUpgradeable.sol"; import "../../../acl/delegated/DelegatedAccessControlEnumerable.sol"; import "../IListingConfigurator.sol"; contract ListingConfiguratorRegistry is Initializable, IListingConfiguratorRegistry, UUPSUpgradeable, ContractEntity, AccessControlledUpgradeable, DelegatedAccessControlEnumerable, ListingConfiguratorRegistryStorage { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; using AddressUpgradeable for address; using ERC165CheckerUpgradeable for address; /** * @dev ListingConfiguratorRegistry initialization params. * @param acl ACL contract address. * @param metahub Metahub contract address. * @param listingConfiguratorController ListingConfiguratorController contract address. */ struct ListingConfiguratorRegistryInitParams { IACL acl; IMetahub metahub; IListingConfiguratorController listingConfiguratorController; } /** * @dev Thrown if provided listing configurator address does not implement IListingConfigurator interface. */ error InvalidListingConfiguratorInterface(); /** * @dev Thrown upon attempting to register a listing configurator twice. * @param listingConfigurator Duplicate listing configurator address. */ error ListingConfiguratorIsAlreadyRegistered(address listingConfigurator); /** * @dev Emitted when a new listing configurator is registered. * @param listingConfigurator Listing configurator address */ event ListingConfiguratorRegistered( address indexed listingConfigurator, address indexed listingConfiguratorController, address indexed admin ); /** * @dev Constructor that gets called for the implementation contract. * @custom:oz-upgrades-unsafe-allow constructor */ constructor() { _disableInitializers(); } function initialize(ListingConfiguratorRegistryInitParams calldata params) external initializer { __UUPSUpgradeable_init(); __DelegatedAccessControlEnumerable_init(); if (address(params.acl) == address(0)) revert InvalidZeroAddress(); _aclContract = params.acl; _setController(address(params.listingConfiguratorController)); _metahub = IMetahub(params.metahub); } /** * @inheritdoc IListingConfiguratorRegistry */ function registerListingConfigurator(address listingConfigurator, address admin) external override { if ( !listingConfigurator.isContract() || !listingConfigurator.supportsInterface(type(IListingConfigurator).interfaceId) ) { revert InvalidListingConfiguratorInterface(); } if (!_configurators.add(listingConfigurator)) { revert ListingConfiguratorIsAlreadyRegistered(listingConfigurator); } _grantRole(listingConfigurator, DELEGATED_ADMIN, admin); _configuratorControllers[listingConfigurator] = _controller; emit ListingConfiguratorRegistered(listingConfigurator, address(_controller), admin); } function setController(address controller) external onlyAdmin { _setController(controller); } function getController(address listingConfigurator) external view returns (IListingConfiguratorController) { if (!_configurators.contains(listingConfigurator)) { revert ListingConfiguratorNotRegistered(listingConfigurator); } return _configuratorControllers[listingConfigurator]; } /** * @inheritdoc IContractEntity */ function contractKey() external pure override returns (bytes4) { return Contracts.LISTING_CONFIGURATOR_REGISTRY; } /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view override(ContractEntity, DelegatedAccessControlEnumerable, IERC165) returns (bool) { return interfaceId == type(IListingConfiguratorRegistry).interfaceId || ContractEntity.supportsInterface(interfaceId) || super.supportsInterface(interfaceId); } function _setController(address controller) internal { _validateListingConfiguratorController(controller); emit ListingConfiguratorControllerChanged(address(_controller), controller); _controller = IListingConfiguratorController(controller); } function _grantRole( address delegate, string memory role, address account ) internal override { if (!_configurators.contains(delegate)) { revert CannotGrantRoleForUnregisteredController(delegate); } super._grantRole(delegate, role, account); } function _authorizeUpgrade(address newImplementation) internal override onlyAdmin { // solhint-disable-previous-line no-empty-blocks } /** * @dev Checks that provided listing configurator controller address is a valid contract. */ function _validateListingConfiguratorController(address controller) internal view { if ( !controller.isContract() || !controller.supportsInterface(type(IListingConfiguratorController).interfaceId) ) { revert InvalidListingConfiguratorController(controller); } } function _acl() internal view virtual override returns (IACL) { return _aclContract; } }