// SPDX-License-Identifier: FSL-1.1-MIT pragma solidity ^0.8.24; import { Context } from "@openzeppelin/contracts/utils/Context.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { ERC165, IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; contract ERC1155Facet is Context, ERC165, IERC1155 { /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); using Address for address; mapping(uint256 => mapping(address => uint256)) private _balances; mapping(address => mapping(address => bool)) private _operatorApprovals; function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC1155).interfaceId || super.supportsInterface(interfaceId); } function balanceOf(address account, uint256 id) external view override returns (uint256) { require(account != address(0), "ERC1155: balance query for the zero address"); return _balances[id][account]; } function balanceOfBatch( address[] calldata accounts, uint256[] calldata ids ) external view override returns (uint256[] memory) { require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); uint256[] memory batchBalances = new uint256[](accounts.length); for (uint256 i = 0; i < accounts.length; ++i) { batchBalances[i] = _balances[ids[i]][accounts[i]]; } return batchBalances; } function setApprovalForAll(address operator, bool approved) external override { _setApprovalForAll(_msgSender(), operator, approved); } function isApprovedForAll(address account, address operator) external view override returns (bool) { return _operatorApprovals[account][operator]; } function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external override { require( from == _msgSender() || _operatorApprovals[from][_msgSender()], "ERC1155: caller is not owner nor approved" ); _safeTransferFrom(from, to, id, amount, data); } function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external override { require( from == _msgSender() || _operatorApprovals[from][_msgSender()], "ERC1155: transfer caller is not owner nor approved" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } function _safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) internal { require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; emit TransferSingle(operator, from, to, id, amount); _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); } function _safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal { require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; } emit TransferBatch(operator, from, to, ids, amounts); _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); } function _setApprovalForAll(address owner, address operator, bool approved) internal { require(owner != operator, "ERC1155: setting approval status for self"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 value, bytes memory data ) private { if (to.code.length > 0) { try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { // Tokens rejected revert ERC1155InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { // non-ERC1155Receiver implementer revert ERC1155InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) private { if (to.code.length > 0) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { // Tokens rejected revert ERC1155InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { // non-ERC1155Receiver implementer revert ERC1155InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } // Additional functions for minting and burning function mint(address to, uint256 id, uint256 amount, bytes calldata data) external { require(to != address(0), "ERC1155: mint to the zero address"); address operator = _msgSender(); _balances[id][to] += amount; emit TransferSingle(operator, address(0), to, id, amount); _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); } function burn(address from, uint256 id, uint256 amount) external { require(from != address(0), "ERC1155: burn from the zero address"); require( from == _msgSender() || _operatorApprovals[from][_msgSender()], "ERC1155: caller is not owner nor approved" ); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } emit TransferSingle(_msgSender(), from, address(0), id, amount); } function getSelectors() external pure returns (bytes4[] memory) { bytes4[] memory selectors = new bytes4[](8); selectors[0] = this.balanceOf.selector; selectors[1] = this.balanceOfBatch.selector; selectors[2] = this.setApprovalForAll.selector; selectors[3] = this.isApprovedForAll.selector; selectors[4] = this.safeTransferFrom.selector; selectors[5] = this.safeBatchTransferFrom.selector; selectors[6] = this.mint.selector; selectors[7] = this.burn.selector; return selectors; } }