// 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 "../interfaces/IBEERC1155.sol"; import "../interfaces/IBEERC721.sol"; contract NftChipLocker is Ownable, ERC1155Holder{ mapping(address => bool) public nftTokenSupported; using EnumerableSet for EnumerableSet.UintSet; uint256 public constant MAX_CHIP_NUM = 4; /** * nft address => chip address => nftid => chip tokenid */ mapping( address => mapping(address => mapping(uint256 => EnumerableSet.UintSet))) chipPlugined; /** * nft address => chip address => chip tokenid => nftid */ mapping( address => mapping(address => mapping(uint256 => uint256))) chipOwner; event ChipPlugin( address nft, uint256 nftId, address chip, uint256[] ids ); event ChipUnplug( address nft, uint256 nftId, 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 nft, address chip, uint256 nftId, uint256[] memory chipIds) external { require(nftTokenSupported[nft], "NftChipLocker: Unsupported NFT"); require(nftTokenSupported[chip], "NftChipLocker: Unsupported Chip"); require(!IBEERC721(nft).isLocked(nftId), "NftChipLocker: Can not pluin chip to locked token"); require(IERC721(nft).ownerOf(nftId) == msg.sender, "NftChipLocker: not owner of this nft now"); uint256 len = chipIds.length; uint256 currentNum = chipPlugined[nft][chip][nftId].length(); require(len + currentNum <= MAX_CHIP_NUM, "NftChipLocker: reach max chip num"); uint256[] memory amounts = new uint256[](len); for (uint256 i = 0; i < len; ++i) { require( IERC1155(chip).balanceOf(msg.sender, chipIds[i]) > 0, "NftChipLocker: not enough chip" ); require( !IBEERC1155(chip).isLocked(chipIds[i]), "NftChipLocker: chip is locked" ); chipPlugined[nft][chip][nftId].add(chipIds[i]); chipOwner[nft][chip][chipIds[i]] = nftId; amounts[i] = 1; } IERC1155(chip).safeBatchTransferFrom(msg.sender, address(this), chipIds, amounts, ""); emit ChipPlugin(nft, nftId, chip, chipIds); } function unplugChip(address nft, address chip, uint256 nftId, uint256[] memory chipIds) external { require(nftTokenSupported[nft], "NftChipLocker: Unsupported NFT"); require(nftTokenSupported[chip], "NftChipLocker: Unsupported Chip"); require(!IBEERC721(nft).isLocked(nftId), "NftChipLocker: Can not pluin chip to locked token"); require(IERC721(nft).ownerOf(nftId) == msg.sender, "NftChipLocker: not owner of this nft now"); uint256 len = chipIds.length; uint256[] memory amounts = new uint256[](len); for (uint256 i = 0; i < len; ++i) { require( chipPlugined[nft][chip][nftId].contains(chipIds[i]), "NftChipLocker: chip not exists" ); chipPlugined[nft][chip][nftId].remove(chipIds[i]); delete chipOwner[nft][chip][chipIds[i]]; amounts[i] = 1; } IERC1155(chip).safeBatchTransferFrom(address(this), msg.sender, chipIds, amounts, ""); emit ChipUnplug(nft, nftId, chip, 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) { return chipPlugined[nft][chip][tokenId].length(); } function pluginedChips(address nft, address chip, uint256 tokenId) external view returns(uint256[] memory) { return chipPlugined[nft][chip][tokenId].values(); } }