// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../core/HasSignature.sol"; import "../utils/TimeChecker.sol"; import "../utils/UInt.sol"; interface IBurnableERC721 is IERC721 { function burn(uint256 tokenId) external; function batchMint(address to, uint256[] memory tokenIds) external; } contract EvolveProxy is Ownable, TimeChecker, HasSignature { using UInt for uint256; IBurnableERC721 public nft; address public executor; event TokenEvolved( uint256 indexed evolveEventId, address indexed owner, uint256[] tokenIds ); constructor(address _address){ nft = IBurnableERC721(_address); } /** * @dev update executor */ function updateExecutor(address account) external onlyOwner { require(account != address(0), "address can not be zero"); executor = account; } /** * @dev evolve function to Counter Fire nft NFT * tokenIds: [nft_to_evolve, nft_for_burn, nft_for_burn...] * nft_to_evolve: nft to evolve, auto mint if not exist */ function evolveNft( uint256 evolveEventId, uint256[] calldata tokenIds, uint256 startTime, uint256 saltNonce, bytes calldata signature ) external signatureValid(signature) timeValid(startTime) { require(tokenIds.length > 0, "EvolveProxy: tokenIds length must > 0"); require(tokenIds[0] > 0, "EvolveProxy: nft to evolve can not be 0"); address user = _msgSender(); bytes32 criteriaMessageHash = getMessageHash( evolveEventId, user, startTime, saltNonce, tokenIds ); checkSigner(executor, criteriaMessageHash, signature); try nft.batchMint(user, tokenIds[0].asSingletonArray()) {} catch {} require(nft.ownerOf(tokenIds[0]) == user,"EvolveProxy: not owner of this nft now"); if (tokenIds.length > 1) { for (uint256 i = 1; i < tokenIds.length; ++i) { nft.burn(tokenIds[i]); } } _useSignature(signature); emit TokenEvolved(evolveEventId,user,tokenIds); } function getMessageHash( uint256 _eventId, address _user, uint256 _startTime, uint256 _saltNonce, uint256[] memory _tokenIds ) public pure returns (bytes32) { bytes memory encoded = abi.encodePacked(_eventId, _user, _startTime, _saltNonce); uint256 len = _tokenIds.length; for (uint256 i = 0; i < len; ++i) { encoded = bytes.concat(encoded, abi.encodePacked(_tokenIds[i])); } return keccak256(encoded); } }