// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/utils/Multicall.sol"; import "./IListingTermsRegistry.sol"; import "../../contract-registry/ContractEntity.sol"; import "../../contract-registry/Contracts.sol"; import "../../acl/direct/AccessControlledUpgradeable.sol"; import "./ListingTermsRegistryStorage.sol"; import "../listing-manager/IListingManager.sol"; contract ListingTermsRegistry is IListingTermsRegistry, UUPSUpgradeable, ContractEntity, AccessControlledUpgradeable, ListingTermsRegistryStorage, Multicall { using CountersUpgradeable for CountersUpgradeable.Counter; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; /** * @dev ListingTermsRegistry initialization params. * @param acl ACL contract address */ struct ListingTermsRegistryInitParams { IACL acl; IMetahub metahub; } /** * @dev Modifier to make sure the function is called by the account with LISTING_WIZARD role. */ modifier onlyAuthorizedToAlterListingTerms() { IListingManager(_metahub.getContract(Contracts.LISTING_MANAGER)).checkIsListingWizard(_msgSender()); _; } /** * @dev Modifier to make a function callable only for the registered lister strategy override config. */ modifier onlyRegisteredListingTerms(uint256 listingTermsId) { checkRegisteredListingTerms(listingTermsId); _; } /** * @custom:oz-upgrades-unsafe-allow constructor */ constructor() initializer { // solhint-disable-previous-line no-empty-blocks } /** * @dev Contract initializer. * @param params Initialization params. */ function initialize(ListingTermsRegistryInitParams calldata params) external initializer { __UUPSUpgradeable_init(); _aclContract = IACL(params.acl); _metahub = IMetahub(params.metahub); } /** * @inheritdoc IListingTermsRegistry */ function registerGlobalListingTerms(uint256 listingId, ListingTerms calldata terms) external onlyAuthorizedToAlterListingTerms returns (uint256 listingTermsId) { listingTermsId = _registerListingTerms(terms); _listingTermsParams[listingTermsId] = Params(listingId, 0, address(0)); _globalListingTerms[listingId].add(listingTermsId); emit GlobalListingTermsRegistered(listingId, listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function removeGlobalListingTerms(uint256 listingId, uint256 listingTermsId) external onlyAuthorizedToAlterListingTerms { if (!_areRegisteredGlobalListingTerms(listingId, listingTermsId)) { revert GlobalListingTermsMismatch(listingId, listingTermsId); } _globalListingTerms[listingId].remove(listingTermsId); delete _listingTerms[listingTermsId]; delete _listingTermsParams[listingTermsId]; emit GlobalListingTermsRemoved(listingId, listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function registerUniverseListingTerms( uint256 listingId, uint256 universeId, ListingTerms calldata terms ) external onlyAuthorizedToAlterListingTerms returns (uint256 listingTermsId) { listingTermsId = _registerListingTerms(terms); _listingTermsParams[listingTermsId] = Params(listingId, universeId, address(0)); _universeListingTerms[listingId][universeId].add(listingTermsId); emit UniverseListingTermsRegistered(listingId, universeId, listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function removeUniverseListingTerms( uint256 listingId, uint256 universeId, uint256 listingTermsId ) external onlyAuthorizedToAlterListingTerms { if (!_areRegisteredUniverseListingTerms(listingId, universeId, listingTermsId)) { revert UniverseListingTermsMismatch(listingId, universeId, listingTermsId); } _universeListingTerms[listingId][universeId].remove(listingTermsId); delete _listingTerms[listingTermsId]; delete _listingTermsParams[listingTermsId]; emit UniverseListingTermsRemoved(listingId, universeId, listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function registerWarperListingTerms( uint256 listingId, address warperAddress, ListingTerms calldata terms ) external onlyAuthorizedToAlterListingTerms returns (uint256 listingTermsId) { listingTermsId = _registerListingTerms(terms); _listingTermsParams[listingTermsId] = Params(listingId, 0, warperAddress); _warperListingTerms[listingId][warperAddress].add(listingTermsId); emit WarperListingTermsRegistered(listingId, warperAddress, listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function removeWarperListingTerms( uint256 listingId, address warperAddress, uint256 listingTermsId ) external onlyAuthorizedToAlterListingTerms { if (!_areRegisteredWarperListingTerms(listingId, warperAddress, listingTermsId)) { revert WarperListingTermsMismatch(listingId, warperAddress, listingTermsId); } _warperListingTerms[listingId][warperAddress].remove(listingTermsId); delete _listingTerms[listingTermsId]; delete _listingTermsParams[listingTermsId]; emit WarperListingTermsRemoved(listingId, warperAddress, listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function listingTerms(uint256 listingTermsId) external view onlyRegisteredListingTerms(listingTermsId) returns (ListingTerms memory) { return _listingTerms[listingTermsId]; } /** * @inheritdoc IListingTermsRegistry */ function listingTermsWithParams(uint256 listingTermsId) external view onlyRegisteredListingTerms(listingTermsId) returns (ListingTerms memory, Params memory) { return (_listingTerms[listingTermsId], _listingTermsParams[listingTermsId]); } /** * @inheritdoc IListingTermsRegistry */ function allListingTerms( Params calldata params, uint256 offset, uint256 limit ) external view returns (uint256[] memory listingTermsIds, ListingTerms[] memory listingTermsList) { if (_warperListingTerms[params.listingId][params.warperAddress].length() > 0) { (listingTermsIds, listingTermsList) = _paginateIndexedListingTerms( _warperListingTerms[params.listingId][params.warperAddress], offset, limit ); } else if (_universeListingTerms[params.listingId][params.universeId].length() > 0) { (listingTermsIds, listingTermsList) = _paginateIndexedListingTerms( _universeListingTerms[params.listingId][params.universeId], offset, limit ); } else if (_globalListingTerms[params.listingId].length() > 0) { (listingTermsIds, listingTermsList) = _paginateIndexedListingTerms( _globalListingTerms[params.listingId], offset, limit ); } } /** * @inheritdoc IContractEntity */ function contractKey() external pure override returns (bytes4) { return Contracts.LISTING_TERMS_REGISTRY; } /** * @inheritdoc IListingTermsRegistry */ function areRegisteredListingTerms(uint256 listingTermsId) public view returns (bool) { return _listingTerms[listingTermsId].strategyId.length > 0 && _listingTerms[listingTermsId].strategyData.length > 0; } /** * @inheritdoc IListingTermsRegistry */ function areRegisteredListingTermsWithParams(uint256 listingTermsId, Params calldata params) public view returns (bool existance) { if (_warperListingTerms[params.listingId][params.warperAddress].contains(listingTermsId)) { return true; } else if (_universeListingTerms[params.listingId][params.universeId].contains(listingTermsId)) { return _areRegisteredUniverseListingTermsWithParams(params); } else if (_globalListingTerms[params.listingId].contains(listingTermsId)) { return _areRegisteredGlobalListingTermsWithParams(params); } } /** * @inheritdoc IListingTermsRegistry */ function checkRegisteredListingTerms(uint256 listingTermsId) public view { if (!areRegisteredListingTerms(listingTermsId)) revert UnregisteredListingTerms(listingTermsId); } /** * @inheritdoc IListingTermsRegistry */ function checkRegisteredListingTermsWithParams(uint256 listingTermsId, Params calldata params) public view { if (!areRegisteredListingTermsWithParams(listingTermsId, params)) { revert WrongListingTermsIdForParams( listingTermsId, params.listingId, params.universeId, params.warperAddress ); } } /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view override(ContractEntity, IERC165) returns (bool) { return interfaceId == type(IListingTermsRegistry).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc UUPSUpgradeable */ function _authorizeUpgrade(address newImplementation) internal override onlyAdmin { // solhint-disable-previous-line no-empty-blocks } /** * @dev Registers new listing terms. * @param terms The listing terms. * @return listingTermsId Listing terms ID. */ function _registerListingTerms(ListingTerms calldata terms) internal returns (uint256 listingTermsId) { // Generetion of new listing terms ID _listingTermsIdTracker.increment(); listingTermsId = _listingTermsIdTracker.current(); // storing new listing terms data _listingTerms[listingTermsId] = terms; emit ListingTermsRegistered(listingTermsId, terms.strategyId, terms.strategyData); } /** * @dev Returns all Listing Terms for Listing ID. * @param listingTermsIndex Listing terms index EnumerableSetUpgradeable.UintSet. * @param offset List offset value. * @param limit List limit value. * @return List of all Listing Terms. */ function _paginateIndexedListingTerms( EnumerableSetUpgradeable.UintSet storage listingTermsIndex, uint256 offset, uint256 limit ) internal view returns (uint256[] memory, ListingTerms[] memory) { uint256 indexSize = listingTermsIndex.length(); if (offset >= indexSize) return (new uint256[](0), new ListingTerms[](0)); if (limit > indexSize - offset) { limit = indexSize - offset; } ListingTerms[] memory listingTermsList = new ListingTerms[](limit); uint256[] memory listingTermsIds = new uint256[](limit); for (uint256 i = 0; i < limit; i++) { listingTermsIds[i] = listingTermsIndex.at(offset + i); listingTermsList[i] = _listingTerms[listingTermsIds[i]]; } return (listingTermsIds, listingTermsList); } /** * @dev Checks registration of global listing terms . * @param listingId Listing ID. * @param listingTermsId Listing terms ID. * @return Boolean that is positive in case of existance */ function _areRegisteredGlobalListingTerms(uint256 listingId, uint256 listingTermsId) internal view returns (bool) { return _globalListingTerms[listingId].contains(listingTermsId); } /** * @dev Checks registration of Listing terms for universe. * @param params Listing terms specific params. * @return globalListingTermsExistance Boolean that is positive in case of existance */ function _areRegisteredGlobalListingTermsWithParams(Params calldata params) internal view returns (bool globalListingTermsExistance) { if ( _warperListingTerms[params.listingId][params.warperAddress].length() == 0 && _universeListingTerms[params.listingId][params.universeId].length() == 0 && _globalListingTerms[params.listingId].length() > 0 ) { globalListingTermsExistance = true; } } /** * @dev Checks registration of Listing terms for universe. * @param listingId Listing ID. * @param universeId Universe ID. * @param listingTermsId Listing terms ID. * @return Boolean that is positive in case of existance */ function _areRegisteredUniverseListingTerms( uint256 listingId, uint256 universeId, uint256 listingTermsId ) internal view returns (bool) { return _universeListingTerms[listingId][universeId].contains(listingTermsId); } /** * @dev Checks registration of Listing terms for universe. * @param params Listing terms specific params. * @return universeListingTermsExistance Boolean that is positive in case of existance */ function _areRegisteredUniverseListingTermsWithParams(Params calldata params) internal view returns (bool universeListingTermsExistance) { if ( _warperListingTerms[params.listingId][params.warperAddress].length() == 0 && _universeListingTerms[params.listingId][params.universeId].length() > 0 ) { universeListingTermsExistance = true; } } /** * @dev Checks registration of Listing terms for warper. * @param listingId Listing ID. * @param warperAddress Address of the Warper. * @param listingTermsId Listing terms ID. * @return Boolean that is positive in case of existance */ function _areRegisteredWarperListingTerms( uint256 listingId, address warperAddress, uint256 listingTermsId ) internal view returns (bool) { return _warperListingTerms[listingId][warperAddress].contains(listingTermsId); } /** * @inheritdoc AccessControlledUpgradeable */ function _acl() internal view override returns (IACL) { return _aclContract; } }