// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../interfaces/IBEERC721.sol"; import "../interfaces/IBEERC1155.sol"; import "../utils/UInt.sol"; import "../core/HasSignature.sol"; contract MinterFactory is Ownable, Initializable, HasSignature { using UInt for uint256; address public executor; // NFT contract IBEERC721 public hero; IBEERC721 public equip; IBEERC1155 public chip; IBEERC1155 public shard; uint256 private _duration; mapping(address => bool) public approvalLists; event TokenMinted( address contractAddress, address to, uint256 indexed tokenId ); event TokenMintedBatch( address contractAddress, address indexed to, uint256[] ids, uint256[] amounts ); mapping(bytes => bool) public usedSignatures; constructor() HasSignature("MinterFactory", "1") {} function init(address[4] calldata _erc721s) external initializer onlyOwner { hero = IBEERC721(_erc721s[0]); equip = IBEERC721(_erc721s[1]); chip = IBEERC1155(_erc721s[2]); shard = IBEERC1155(_erc721s[3]); _duration = 1 days; } /** * @dev Allow operation to reverse signature. */ function addApprovalList(address user) external onlyOwner { require(!approvalLists[user], "MinterFactory: Invalid user address"); approvalLists[user] = true; } /** * @dev Remove operation from approval list. */ function removeApprovalList(address user) external onlyOwner { approvalLists[user] = false; } /** * @dev update executor */ function updateExecutor(address account) external onlyOwner { require(account != address(0), "address can not be zero"); executor = account; } /** * @dev Returns the max duration for function called by user */ function getDuration() external view returns (uint256 duration) { return _duration; } /** * @dev Change duration value */ function updateDuation(uint256 valNew) external onlyOwner { _duration = valNew; } /** * @dev mint function to distribute Hero NFT to user */ function mintHeroTo(address to, uint256 tokenId) external onlyOwner { mint721NFT(to, tokenId, hero); } /** * @dev mint function to distribute Equipment NFT to user */ function mintEquipTo(address to, uint256 tokenId) external onlyOwner { mint721NFT(to, tokenId, equip); } /** * @dev mint function to distribute Chip NFT to user */ function mintChipTo(address to, uint256 tokenId) external onlyOwner { mint1155NFT(to, tokenId, 1, chip); } /** * @dev mint function to distribute Shard NFT to user */ function mintShardTo(address to, uint256 tokenId) external onlyOwner { mint1155NFT(to, tokenId, 1, shard); } /** * @dev batch mint 1155 Chip to user */ function mintChipBatch(address to, uint256[] memory ids) external onlyOwner { require( to != address(0), "MinterFactory::mintChipBatch: to address can not be zero" ); require( ids.length > 0, "MinterFactory::mintChipBatch: ids cannot be empty" ); uint256[] memory amounts = new uint256[](ids.length); uint256 len = ids.length; for (uint256 i = 0; i < len; ++i) { amounts[i] = 1; } mint1155NFTBatch(to, ids, amounts, chip); } /** * @dev batch mint 1155 Shard to user */ function mintShardBatch( address to, uint256[] memory ids, uint256[] memory amounts ) external onlyOwner { require(to != address(0), "MinterFactory: to address can not be zero"); require(ids.length > 0, "MinterFactory: ids cannot be empty"); require( ids.length == amounts.length, "MinterFactory: ids and amounts length mismatch" ); mint1155NFTBatch(to, ids, amounts, shard); } function mint721ByUser( address to, uint256 id, uint256 startTime, uint256 saltNonce, bytes calldata signature, IBEERC721 nft ) external { require( startTime + _duration >= block.timestamp, "MinterFactory: signature expired, please send another transaction with new signature" ); require( !usedSignatures[signature], "MinterFactory: signature used. please send another transaction with new signature" ); uint256[] memory signArray = new uint256[](1); signArray[0] = id; bytes32 criteriaMessageHash = getMessageHash(to, startTime, saltNonce, signArray); checkSigner(executor, criteriaMessageHash, signature); mint721NFT(to, id, nft); usedSignatures[signature] = true; } function mint1155BatchByUser( address to, uint256[] memory ids, uint256[] memory amounts, uint256 startTime, uint256 saltNonce, bytes calldata signature, IBEERC1155 nft ) external { uint256 len = ids.length; require(len > 0, "MinterFactory: ids cannot be empty"); require( len == amounts.length, "MinterFactory: ids and amounts length mismatch" ); require( startTime + _duration >= block.timestamp, "MinterFactory: signature expired, please send another transaction with new signature" ); require( !usedSignatures[signature], "MinterFactory: signature used. please send another transaction with new signature" ); uint256[] memory signArray = new uint256[](len * 2); for (uint256 i = 0; i < len; ++i) { require( nft.canMint(ids[i]), "MinterFactory: can not mint for current nft rule setting" ); signArray[i * 2] = ids[i]; signArray[i * 2 + 1] = amounts[i]; } bytes32 criteriaMessageHash = getMessageHash(to, startTime, saltNonce, signArray); checkSigner(executor, criteriaMessageHash, signature); mint1155NFTBatch(to, ids, amounts, nft); usedSignatures[signature] = true; } function mint721NFT( address to, uint256 tokenId, IBEERC721 nft ) internal { require(to != address(0), "MinterFactory: to address can not be zero"); nft.mint(to, tokenId); emit TokenMinted(address(nft), to, tokenId); } function mint1155NFT( address to, uint256 id, uint256 amount, IBEERC1155 nft ) internal { require(to != address(0), "MinterFactory: to address can not be zero"); nft.mintBatch(to, id.asSingletonArray(), amount.asSingletonArray(), ""); emit TokenMinted(address(chip), to, id); } function mint1155NFTBatch( address to, uint256[] memory ids, uint256[] memory amounts, IBEERC1155 nft ) internal { nft.mintBatch(to, ids, amounts, ""); emit TokenMintedBatch(address(nft), to, ids, amounts); } function ignoreSignature( bytes calldata signature ) external { require( approvalLists[_msgSender()], "Must be valid approval list" ); if (!usedSignatures[signature]) { usedSignatures[signature] = true; } } function getMessageHash( address _to, uint256 _startTime, uint256 _saltNonce, uint256[] memory _ids ) public pure returns (bytes32) { bytes memory encoded = abi.encodePacked(_to, _startTime, _saltNonce); uint256 len = _ids.length; for (uint256 i = 0; i < len; ++i) { encoded = bytes.concat(encoded, abi.encodePacked(_ids[i])); } return keccak256(encoded); } }