// 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 "../utils/TimeChecker.sol"; import "../core/HasSignature.sol"; // this contract will transfer ownership to BETimelockController after deployed // all onlyowner method would add timelock contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature { using UInt for uint256; IBEERC1155 public chip; address public executor; mapping(address => bool) public nftTokenSupported; event TokenEvolved( address indexed owner, uint256 indexed nonce, uint256[] tokenIds ); constructor() HasSignature("EvolveFactory", "1") {} function init(address chipAddress) external initializer onlyOwner { chip = IBEERC1155(chipAddress); } /** * @dev update executor */ function updateExecutor(address account) external onlyOwner { require(account != address(0), "address can not be zero"); executor = account; } function addNFTTokenSupport(address nftToken) external onlyOwner { nftTokenSupported[nftToken] = true; } function removeNFTTokenSupport(address nftToken) external onlyOwner { nftTokenSupported[nftToken] = false; } function evolve721NFT( address to, address nftAddress, uint256[2] calldata tokenIds, uint256 startTime, uint256 saltNonce, bytes calldata signature ) external signatureValid(signature) timeValid(startTime) { require( tokenIds[0] > 0 && tokenIds[1] > 0, "EvolveFactory: token to evolve and burn can not be 0" ); require( tokenIds[0] != tokenIds[1], "EvolveFactory: token to evolve and burn can not be same" ); require(nftTokenSupported[nftAddress], "EvolveFactory: Unsupported NFT"); IBEERC721 nft = IBEERC721(nftAddress); require( nft.ownerOf(tokenIds[0]) == to && nft.ownerOf(tokenIds[1]) == to, "EvolveFactory: current address is not owner of this nft now" ); uint256[] memory signArray = new uint256[](2); for (uint256 i = 0; i < tokenIds.length; ++i) { signArray[i] = tokenIds[i]; } bytes32 criteriaMessageHash = getMessageHash( to, startTime, saltNonce, signArray ); checkSigner(executor, criteriaMessageHash, signature); nft.burn(to, tokenIds[1]); _useSignature(signature); emit TokenEvolved(to, saltNonce, signArray); } function evolveChip( address to, uint256[] memory tokenIds, uint256 startTime, uint256 saltNonce, bytes calldata signature ) external signatureValid(signature) timeValid(startTime) { require(to != address(0), "EvolveFacrory: address is zero address"); uint256 len = tokenIds.length; uint256[] memory amounts = new uint256[](len - 1); uint256[] memory idsForBurn = new uint256[](len - 1); for (uint256 i = 0; i < len; ++i) { require( chip.balanceOf(to, tokenIds[i]) > 0, "EvolveFacrory: Chip specified not exists" ); if (i > 0) { idsForBurn[i - 1] = tokenIds[i]; amounts[i - 1] = 1; } } bytes32 criteriaMessageHash = getMessageHash( to, startTime, saltNonce, tokenIds ); checkSigner(executor, criteriaMessageHash, signature); chip.burnBatch(to, idsForBurn, amounts); _useSignature(signature); emit TokenEvolved(to, saltNonce, tokenIds); } 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); } }