contracts-imtbl/contracts/game/NFTLockMain.sol
2024-07-17 15:52:09 +08:00

154 lines
5.5 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {HasSignature} from "../core/HasSignature.sol";
import {TimeChecker} from "../utils/TimeChecker.sol";
interface INFT {
function mint(address to, uint256 tokenID) external;
function transferFrom(address from, address to, uint256 tokenId) external;
}
contract NFTLockMain is ERC721Holder, HasSignature, TimeChecker, Pausable {
using EnumerableSet for EnumerableSet.UintSet;
uint256 public immutable _CACHED_CHAIN_ID;
address public immutable _CACHED_THIS;
address public verifier;
uint256 public maxBatch = 100;
struct NFTInfo {
uint256 tokenId;
address to;
bool isMint;
}
mapping(address nft => mapping(uint256 tokenId => address user)) public addressOriginal;
mapping(address nft => mapping(address user => EnumerableSet.UintSet tokenIdSet)) private lockedRecords;
mapping(address nft => bool status) public supportNftList;
event UnLock(address indexed nft, address indexed user, uint256 nonce, NFTInfo[] nftList);
event Lock(address indexed nft, address indexed sender, address indexed to, uint256[] tokenIds);
event VerifierUpdated(address indexed verifier);
constructor(uint256 _duration, address _verifier) TimeChecker(_duration) {
_CACHED_CHAIN_ID = block.chainid;
_CACHED_THIS = address(this);
verifier = _verifier;
}
/**
* lock NFT
* from eoa only
* @param nft nft address
* @param to passport address for game
* @param tokenIds nft token id list
*/
function lock(address nft, address to, uint256[] calldata tokenIds) external whenNotPaused{
require(tokenIds.length <= maxBatch, "tokenIds too many");
require(to != address(0), "passport can't be zero");
require(supportNftList[nft], "not support nft");
address _sender = _msgSender();
for (uint256 i = 0; i < tokenIds.length; i++) {
addressOriginal[nft][tokenIds[i]] = _sender;
lockedRecords[nft][_sender].add(tokenIds[i]);
INFT(nft).transferFrom(_sender, address(this), tokenIds[i]);
}
emit Lock(nft, _sender, to, tokenIds);
}
/**
* @dev unlock or mint nft
* from passport only
* if tokenId not exists, mint it
* if exists and user is owner, unlock it
*/
function unlockOrMint(
address nft,
NFTInfo[] calldata nftList,
uint256 signTime,
uint256 saltNonce,
bytes calldata signature
) external signatureValid(signature) timeValid(signTime) {
require(nftList.length <= maxBatch, "tokenIds too many");
address _sender = _msgSender();
bytes32 messageHash = getMessageHash(_sender, nft, nftList, _CACHED_THIS, _CACHED_CHAIN_ID, signTime, saltNonce);
checkSigner(verifier, messageHash, signature);
_useSignature(signature);
for (uint256 i = 0; i < nftList.length; i++) {
if (nftList[i].isMint) {
INFT(nft).mint(nftList[i].to, nftList[i].tokenId);
} else {
require(addressOriginal[nft][nftList[i].tokenId] == _sender, "not owner");
delete addressOriginal[nft][nftList[i].tokenId];
lockedRecords[nft][_sender].remove(nftList[i].tokenId);
INFT(nft).transferFrom(address(this), nftList[i].to, nftList[i].tokenId);
}
}
emit UnLock(nft, _sender, saltNonce, nftList);
}
/**
* @dev unlock nft
* from game svr only
*/
function unlockWithSvr(address nft, uint256[] calldata tokenIds) external onlyOwner{
require(tokenIds.length <= maxBatch, "tokenIds too many");
for (uint256 i = 0; i < tokenIds.length; i++) {
address _sender = addressOriginal[nft][tokenIds[i]];
delete addressOriginal[nft][tokenIds[i]];
lockedRecords[nft][_sender].remove(tokenIds[i]);
INFT(nft).transferFrom(address(this), _sender, tokenIds[i]);
}
}
/** ------get------- **/
function lockedNum(address token, address user) public view returns (uint256) {
return lockedRecords[token][user].length();
}
function lockedNft(address token, address user) public view returns (uint256[] memory) {
return lockedRecords[token][user].values();
}
function updateBatch(uint256 _maxBatch) external onlyOwner {
maxBatch = _maxBatch;
}
function addSupportNftList(address[] calldata nftList) external onlyOwner {
for (uint256 i = 0; i < nftList.length; i++) {
supportNftList[nftList[i]] = true;
}
}
function removeSupportNft(address nftAddress) external onlyOwner {
require(supportNftList[nftAddress], "can't remove");
delete supportNftList[nftAddress];
}
/**
* @dev update verifier address
*/
function updateVerifier(address _verifier) external onlyOwner {
require(_verifier != address(0), "NFTClaimer: address can not be zero");
verifier = _verifier;
emit VerifierUpdated(_verifier);
}
function getMessageHash(
address _to,
address _nft,
NFTInfo[] memory _ids,
address _contract,
uint256 _chainId,
uint256 _signTime,
uint256 _saltNonce
) public pure returns (bytes32) {
bytes memory encoded = abi.encodePacked(_to, _nft, _contract, _chainId, _signTime, _saltNonce);
for (uint256 i = 0; i < _ids.length; ++i) {
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i].tokenId));
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i].to));
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i].isMint));
}
return keccak256(encoded);
}
}