// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @rmrk-team/evm-contracts/contracts/RMRK/access/Ownable.sol



// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.21;

import "@openzeppelin/contracts/utils/Context.sol";
import "../library/RMRKErrors.sol";

/**
 * @title Ownable
 * @author RMRK team
 * @notice A minimal ownable smart contractf or owner and contributors.
 * @dev This smart contract is based on "openzeppelin's access/Ownable.sol".
 */
contract Ownable is Context {
    address private _owner;
    mapping(address => uint256) private _contributors;

    /**
     * @notice Used to anounce the transfer of ownership.
     * @param previousOwner Address of the account that transferred their ownership role
     * @param newOwner Address of the account receiving the ownership role
     */
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @notice Event that signifies that an address was granted contributor role or that the permission has been
     *  revoked.
     * @dev This can only be triggered by a current owner, so there is no need to include that information in the event.
     * @param contributor Address of the account that had contributor role status updated
     * @param isContributor A boolean value signifying whether the role has been granted (`true`) or revoked (`false`)
     */
    event ContributorUpdate(address indexed contributor, bool isContributor);

    /**
     * @dev Reverts if called by any account other than the owner or an approved contributor.
     */
    modifier onlyOwnerOrContributor() {
        _onlyOwnerOrContributor();
        _;
    }

    /**
     * @dev Reverts if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _onlyOwner();
        _;
    }

    /**
     * @dev Initializes the contract by setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @notice Returns the address of the current owner.
     * @return Address of the current owner
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @notice Leaves the contract without owner. Functions using the `onlyOwner` modifier will be disabled.
     * @dev Can only be called by the current owner.
     * @dev Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is
     *  only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @notice Transfers ownership of the contract to a new owner.
     * @dev Can only be called by the current owner.
     * @param newOwner Address of the new owner's account
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) revert RMRKNewOwnerIsZeroAddress();
        _transferOwnership(newOwner);
    }

    /**
     * @notice Transfers ownership of the contract to a new owner.
     * @dev Internal function without access restriction.
     * @dev Emits ***OwnershipTransferred*** event.
     * @param newOwner Address of the new owner's account
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @notice Adds or removes a contributor to the smart contract.
     * @dev Can only be called by the owner.
     * @dev Emits ***ContributorUpdate*** event.
     * @param contributor Address of the contributor's account
     * @param grantRole A boolean value signifying whether the contributor role is being granted (`true`) or revoked
     *  (`false`)
     */
    function manageContributor(
        address contributor,
        bool grantRole
    ) external onlyOwner {
        if (contributor == address(0)) revert RMRKNewContributorIsZeroAddress();
        grantRole
            ? _contributors[contributor] = 1
            : _contributors[contributor] = 0;
        emit ContributorUpdate(contributor, grantRole);
    }

    /**
     * @notice Used to check if the address is one of the contributors.
     * @param contributor Address of the contributor whose status we are checking
     * @return Boolean value indicating whether the address is a contributor or not
     */
    function isContributor(address contributor) public view returns (bool) {
        return _contributors[contributor] == 1;
    }

    /**
     * @notice Used to verify that the caller is either the owner or a contributor.
     * @dev If the caller is not the owner or a contributor, the execution will be reverted.
     */
    function _onlyOwnerOrContributor() private view {
        if (owner() != _msgSender() && !isContributor(_msgSender()))
            revert RMRKNotOwnerOrContributor();
    }

    /**
     * @notice Used to verify that the caller is the owner.
     * @dev If the caller is not the owner, the execution will be reverted.
     */
    function _onlyOwner() private view {
        if (owner() != _msgSender()) revert RMRKNotOwner();
    }
}

// File: @rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol



// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.21;

/// @title RMRKErrors
/// @author RMRK team
/// @notice A collection of errors used in the RMRK suite
/// @dev Errors are kept in a centralised file in order to provide a central point of reference and to avoid error
///  naming collisions due to inheritance

/// Attempting to grant the token to 0x0 address
error ERC721AddressZeroIsNotaValidOwner();
/// Attempting to grant approval to the current owner of the token
error ERC721ApprovalToCurrentOwner();
/// Attempting to grant approval when not being owner or approved for all should not be permitted
error ERC721ApproveCallerIsNotOwnerNorApprovedForAll();
/// Attempting to get approvals for a token owned by 0x0 (considered non-existent)
error ERC721ApprovedQueryForNonexistentToken();
/// Attempting to grant approval to self
error ERC721ApproveToCaller();
/// Attempting to use an invalid token ID
error ERC721InvalidTokenId();
/// Attempting to mint to 0x0 address
error ERC721MintToTheZeroAddress();
/// Attempting to manage a token without being its owner or approved by the owner
error ERC721NotApprovedOrOwner();
/// Attempting to mint an already minted token
error ERC721TokenAlreadyMinted();
/// Attempting to transfer the token from an address that is not the owner
error ERC721TransferFromIncorrectOwner();
/// Attempting to safe transfer to an address that is unable to receive the token
error ERC721TransferToNonReceiverImplementer();
/// Attempting to transfer the token to a 0x0 address
error ERC721TransferToTheZeroAddress();
/// Attempting to grant approval of assets to their current owner
error RMRKApprovalForAssetsToCurrentOwner();
/// Attempting to grant approval of assets without being the caller or approved for all
error RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll();
/// Attempting to incorrectly configue a Catalog item
error RMRKBadConfig();
/// Attempting to set the priorities with an array of length that doesn't match the length of active assets array
error RMRKBadPriorityListLength();
/// Attempting to add an asset entry with `Part`s, without setting the `Catalog` address
error RMRKCatalogRequiredForParts();
/// Attempting to transfer a soulbound (non-transferrable) token
error RMRKCannotTransferSoulbound();
/// Attempting to accept a child that has already been accepted
error RMRKChildAlreadyExists();
/// Attempting to interact with a child, using index that is higher than the number of children
error RMRKChildIndexOutOfRange();
/// Attempting to find the index of a child token on a parent which does not own it.
error RMRKChildNotFoundInParent();
/// Attempting to pass collaborator address array and collaborator permission array of different lengths
error RMRKCollaboratorArraysNotEqualLength();
/// Attempting to register a collection that is already registered
error RMRKCollectionAlreadyRegistered();
/// Attempting to manage or interact with colleciton that is not registered
error RMRKCollectionNotRegistered();
/// Attempting to equip a `Part` with a child not approved by the Catalog
error RMRKEquippableEquipNotAllowedByCatalog();
/// Attempting to pass an epired ECDSA deadline
error RMRKExpiredDeadline();
/// Attempting to use ID 0, which is not supported
/// @dev The ID 0 in RMRK suite is reserved for empty values. Guarding against its use ensures the expected operation
error RMRKIdZeroForbidden();
/// Attempting to interact with an asset, using index greater than number of assets
error RMRKIndexOutOfRange();
/// Attempting to reclaim a child that can't be reclaimed
error RMRKInvalidChildReclaim();
/// Attempting to use and invalid ECDSA signature
error RMRKInvalidSignature();
/// Attempting to interact with an end-user account when the contract account is expected
error RMRKIsNotContract();
/// Attempting to interact with a contract that had its operation locked
error RMRKLocked();
/// Attempting to add a pending child after the number of pending children has reached the limit (default limit is 128)
error RMRKMaxPendingChildrenReached();
/// Attempting to add a pending asset after the number of pending assets has reached the limit (default limit is
///  128)
error RMRKMaxPendingAssetsReached();
/// Attempting to burn a total number of recursive children higher than maximum set
/// @param childContract Address of the collection smart contract in which the maximum number of recursive burns was reached
/// @param childId ID of the child token at which the maximum number of recursive burns was reached
error RMRKMaxRecursiveBurnsReached(address childContract, uint256 childId);
/// Attempting to mint a number of tokens that would cause the total supply to be greater than maximum supply
error RMRKMintOverMax();
/// Attempting to mint a nested token to a smart contract that doesn't support nesting
error RMRKMintToNonRMRKNestableImplementer();
/// Attempting to mint zero tokens
error RMRKMintZero();
/// Attempting to pass complementary arrays of different lengths
error RMRKMismachedArrayLength();
/// Attempting to transfer a child before it is unequipped
error RMRKMustUnequipFirst();
/// Attempting to nest a child over the nestable limit (current limit is 100 levels of nesting)
error RMRKNestableTooDeep();
/// Attempting to nest the token to own descendant, which would create a loop and leave the looped tokens in limbo
error RMRKNestableTransferToDescendant();
/// Attempting to nest the token to a smart contract that doesn't support nesting
error RMRKNestableTransferToNonRMRKNestableImplementer();
/// Attempting to nest the token into itself
error RMRKNestableTransferToSelf();
/// Attempting to interact with an asset that can not be found
error RMRKNoAssetMatchingId();
/// Attempting to manage an asset without owning it or having been granted permission by the owner to do so
error RMRKNotApprovedForAssetsOrOwner();
/// Attempting to interact with a token without being its owner or having been granted permission by the
///  owner to do so
/// @dev When a token is nested, only the direct owner (NFT parent) can mange it. In that case, approved addresses are
///  not allowed to manage it, in order to ensure the expected behaviour
error RMRKNotApprovedOrDirectOwner();
/// Attempting to manage a collection without being the collection's collaborator
error RMRKNotCollectionCollaborator();
/// Attemting to manage a collection without being the collection's issuer
error RMRKNotCollectionIssuer();
/// Attempting to manage a collection without being the collection's issuer or collaborator
error RMRKNotCollectionIssuerOrCollaborator();
/// Attempting to compose an asset wihtout having an associated Catalog
error RMRKNotComposableAsset();
/// Attempting to unequip an item that isn't equipped
error RMRKNotEquipped();
/// Attempting to interact with a management function without being the smart contract's owner
error RMRKNotOwner();
/// Attempting to interact with a function without being the owner or contributor of the collection
error RMRKNotOwnerOrContributor();
/// Attempting to manage a collection without being the specific address
error RMRKNotSpecificAddress();
/// Attempting to manage a token without being its owner
error RMRKNotTokenOwner();
/// Attempting to transfer the ownership to the 0x0 address
error RMRKNewOwnerIsZeroAddress();
/// Attempting to assign a 0x0 address as a contributor
error RMRKNewContributorIsZeroAddress();
/// Attemtping to use `Ownable` interface without implementing it
error RMRKOwnableNotImplemented();
/// Attempting an operation requiring the token being nested, while it is not
error RMRKParentIsNotNFT();
/// Attempting to add a `Part` with an ID that is already used
error RMRKPartAlreadyExists();
/// Attempting to use a `Part` that doesn't exist
error RMRKPartDoesNotExist();
/// Attempting to use a `Part` that is `Fixed` when `Slot` kind of `Part` should be used
error RMRKPartIsNotSlot();
/// Attempting to interact with a pending child using an index greater than the size of pending array
error RMRKPendingChildIndexOutOfRange();
/// Attempting to add an asset using an ID that has already been used
error RMRKAssetAlreadyExists();
/// Attempting to equip an item into a slot that already has an item equipped
error RMRKSlotAlreadyUsed();
/// Attempting to equip an item into a `Slot` that the target asset does not implement
error RMRKTargetAssetCannotReceiveSlot();
/// Attempting to equip a child into a `Slot` and parent that the child's collection doesn't support
error RMRKTokenCannotBeEquippedWithAssetIntoSlot();
/// Attempting to compose a NFT of a token without active assets
error RMRKTokenDoesNotHaveAsset();
/// Attempting to determine the asset with the top priority on a token without assets
error RMRKTokenHasNoAssets();
/// Attempting to accept or transfer a child which does not match the one at the specified index
error RMRKUnexpectedChildId();
/// Attempting to reject all pending assets but more assets than expected are pending
error RMRKUnexpectedNumberOfAssets();
/// Attempting to reject all pending children but children assets than expected are pending
error RMRKUnexpectedNumberOfChildren();
/// Attempting to accept or reject an asset which does not match the one at the specified index
error RMRKUnexpectedAssetId();
/// Attempting an operation expecting a parent to the token which is not the actual one
error RMRKUnexpectedParent();
/// Attempting not to pass an empty array of equippable addresses when adding or setting the equippable addresses
error RMRKZeroLengthIdsPassed();
/// Attempting to set the royalties to a value higher than 100% (10000 in basis points)
error RMRKRoyaltiesTooHigh();
/// Attempting to do a bulk operation on a token that is not owned by the caller
error RMRKCanOnlyDoBulkOperationsOnOwnedTokens();
/// Attempting to do a bulk operation with multiple tokens at a time
error RMRKCanOnlyDoBulkOperationsWithOneTokenAtATime();
/// Attempting to pay with native token with a value different than expected
error RMRKWrongValueSent();

// File: contracts/interfaces/IRMRKDeployer.sol



//SPDX-License-Identifier: Apache 2.0

pragma solidity ^0.8.21;

/**
 * @title RMRK Deployer Interface
 * @notice This is interface is for an intermediary contract whose only purpose is to deploy Wrapped Collections.
 * @dev This contract does not have any validation, it is kept the minimal possible to avoid breaking the size limit.
 */
interface IRMRKDeployer {
    /**
     * @notice Deploys a new collection.
     * @param name Name of the token collection
     * @param symbol Symbol of the token collection
     * @param collectionMetadata CID of the collection metadata
     * @param maxSupply The maximum supply of tokens
     * @param royaltyRecipient Recipient of resale royalties
     * @param royaltyPercentageBps The percentage to be paid from the sale of the token expressed in basis points
     * @param initialAssetsMetadata Array with metadata of the initial assets which will be added into every minted token
     */
    function deployCollection(
        string memory name,
        string memory symbol,
        string memory collectionMetadata,
        uint256 maxSupply,
        address collectionOwner,
        address royaltyRecipient,
        uint16 royaltyPercentageBps,
        string[] memory initialAssetsMetadata
    ) external returns (address newCollection);
}

// File: contracts/interfaces/IRMRKMintaurRegistry.sol



//SPDX-License-Identifier: Apache 2.0

pragma solidity ^0.8.21;

/**
 * @title RMRK Mint'aur Registry Interface
 * @notice
 */
interface IRMRKMintaurRegistry {
    function mint(
        address collection,
        address to,
        uint256 numToMint
    ) external payable;

    function storeNewCollection(
        address collection,
        uint256 mintPrice,
        uint256 mintFee,
        address beneficiary,
        uint256 cutOffDate
    ) external;
}

// File: contracts/interfaces/IRMRKRegistry.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.21;

interface IRMRKRegistry {
    enum LegoCombination {
        None,
        MultiAsset,
        Nestable,
        NestableMultiAsset,
        Equippable,
        ERC721,
        ERC1155,
        Custom
    }

    enum MintingType {
        None,
        RMRKPreMint,
        RMRKLazyMintNativeToken,
        RMRKLazyMintERC20,
        Custom
    }

    struct CollectionConfig {
        bool usesOwnable;
        bool usesAccessControl;
        bool usesRMRKContributor;
        bool usesRMRKMintingUtils;
        bool usesRMRKLockable;
        bool hasStandardAssetManagement; // has addAssetEntry, addEquippableAssetEntry, addAssetToToken, etc
        bool hasStandardMinting; // has mint(address to, uint256 numToMint)
        bool hasStandardNestMinting; // has nestMint(address to, uint256 numToMint, uint256 destinationId)
        bool autoAcceptsFirstAsset;
        uint8 customLegoCombination;
        uint8 customMintingType;
        bytes32 adminRole; // Only for AccessControl users
    }

    struct Collection {
        address collection;
        address verificationSponsor;
        uint256 verificationFeeBalance;
        LegoCombination legoCombination;
        MintingType mintingType;
        bool isSoulbound;
        bool visible;
        bool verified;
        CollectionConfig config;
    }

    event CollectionAdded(
        address deployer,
        string name,
        string symbol,
        uint256 maxSupply,
        string collectionMetadata,
        LegoCombination legoCombination,
        MintingType mintingType,
        bool isSoulbound,
        CollectionConfig config
    );

    function addCollection(
        address collection,
        address deployer,
        uint256 maxSupply,
        LegoCombination legoCombination,
        MintingType mintingType,
        bool isSoulbound,
        CollectionConfig memory config,
        string memory collectionMetadata
    ) external;
}

// File: contracts/RMRKMintaur.sol



//SPDX-License-Identifier: Apache 2.0

pragma solidity ^0.8.21;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/access/Ownable.sol";
import "./interfaces/IRMRKDeployer.sol";
import "./interfaces/IRMRKMintaurRegistry.sol";
import "./interfaces/IRMRKRegistry.sol";

error FailedToSend();
error FeeTooLow();
error InvalidCutOffDate();
error WrongValueSent();

/**
 * @title RMRK Mint'aur
 * @notice This contract is used to create new RMRK collections
 */
contract RMRKMintaur is Ownable {
    /**
     * @notice Emitted when a collection is created.
     * @param collection The address of the new collection
     * @param deployer The address of the deployer
     */
    event CollectionDeployed(
        address indexed collection,
        address indexed deployer,
        uint256 maxSupply,
        uint256 mintPrice,
        uint256 cutOffDate,
        string collectionMetadata
    );

    uint8 CUSTOM_MINTING_TYPE_FROM_MINTAUR = 3;
    IRMRKRegistry.CollectionConfig private _defaultCollectionConfig;

    address private _platformBeneficiary;
    IRMRKDeployer private _deployer;
    IRMRKRegistry private _registry;
    IRMRKMintaurRegistry private _mintaurRegistry;

    uint256 private _assetPinningPrice;
    uint256 private _minimumMintingFee;
    uint256 private _mintingFeePercentageBPS;

    /**
     * @notice Initializes the contract.
     * @dev The basis points (bPt) are integer representation of percentage up to the second decimal space. Meaning that
     *  1 bPt equals 0.01% and 500 bPt equal 5%.
     * @param beneficiary The address of the beneficiary
     * @param deployer The address of the deployer contract
     * @param registry The address of the registry contract
     * @param assetPinningPrice The individial pinning price per asset
     * @param minimumMintingFee The minimum price per minted token
     * @param mintingFeePercentageBPS The minting fee percentage in basis points
     */
    constructor(
        address beneficiary,
        address deployer,
        address registry,
        uint256 assetPinningPrice,
        uint256 minimumMintingFee,
        uint256 mintingFeePercentageBPS
    ) {
        _platformBeneficiary = beneficiary;
        _deployer = IRMRKDeployer(deployer);
        _registry = IRMRKRegistry(registry);
        _assetPinningPrice = assetPinningPrice;
        _minimumMintingFee = minimumMintingFee;
        _mintingFeePercentageBPS = mintingFeePercentageBPS;

        _defaultCollectionConfig = IRMRKRegistry.CollectionConfig(
            true,
            false,
            true,
            true,
            false,
            true,
            false,
            false,
            false,
            0,
            CUSTOM_MINTING_TYPE_FROM_MINTAUR,
            0x0
        );
    }

    // -------------- GETTERS --------------

    /**
     * @notice Returns the address of the platform beneficiary.
     * @return beneficiary The address of the platform beneficiary
     */
    function getBeneficiary() public view returns (address) {
        return _platformBeneficiary;
    }

    /**
     * @notice Returns the address of the deployer contract.
     * @return deployer The address of the deployer contract
     */
    function getDeployer() public view returns (address) {
        return address(_deployer);
    }

    /**
     * @notice Returns the address of the registry contract.
     * @return registry The address of the registry contract
     */
    function getRegistry() public view returns (address) {
        return address(_registry);
    }

    /**
     * @notice Returns the address of the mintaur registry contract.
     * @return mintaurRegistry The address of the mintaur registry contract
     */
    function getMintaurRegistry() public view returns (address) {
        return address(_mintaurRegistry);
    }

    /**
     * @notice Returns the individual pinning price per asset.
     * @return assetPinningPrice The individual pinning price per asset
     */
    function getAssetPinningPrice() public view returns (uint256) {
        return _assetPinningPrice;
    }

    /**
     * @notice Returns the minimum fee per minted token.
     * @return minimumMintingFee The minimum fee per minted token
     */
    function getMinimumMintingFee() public view returns (uint256) {
        return _minimumMintingFee;
    }

    /**
     * @notice Returns the minting fee percentage in basis points.
     * @return mintingFeePercentageBPS The minting fee percentage in basis points
     */
    function getMintingFeePercentageBPS() public view returns (uint256) {
        return _mintingFeePercentageBPS;
    }

    // -------------- ADMIN SETTERS --------------

    /**
     * @notice Sets the address of the beneficiary.
     * @param beneficiary The address of the beneficiary
     */
    function setBeneficiary(address beneficiary) public onlyOwner {
        _platformBeneficiary = beneficiary;
    }

    /**
     * @notice Sets the address of the deployer contract.
     * @param deployer The address of the deployer contract
     */
    function setDeployer(address deployer) public onlyOwnerOrContributor {
        _deployer = IRMRKDeployer(deployer);
    }

    /**
     * @notice Sets the address of the registry contract.
     * @param registry The address of the registry contract
     */
    function setRegistry(address registry) public onlyOwnerOrContributor {
        _registry = IRMRKRegistry(registry);
    }

    /**
     * @notice Sets the address of the mintaur registry contract.
     * @param mintaurRegistry The address of the mintaur registry contract
     */
    function setMintaurRegistry(
        address mintaurRegistry
    ) public onlyOwnerOrContributor {
        _mintaurRegistry = IRMRKMintaurRegistry(mintaurRegistry);
    }

    /**
     * @notice Sets the individual pinning price per asset.
     * @param assetPinningPrice The individual pinning price per asset
     */
    function setAssetPinningPrice(
        uint256 assetPinningPrice
    ) public onlyOwnerOrContributor {
        _assetPinningPrice = assetPinningPrice;
    }

    /**
     * @notice Sets the minimum fee per minted token
     * @param minimumMintingFee The minimum fee per minted token
     */
    function setMinimumMintingFee(
        uint256 minimumMintingFee
    ) public onlyOwnerOrContributor {
        _minimumMintingFee = minimumMintingFee;
    }

    /**
     * @notice Sets the minting fee percentage in basis points.
     * @param mintingFeePercentageBPS The minting fee percentage in basis points
     */
    function setMintingFeePercentageBPS(
        uint256 mintingFeePercentageBPS
    ) public onlyOwnerOrContributor {
        _mintingFeePercentageBPS = mintingFeePercentageBPS;
    }

    // -------------- Deploying --------------

    /**
     * @notice Deploys a new collection.
     * @param name Name of the token collection
     * @param symbol Symbol of the token collection
     * @param collectionMetadata CID of the collection metadata
     * @param maxSupply The maximum supply of tokens
     * @param royaltyRecipient Recipient of resale royalties
     * @param royaltyPercentageBps The percentage to be paid from the sale of the token expressed in basis points
     * @param initialAssetsMetadata Array with metadata of the initial assets which will be added into every minte
     */
    function deployCollection(
        string memory name,
        string memory symbol,
        string memory collectionMetadata,
        uint256 maxSupply,
        uint256 mintPrice,
        address beneficiary,
        address royaltyRecipient,
        uint16 royaltyPercentageBps,
        uint256 cutOffDate,
        string[] memory initialAssetsMetadata
    ) external payable {
        if (mintPrice < _minimumMintingFee) revert FeeTooLow();
        if (cutOffDate < block.timestamp) revert InvalidCutOffDate();
        _chargeAssetPinning(initialAssetsMetadata.length);

        address newCollection = _deployer.deployCollection(
            name,
            symbol,
            collectionMetadata,
            maxSupply,
            msg.sender,
            royaltyRecipient,
            royaltyPercentageBps,
            initialAssetsMetadata
        );
        _addCollectionToRegistry(newCollection, collectionMetadata, maxSupply);
        _mintaurRegistry.storeNewCollection(
            newCollection,
            mintPrice,
            getMintFeeEstimate(mintPrice),
            beneficiary,
            cutOffDate
        );

        emit CollectionDeployed(
            newCollection,
            msg.sender,
            maxSupply,
            mintPrice,
            cutOffDate,
            collectionMetadata
        );
    }

    function mint(
        address collection,
        address to,
        uint256 numToMint
    ) external payable {
        // Extra checks (e.g. allow lists) in the future can be added here
        _mintaurRegistry.mint{value: msg.value}(
            collection,
            to,
            numToMint
        );
    }

    function getMintFeeEstimate(
        uint256 mintPrice
    ) public view returns (uint256) {
        uint256 fee = (mintPrice * _mintingFeePercentageBPS) / 10000;
        if (fee < _minimumMintingFee) fee = _minimumMintingFee;
        return fee;
    }

    function _addCollectionToRegistry(
        address collection,
        string memory collectionMetadata,
        uint256 maxSupply
    ) private {
        _registry.addCollection(
            collection,
            _msgSender(),
            maxSupply,
            IRMRKRegistry.LegoCombination.Equippable,
            IRMRKRegistry.MintingType.Custom,
            false,
            _defaultCollectionConfig,
            collectionMetadata
        );
    }

    function _chargeAssetPinning(uint256 numAssets) private {
        uint256 price = _assetPinningPrice * numAssets;
        if (msg.value != price) revert WrongValueSent();

        (bool sent, ) = _platformBeneficiary.call{
            value: msg.value
        }("");
        if (!sent) revert FailedToSend();
    }
}