// 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 "./ITaxStrategyRegistry.sol"; import "../../contract-registry/ContractEntity.sol"; import "../../contract-registry/Contracts.sol"; import "../../acl/direct/AccessControlledUpgradeable.sol"; import "../tax-strategies/ITaxController.sol"; import "./TaxStrategyRegistryStorage.sol"; contract TaxStrategyRegistry is ITaxStrategyRegistry, UUPSUpgradeable, ContractEntity, AccessControlledUpgradeable, TaxStrategyRegistryStorage { using ERC165CheckerUpgradeable for address; /** * @dev ListingStrategyRegistry initialization params. * @param acl ACL contract address. * @param taxStrategyRegistry Tax strategy registry contract address. */ struct TaxStrategyRegistryInitParams { IACL acl; IMetahub metahub; } /** * @dev Modifier to make a function callable only for the registered listing tax strategy. */ modifier onlyRegisteredListingTaxStrategy(bytes4 listingTaxStrategyId) { checkRegisteredTaxStrategy(listingTaxStrategyId); _; } /** * @custom:oz-upgrades-unsafe-allow constructor */ constructor() initializer { // solhint-disable-previous-line no-empty-blocks } /** * @dev Contract initializer. * @param params Tax Strategy Registry initialization params. */ function initialize(TaxStrategyRegistryInitParams calldata params) external initializer { __UUPSUpgradeable_init(); _aclContract = IACL(params.acl); _metahub = IMetahub(params.metahub); } /** * @inheritdoc ITaxStrategyRegistry */ function registerTaxStrategy(bytes4 taxStrategyId, TaxStrategyConfig calldata config) external onlyAdmin { _checkValidTaxController(taxStrategyId, config.controller); if (isRegisteredTaxStrategy(taxStrategyId)) { revert TaxStrategyIsAlreadyRegistered(taxStrategyId); } _taxStrategies[taxStrategyId] = config; emit TaxStrategyRegistered(taxStrategyId, config.controller); } /** * @inheritdoc ITaxStrategyRegistry */ function setTaxController(bytes4 taxStrategyId, address controller) external onlySupervisor onlyRegisteredListingTaxStrategy(taxStrategyId) { _checkValidTaxController(taxStrategyId, controller); _taxStrategies[taxStrategyId].controller = controller; emit TaxStrategyControllerChanged(taxStrategyId, controller); } /** * @inheritdoc ITaxStrategyRegistry */ function taxController(bytes4 taxStrategyId) external view onlyRegisteredListingTaxStrategy(taxStrategyId) returns (address) { return _taxStrategies[taxStrategyId].controller; } /** * @inheritdoc ITaxStrategyRegistry */ function taxStrategy(bytes4 taxStrategyId) external view onlyRegisteredListingTaxStrategy(taxStrategyId) returns (TaxStrategyConfig memory) { return _taxStrategies[taxStrategyId]; } /** * @inheritdoc IContractEntity */ function contractKey() external pure override returns (bytes4) { return Contracts.TAX_STRATEGY_REGISTRY; } /** * @inheritdoc ITaxStrategyRegistry */ function isRegisteredTaxStrategy(bytes4 taxStrategyId) public view returns (bool) { return _taxStrategies[taxStrategyId].controller != address(0); } /** * @inheritdoc ITaxStrategyRegistry */ function checkRegisteredTaxStrategy(bytes4 taxStrategyId) public view { if (!isRegisteredTaxStrategy(taxStrategyId)) { revert UnregisteredTaxStrategy(taxStrategyId); } } /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view override(ContractEntity, IERC165) returns (bool) { return interfaceId == type(ITaxStrategyRegistry).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc UUPSUpgradeable */ function _authorizeUpgrade(address newImplementation) internal override onlyAdmin { // solhint-disable-previous-line no-empty-blocks } /** * @dev Reverts if provided address is not a valid tax controller. * @param taxStrategyId Tax strategy ID. * @param controller Tax controller address. */ function _checkValidTaxController(bytes4 taxStrategyId, address controller) internal view { if (!controller.supportsInterface(type(ITaxController).interfaceId)) revert InvalidTaxControllerInterface(); bytes4 contractTaxStrategyId = ITaxController(controller).strategyId(); if (contractTaxStrategyId != taxStrategyId) { revert TaxStrategyMismatch(contractTaxStrategyId, taxStrategyId); } } /** * @inheritdoc AccessControlledUpgradeable */ function _acl() internal view override returns (IACL) { return _aclContract; } }