// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author: manifold.xyz import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "../extensions/ERC1155/IERC1155CreatorExtensionApproveTransfer.sol"; import "../extensions/ERC1155/IERC1155CreatorExtensionBurnable.sol"; import "../permissions/ERC1155/IERC1155CreatorMintPermissions.sol"; import "./IERC1155CreatorCore.sol"; import "./CreatorCore.sol"; /** * @dev Core ERC1155 creator implementation */ abstract contract ERC1155CreatorCore is CreatorCore, IERC1155CreatorCore { uint256 constant public VERSION = 3; using EnumerableSet for EnumerableSet.AddressSet; // Track registered extensions data mapping (address => bool) internal _extensionApproveTransfers; mapping (address => address) internal _extensionPermissions; // For tracking which extension a token was minted by mapping (uint256 => address) internal _tokensExtension; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(CreatorCore, IERC165) returns (bool) { return interfaceId == type(IERC1155CreatorCore).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {CreatorCore-_setApproveTransferExtension} */ function _setApproveTransferExtension(address extension, bool enabled) internal override { if (ERC165Checker.supportsInterface(extension, type(IERC1155CreatorExtensionApproveTransfer).interfaceId)) { _extensionApproveTransfers[extension] = enabled; emit ExtensionApproveTransferUpdated(extension, enabled); } } /** * @dev Set mint permissions for an extension */ function _setMintPermissions(address extension, address permissions) internal { require(_extensions.contains(extension), "Invalid extension"); require(permissions == address(0) || ERC165Checker.supportsInterface(permissions, type(IERC1155CreatorMintPermissions).interfaceId), "Invalid address"); if (_extensionPermissions[extension] != permissions) { _extensionPermissions[extension] = permissions; emit MintPermissionsUpdated(extension, permissions, msg.sender); } } /** * If mint permissions have been set for an extension (extensions can mint by default), * check if an extension can mint via the permission contract's approveMint function. */ function _checkMintPermissions(address[] memory to, uint256[] memory tokenIds, uint256[] memory amounts) internal { if (_extensionPermissions[msg.sender] != address(0)) { IERC1155CreatorMintPermissions(_extensionPermissions[msg.sender]).approveMint(msg.sender, to, tokenIds, amounts); } } /** * Post burn actions */ function _postBurn(address owner, uint256[] calldata tokenIds, uint256[] calldata amounts) internal virtual { require(tokenIds.length > 0, "Invalid input"); address extension = _tokensExtension[tokenIds[0]]; for (uint i; i < tokenIds.length;) { require(_tokensExtension[tokenIds[i]] == extension, "Mismatched token originators"); unchecked { ++i; } } // Callback to originating extension if needed if (extension != address(0)) { if (ERC165Checker.supportsInterface(extension, type(IERC1155CreatorExtensionBurnable).interfaceId)) { IERC1155CreatorExtensionBurnable(extension).onBurn(owner, tokenIds, amounts); } } } /** * Approve a transfer */ function _approveTransfer(address from, address to, uint256[] memory tokenIds, uint256[] memory amounts) internal { // Do not need to approve mints if (from == address(0)) return; address extension = _tokensExtension[tokenIds[0]]; for (uint i; i < tokenIds.length;) { require(_tokensExtension[tokenIds[i]] == extension, "Mismatched token originators"); unchecked { ++i; } } if (extension != address(0) && _extensionApproveTransfers[extension]) { require(IERC1155CreatorExtensionApproveTransfer(extension).approveTransfer(msg.sender, from, to, tokenIds, amounts), "Extension approval failure"); } else if (_approveTransferBase != address(0)) { require(IERC1155CreatorExtensionApproveTransfer(_approveTransferBase).approveTransfer(msg.sender, from, to, tokenIds, amounts), "Extension approval failure"); } } function _tokenExtension(uint256 tokenId) internal view override returns(address) { return _tokensExtension[tokenId]; } }