// 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 "../HasSignature.sol"; interface IMintableERC721 is IERC721 { function mint(address to, uint256 tokenId) external; function burn(address owner, uint256 tokenId) external; } contract NFTActivateProxy is Ownable, Initializable, HasSignature { uint256 private constant MINI_GEN_NFT_ID = 1e17; uint256 private constant MAX_GAME_NFT_ID = 1e16; IMintableERC721 public hero; IMintableERC721 public equip; IMintableERC721 public chip; uint256 public constant TYPE_NONE = 0; uint256 public constant TYPE_HERO = 1; uint256 public constant TYPE_EQUIP = 2; uint256 public constant TYPE_CHIP = 3; address public executor; mapping(bytes => bool) public usedSignatures; event LogNFTActivate( address indexed to, uint256 indexed nftOld, uint256 nftNew, uint256 nftType ); constructor() HasSignature("NFTActivateProxy", "1") {} function init(address[3] calldata _erc721s) external initializer onlyOwner { hero = IMintableERC721(_erc721s[0]); equip = IMintableERC721(_erc721s[1]); chip = IMintableERC721(_erc721s[2]); } /** * @dev update executor */ function updateExecutor(address account) external onlyOwner { require(account != address(0), "address can not be zero"); executor = account; } function activateOne( uint256 nftOld, uint256 nftNew, uint256 nftType, uint256 saltNonce, bytes calldata signature ) external { require(nftOld >= MINI_GEN_NFT_ID, "NFTActivateProxy: GEN nftid must greater than 1e18"); require(nftNew < MAX_GAME_NFT_ID, "NFTActivateProxy: GEN nftid must less than 1e17"); require(nftType > 0, "NFTActivateProxy: nft type must greater than 0"); require(nftType < 4, "NFTActivateProxy: nft type must less than 4"); require( !usedSignatures[signature], "NFTActivateProxy: signature used. please send another transaction with new signature" ); address owner = msg.sender; IMintableERC721 tokenOld = getIMintableERC721ByType(nftType); require( tokenOld.ownerOf(nftOld) == owner, "NFTActivateProxy: only owner can activate this nft" ); bytes32 criteriaMessageHash = getMessageHash( owner, nftOld, nftNew, nftType, saltNonce ); checkSigner(executor, criteriaMessageHash, signature); // activate the old nft tokenOld.burn(owner, nftOld); usedSignatures[signature] = true; mint721WithType(owner, nftNew, nftType); emit LogNFTActivate(owner, nftOld, nftNew, nftType); } function getIMintableERC721ByType(uint256 nftType) private view returns(IMintableERC721){ if (nftType == TYPE_HERO) { return hero; } else if (nftType == TYPE_EQUIP) { return equip; } else { return chip; } } function mint721WithType( address to, uint256 tokenId, uint256 typeNum ) private { if (typeNum == TYPE_HERO) { hero.mint(to, tokenId); } else if (typeNum == TYPE_EQUIP) { equip.mint(to, tokenId); } else { chip.mint(to, tokenId); } } function getMessageHash( address _owner, uint256 _nftOld, uint256 _nftNew, uint256 _nftType, uint256 _saltNonce ) public pure returns (bytes32) { return keccak256( abi.encodePacked( _owner, _nftOld, _nftNew, _nftType, _saltNonce ) ); } }