// 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; IBEERC721 public hero; IBEERC721 public equip; IBEERC1155 public chip; uint256 private _duration; address public executor; event TokenEvolved( address indexed owner, uint256[3] tokenIds ); constructor() HasSignature("EvolveFactory", "1"){ } function init(address[3] calldata _erc721s) external initializer onlyOwner { hero = IBEERC721(_erc721s[0]); equip = IBEERC721(_erc721s[1]); chip = IBEERC1155(_erc721s[2]); } /** * @dev update executor */ function updateExecutor(address account) external onlyOwner { require(account != address(0), 'address can not be zero'); executor = account; } function evolve721NFT( address to, uint256[3] calldata tokenIds, uint256 startTime, uint256 saltNonce, bytes calldata signature, IBEERC721 nft ) internal signatureValid(signature) timeValid(startTime){ require( tokenIds[0] > 0 && tokenIds[1] > 0, "EvolveFactory: equip to evolve and burn can not be 0" ); require( tokenIds[0] != tokenIds[1], "EvolveFactory: equip to evolve and burn can not be same" ); require( nft.ownerOf(tokenIds[0]) == to, "EvolveFactory: current address is not owner of this nft now" ); require( nft.ownerOf(tokenIds[1]) == to, "EvolveFactory: current address is not owner of this nft now" ); if (tokenIds[2] > 0) { require( nft.ownerOf(tokenIds[2]) == to, "EvolveFactory: current address is not owner of this nft now" ); } uint256[] memory signArray = new uint256[](3); 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]); if (tokenIds[2] > 0) { uint256 amount = 1; chip.burnBatch(to, tokenIds[2].asSingletonArray(), amount.asSingletonArray()); } useSignature(signature); emit TokenEvolved( to, 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); } }