// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; // modules import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { AccessControlExtendedInitAbstract } from "../AccessControlExtended/AccessControlExtendedInitAbstract.sol"; import { LSP7DigitalAssetInitAbstract } from "../../LSP7DigitalAssetInitAbstract.sol"; // interfaces import {ILSP7Mintable} from "./ILSP7Mintable.sol"; // errors import {LSP7MintDisabled} from "./LSP7MintableErrors.sol"; /// @title LSP7MintableInitAbstract /// @dev Abstract contract implementing a mintable LSP7 token extension, allowing any address granted the `MINTER_ROLE` to mint new tokens until minting is disabled. /// Inherits from LSP7DigitalAsset to provide core token functionality. abstract contract LSP7MintableInitAbstract is ILSP7Mintable, LSP7DigitalAssetInitAbstract, AccessControlExtendedInitAbstract { /// @notice Indicates whether minting is currently enabled or not. bool public isMintable; /// @dev keccak256("MINTER_ROLE") bytes32 public constant MINTER_ROLE = 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6; /// @notice Initializes the LSP7Mintable contract with base token params and minting status. /// @dev Initializes the LSP7DigitalAsset base and sets the minting status. /// @param name_ The name of the token. /// @param symbol_ The symbol of the token. /// @param newOwner_ The owner of the contract. /// @param lsp4TokenType_ The token type (see LSP4). /// @param isNonDivisible_ Whether the token is non-divisible. /// @param mintable_ True to enable minting after deployment, false to disable it forever. /// @custom:info If `mintable_` is set to `true` then it can be disabled using `disableMinting()` function later on. function __LSP7Mintable_init( string memory name_, string memory symbol_, address newOwner_, uint256 lsp4TokenType_, bool isNonDivisible_, bool mintable_ ) internal virtual onlyInitializing { LSP7DigitalAssetInitAbstract._initialize( name_, symbol_, newOwner_, lsp4TokenType_, isNonDivisible_ ); __AccessControlExtended_init(); __LSP7Mintable_init_unchained(mintable_); } /// @notice Unchained initializer for the minting status. /// @dev Sets the initial minting status. /// @param mintable_ True to enable minting after deployment, false to disable it forever. function __LSP7Mintable_init_unchained( bool mintable_ ) internal virtual onlyInitializing { isMintable = mintable_; emit MintingStatusChanged({enabled: mintable_}); if (mintable_) { _grantRole(MINTER_ROLE, owner()); } } /// @inheritdoc ILSP7Mintable /// @custom:warning Once this function is called, any address holding the `MINTER_ROLE` will be inoperable. /// @custom:info The list of addresses holding the `MINTER_ROLE` remains populated after the minting feature is switched off. function disableMinting() public virtual override onlyOwner { require(isMintable, LSP7MintDisabled()); isMintable = false; emit MintingStatusChanged({enabled: false}); } /// @inheritdoc ILSP7Mintable function mint( address to, uint256 amount, bool force, bytes memory data ) public virtual override onlyRole(MINTER_ROLE) { _mint(to, amount, force, data); } function supportsInterface( bytes4 interfaceId ) public view virtual override( AccessControlExtendedInitAbstract, LSP7DigitalAssetInitAbstract ) returns (bool) { return AccessControlExtendedInitAbstract.supportsInterface(interfaceId) || LSP7DigitalAssetInitAbstract.supportsInterface(interfaceId); } /// @notice Internal function to mint tokens, overridden to enforce minting status. /// @dev Checks if minting is enabled, reverting with LSP7MintDisabled if not. Calls the parent _mint function from LSP7DigitalAsset. /// @param to The address to receive the minted tokens. /// @param amount The number of tokens to mint. /// @param force When true, allows minting to any address; when false, requires `to` to support LSP1 UniversalReceiver. /// @param data Additional data included in the Transfer event and sent to `to`’s UniversalReceiver hook, if applicable. /// /// @custom:warning This internal function does not check for `MINTER_ROLE` access control. /// Derived contracts that expose this function publicly and want to gate it by `MINTER_ROLE` must enforce it with `onlyRole(MINTER_ROLE)`. function _mint( address to, uint256 amount, bool force, bytes memory data ) internal virtual override { require(isMintable, LSP7MintDisabled()); super._mint(to, amount, force, data); } function _transferOwnership( address newOwner ) internal virtual override(AccessControlExtendedInitAbstract, OwnableUpgradeable) { // restore default admin hierarchy so a previously-installed custom admin // cannot grant MINTER_ROLE to new accounts post-transfer _setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE); super._transferOwnership(newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps * * @custom:info The size of the `__gap` array is calculated so that the amount of storage used by the contract * always adds up to the same number (in this case 50 storage slots). */ uint256[49] private __gap; }