// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "../core/HasSignature.sol"; import "../utils/TimeChecker.sol"; import "../interfaces/IBEERC1155.sol"; import "../interfaces/IBEERC721.sol"; contract NftChipLocker is Ownable, ERC1155Holder, HasSignature, TimeChecker{ mapping(address => bool) public nftTokenSupported; using EnumerableSet for EnumerableSet.UintSet; uint256 public constant MAX_CHIP_NUM = 4; constructor() HasSignature("NftChipLocker", "1") {} /** * nft address => chip address => nftid => slot => chip tokenid */ mapping( address => mapping(address => mapping(uint256 => mapping(uint256 => uint256)))) chipPlugined; /** * nft address => chip address => chip tokenid => nftid */ mapping( address => mapping(address => mapping(uint256 => uint256))) chipOwner; event ChipPlugin( address indexed nft, uint256 indexed nftId, uint256 indexed nonce, address chip, uint256[] ids ); event ChipUnplug( address indexed nft, uint256 indexed nftId, uint256 indexed nonce, address chip, uint256[] ids ); function addNFTTokenSupport(address nftToken) external onlyOwner { nftTokenSupported[nftToken] = true; } function removeNFTTokenSupport(address nftToken) external onlyOwner { nftTokenSupported[nftToken] = false; } function pluginChip( address[3] calldata addresses, uint256[3] calldata values, uint256[] memory chipIds, uint256[] memory chipSlot, bytes calldata signature ) external signatureValid(signature) timeValid(values[2]) { // addresses[3] [nft, chip, svr_address] // uint256[3] [token_id,salt_nonce,startTime] require(chipIds.length == chipSlot.length, "NftChipLocker: chip id and index mislength"); require(chipIds.length <= MAX_CHIP_NUM, "NftChipLocker: chip num reach max allow"); require(nftTokenSupported[addresses[0]], "NftChipLocker: Unsupported NFT"); require(nftTokenSupported[addresses[1]], "NftChipLocker: Unsupported Chip"); require(!IBEERC721(addresses[0]).isLocked(values[0]), "NftChipLocker: Can not pluin chip to locked token"); require(IERC721(addresses[0]).ownerOf(values[0]) == msg.sender, "NftChipLocker: not owner of this nft now"); uint256[] memory signArray = new uint256[](values.length + chipIds.length * 2); for (uint256 i = 0; i < values.length; ++i ) { signArray[i] = values[i]; } uint256[] memory amounts = new uint256[](chipIds.length); for (uint256 i = 0; i < chipIds.length; ++i) { require( chipSlot[i] < MAX_CHIP_NUM, "NftChipLocker: slot error" ); require( IERC1155(addresses[1]).balanceOf(msg.sender, chipIds[i]) > 0, "NftChipLocker: not enough chip" ); require( !IBEERC1155(addresses[1]).isLocked(chipIds[i]), "NftChipLocker: chip is locked" ); require( chipPlugined[addresses[0]][addresses[1]][values[0]][chipSlot[i]] == 0, "NftChipLocker: slot already plugined" ); chipPlugined[addresses[0]][addresses[1]][values[0]][chipSlot[i]] = chipIds[i]; chipOwner[addresses[0]][addresses[1]][chipIds[i]] = values[0]; amounts[i] = 1; signArray[values.length + 2 * i] = chipIds[i]; signArray[values.length + 2 * i + 1] = chipSlot[i]; } bytes32 criteriaMessageHash = getMessageHash( addresses[0], addresses[1], _msgSender(), signArray ); checkSigner(addresses[2], criteriaMessageHash, signature); IERC1155(addresses[1]).safeBatchTransferFrom(msg.sender, address(this), chipIds, amounts, ""); _useSignature(signature); emit ChipPlugin(addresses[0], values[0], values[1], addresses[1], chipIds); } function unplugChip( address[3] calldata addresses, uint256[3] calldata values, uint256[] memory chipIds, uint256[] memory chipSlot, bytes calldata signature ) external signatureValid(signature) timeValid(values[2]) { // addresses[3] [nft, chip, svr_address] // uint256[3] [token_id,salt_nonce,startTime] require(chipIds.length == chipSlot.length, "NftChipLocker: chip id and index mislength"); require(chipIds.length <= MAX_CHIP_NUM, "NftChipLocker: chip num reach max allow"); require(nftTokenSupported[addresses[0]], "NftChipLocker: Unsupported NFT"); require(nftTokenSupported[addresses[1]], "NftChipLocker: Unsupported Chip"); require(!IBEERC721(addresses[0]).isLocked(values[0]), "NftChipLocker: Can not pluin chip to locked token"); require(IERC721(addresses[0]).ownerOf(values[0]) == msg.sender, "NftChipLocker: not owner of this nft now"); uint256[] memory signArray = new uint256[](values.length + chipIds.length * 2); for (uint256 i = 0; i < values.length; ++i ) { signArray[i] = values[i]; } uint256[] memory amounts = new uint256[](chipIds.length); for (uint256 i = 0; i < chipIds.length; ++i) { require( chipPlugined[addresses[0]][addresses[1]][values[0]][chipSlot[i]] > 0, "NftChipLocker: chip not exists" ); delete chipPlugined[addresses[0]][addresses[1]][values[0]][chipSlot[i]]; delete chipOwner[addresses[0]][addresses[1]][chipIds[i]]; amounts[i] = 1; signArray[values.length + 2 * i] = chipIds[i]; signArray[values.length + 2 * i + 1] = chipSlot[i]; } bytes32 criteriaMessageHash = getMessageHash( addresses[0], addresses[1], _msgSender(), signArray ); checkSigner(addresses[2], criteriaMessageHash, signature); IERC1155(addresses[1]).safeBatchTransferFrom(address(this), msg.sender, chipIds, amounts, ""); _useSignature(signature); emit ChipUnplug(addresses[0], values[0], values[1], addresses[1], chipIds); } function chipOwnerTokenid(address nft, address chip, uint256 chipId) external view returns(uint256) { return chipOwner[nft][chip][chipId]; } function pluginedChipNum(address nft, address chip, uint256 tokenId) public view returns(uint256) { uint256 len = 0; for (uint256 i = 0; i < MAX_CHIP_NUM; ++i) { if (chipPlugined[nft][chip][tokenId][i] > 0) { len ++; } } return len; } function pluginedChips(address nft, address chip, uint256 tokenId) external view returns(uint256[] memory) { uint256[] memory result = new uint256[](MAX_CHIP_NUM); for (uint256 i = 0; i < MAX_CHIP_NUM; ++i) { result[i] = chipPlugined[nft][chip][tokenId][i]; } return result; } function getMessageHash( address _nftAddress, address _chipAddress, address _userAddress, uint256[] memory _datas ) public pure returns (bytes32) { bytes memory encoded = abi.encodePacked( _nftAddress, _chipAddress, _userAddress ); uint256 len = _datas.length; for (uint256 i = 0; i < len; ++i) { encoded = bytes.concat(encoded, abi.encodePacked(_datas[i])); } return keccak256(encoded); } }