213 lines
7.2 KiB
Solidity
213 lines
7.2 KiB
Solidity
|
|
// 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[2] [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[2] [nftId, chip]
|
|
// 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);
|
|
}
|
|
}
|