// 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/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IBEERC721.sol"; import "../interfaces/IBEERC1155.sol"; import "../utils/UInt.sol"; import "../utils/TimeChecker.sol"; import "../core/HasSignature.sol"; import "./FactoryBase.sol"; contract MinterFactory is Ownable, FactoryBase, TimeChecker, Initializable, HasSignature { using UInt for uint256; using SafeERC20 for IERC20; address public executor; // NFT contract IBEERC721 public hero; IBEERC721 public equip; IBEERC1155 public chip; IBEERC1155 public shard; address public feeToAddress; event TokenMinted( address contractAddress, address indexed to, uint256 indexed nonce, uint256 indexed tokenId ); event TokenMintedBatch( address contractAddress, address indexed to, uint256 indexed nonce, uint256[] ids, uint256[] amounts ); 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]); } /** * @dev update executor */ function updateExecutor(address account) external onlyOwner { require(account != address(0), "address can not be zero"); executor = account; } function setFeeToAddress(address _feeToAddress) external onlyOwner { require( _feeToAddress != address(0), "fee received address can not be zero" ); feeToAddress = _feeToAddress; } /** * @dev mint function to distribute Hero NFT to user */ function mintHeroTo(address to, uint256 tokenId, uint256 nonce) external onlyOwner { mint721NFT(to, tokenId, nonce, hero); } /** * @dev mint function to distribute Equipment NFT to user */ function mintEquipTo(address to, uint256 tokenId, uint256 nonce) external onlyOwner { mint721NFT(to, tokenId, nonce, equip); } /** * @dev mint function to distribute Chip NFT to user */ function mintChipTo(address to, uint256 tokenId, uint256 nonce) external onlyOwner { mint1155NFT(to, tokenId, nonce, 1, chip); } /** * @dev mint function to distribute Shard NFT to user */ function mintShardTo(address to, uint256 tokenId, uint256 nonce) external onlyOwner { mint1155NFT(to, tokenId, nonce, 1, shard); } /** * @dev batch mint 1155 Chip to user */ function mintChipBatch(address to, uint256[] memory ids, uint256 nonce) 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, nonce, ids, amounts, chip); } /** * @dev batch mint 1155 Shard to user */ function mintShardBatch( address to, uint256 nonce, 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, nonce, ids, amounts, shard); } function mint721ByUser( address to, uint256 id, uint256 startTime, uint256 saltNonce, bytes calldata signature, IBEERC721 nft ) external signatureValid(signature) timeValid(startTime) { uint256[] memory signArray = new uint256[](1); signArray[0] = id; bytes32 criteriaMessageHash = getMessageHash( to, address(nft), startTime, saltNonce, signArray ); checkSigner(executor, criteriaMessageHash, signature); mint721NFT(to, id, saltNonce, nft); _useSignature(signature); } function mint1155BatchByUser( address to, uint256[] memory ids, uint256[] memory amounts, uint256 startTime, uint256 saltNonce, bytes calldata signature, IBEERC1155 nft ) external signatureValid(signature) timeValid(startTime) { uint256 len = ids.length; require(len > 0, "MinterFactory: ids cannot be empty"); require( len == amounts.length, "MinterFactory: ids and amounts length mismatch" ); 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, address(nft), startTime, saltNonce, signArray ); checkSigner(executor, criteriaMessageHash, signature); mint1155NFTBatch(to, saltNonce, ids, amounts, nft); _useSignature(signature); } function shardMixByUser( ShardParam memory param, uint256[] memory ids, uint256[] memory amounts, bytes calldata signature, IBEERC721 nft ) external signatureValid(signature) timeValid(param.startTime) { require(ids.length > 0, "MinterFactory: ids cannot be empty"); require( ids.length == amounts.length, "MinterFactory: ids and amounts length mismatch" ); uint256[] memory signArray = new uint256[](ids.length * 2); for (uint256 i = 0; i < ids.length; ++i) { require( shard.balanceOf(param.to, ids[i]) > 0, "MinterFactory: not enough shard" ); signArray[i * 2] = ids[i]; signArray[i * 2 + 1] = amounts[i]; } bytes32 criteriaMessageHash = getShardMixHash( param, address(nft), signArray ); checkSigner(executor, criteriaMessageHash, signature); // Check payment approval and buyer balance IERC20 paymentContract = IERC20(param.payToken); require( paymentContract.balanceOf(param.to) >= param.payAmount, "MinterFactory: doesn't have enough token to mix shard" ); require( paymentContract.allowance(param.to, address(this)) >= param.payAmount, "MinterFactory: doesn't approve MinterFactory to spend payment amount" ); // transfer money to address paymentContract.safeTransferFrom(param.to, feeToAddress, param.payAmount); shard.burnBatch(param.to, ids, amounts); mint721NFT(param.to, param.nftId, param.saltNonce, nft); _useSignature(signature); } function mint721NFT( address to, uint256 tokenId, uint256 nonce, IBEERC721 nft ) internal { require(to != address(0), "MinterFactory: to address can not be zero"); nft.mint(to, tokenId); emit TokenMinted(address(nft), to, nonce, tokenId); } function mint1155NFT( address to, uint256 id, uint256 nonce, 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, nonce, id); } function mint1155NFTBatch( address to, uint256 nonce, uint256[] memory ids, uint256[] memory amounts, IBEERC1155 nft ) internal { nft.mintBatch(to, ids, amounts, ""); emit TokenMintedBatch(address(nft), to, nonce, ids, amounts); } function getMessageHash( address _to, address _nftAddress, uint256 _startTime, uint256 _saltNonce, uint256[] memory _ids ) public pure returns (bytes32) { bytes memory encoded = abi.encodePacked( _to, _nftAddress, _startTime, _saltNonce ); uint256 len = _ids.length; for (uint256 i = 0; i < len; ++i) { encoded = bytes.concat(encoded, abi.encodePacked(_ids[i])); } return keccak256(encoded); } function getShardMixHash( ShardParam memory param, address nftAddress, uint256[] memory _ids ) internal pure returns (bytes32) { bytes memory encoded = abi.encodePacked( param.to, nftAddress, param.nftId, param.payToken, param.payAmount, param.startTime, param.saltNonce ); uint256 len = _ids.length; for (uint256 i = 0; i < len; ++i) { encoded = bytes.concat(encoded, abi.encodePacked(_ids[i])); } return keccak256(encoded); } }