292 lines
7.9 KiB
Solidity
292 lines
7.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.10;
|
|
|
|
import "@openzeppelin/contracts/utils/Strings.sol";
|
|
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
|
|
abstract contract BEBase1155 is ERC1155, Ownable, AccessControl {
|
|
using Strings for uint256;
|
|
|
|
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
|
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
|
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
|
bytes32 public constant LOCK_ROLE = keccak256("LOCK_ROLE");
|
|
|
|
mapping(uint256 => bool) public lockedTokens;
|
|
|
|
mapping(address => uint256[]) private _ownedTokens;
|
|
// for 0 means not exists, value stored = index + 1;
|
|
mapping(address => mapping(uint256 => uint256)) private _ownedTokensIndex;
|
|
|
|
mapping(uint256 => uint256) private _totalSupply;
|
|
|
|
uint256 public maxSupply = 0;
|
|
|
|
struct TokenStruct {
|
|
uint256 tokenId;
|
|
uint256 amount;
|
|
}
|
|
|
|
constructor() ERC1155("https://market.cebg.games/api/nft/info/{id}") {
|
|
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
|
_grantRole(URI_SETTER_ROLE, msg.sender);
|
|
}
|
|
|
|
function setURI(string memory newuri) external onlyRole(URI_SETTER_ROLE) {
|
|
_setURI(newuri);
|
|
}
|
|
|
|
function mintBatch(
|
|
address to,
|
|
uint256[] memory ids,
|
|
uint256[] memory amounts,
|
|
bytes memory data
|
|
) external onlyRole(MINTER_ROLE) {
|
|
_mintBatch(to, ids, amounts, data);
|
|
}
|
|
|
|
function burnBatch(
|
|
address account,
|
|
uint256[] memory ids,
|
|
uint256[] memory values
|
|
) external onlyRole(BURN_ROLE) {
|
|
_burnBatch(account, ids, values);
|
|
}
|
|
|
|
// The following functions are overrides required by Solidity.
|
|
|
|
function supportsInterface(bytes4 interfaceId)
|
|
public
|
|
view
|
|
override(ERC1155, AccessControl)
|
|
returns (bool)
|
|
{
|
|
return super.supportsInterface(interfaceId);
|
|
}
|
|
|
|
/**
|
|
* @dev Add factory to mint/burn item
|
|
*/
|
|
function setMintFactory(address factory) external onlyOwner {
|
|
_grantRole(MINTER_ROLE, factory);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove factory
|
|
*/
|
|
function removeMintFactory(address factory) external onlyOwner {
|
|
_revokeRole(MINTER_ROLE, factory);
|
|
}
|
|
|
|
function grantLockRole(address account) external onlyOwner {
|
|
_grantRole(LOCK_ROLE, account);
|
|
}
|
|
|
|
function revokeLockRole(address account) external onlyOwner {
|
|
_revokeRole(LOCK_ROLE, account);
|
|
}
|
|
|
|
/**
|
|
* @dev Add factory to burn item
|
|
*/
|
|
function grantBurnRole(address proxy) external onlyOwner {
|
|
_grantRole(BURN_ROLE, proxy);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove proxy
|
|
*/
|
|
function revokeBurnRole(address proxy) external onlyOwner {
|
|
_revokeRole(BURN_ROLE, proxy);
|
|
}
|
|
|
|
/**
|
|
* @dev Total amount of tokens in with a given id.
|
|
*/
|
|
function totalSupply(uint256 id) external view virtual returns (uint256) {
|
|
return _totalSupply[id];
|
|
}
|
|
|
|
/**
|
|
* @dev Indicates whether any token exist with a given id, or not.
|
|
*/
|
|
function exists(uint256 id) public view virtual returns (bool) {
|
|
return _totalSupply[id] > 0;
|
|
}
|
|
|
|
function canMint(
|
|
uint256 /*id*/
|
|
) external view virtual returns (bool) {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @dev Lock token to use in game or for rental
|
|
*/
|
|
function lock(uint256 id) external onlyRole(LOCK_ROLE) {
|
|
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
|
require(!lockedTokens[id], "Token has already locked");
|
|
lockedTokens[id] = true;
|
|
}
|
|
|
|
function lockBatch(uint256[] memory ids) external onlyRole(LOCK_ROLE) {
|
|
uint256 len = ids.length;
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
uint256 id = ids[i];
|
|
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
|
require(!lockedTokens[id], "Token has already locked");
|
|
lockedTokens[id] = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Unlock token to use blockchain or sale on marketplace
|
|
*/
|
|
function unlock(uint256 id) external onlyRole(LOCK_ROLE) {
|
|
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
|
require(lockedTokens[id], "Token has already unlocked");
|
|
lockedTokens[id] = false;
|
|
}
|
|
|
|
function unlockBatch(uint256[] memory ids) external onlyRole(LOCK_ROLE) {
|
|
uint256 len = ids.length;
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
uint256 id = ids[i];
|
|
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
|
require(lockedTokens[id], "Token has already unlocked");
|
|
lockedTokens[id] = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Get lock status
|
|
*/
|
|
function isLocked(uint256 id) external view returns (bool) {
|
|
return lockedTokens[id];
|
|
}
|
|
|
|
function isLockedBatch(uint256[] memory ids)
|
|
external
|
|
view
|
|
returns (bool[] memory)
|
|
{
|
|
uint256 len = ids.length;
|
|
bool[] memory results = new bool[](len);
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
results[i] = lockedTokens[ids[i]];
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* @dev See {ERC1155-_beforeTokenTransfer}.
|
|
*/
|
|
function _beforeTokenTransfer(
|
|
address operator,
|
|
address from,
|
|
address to,
|
|
uint256[] memory ids,
|
|
uint256[] memory amounts,
|
|
bytes memory data
|
|
) internal virtual override {
|
|
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
|
|
uint256 len = ids.length;
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
require(!lockedTokens[ids[i]], "Can not transfer locked token");
|
|
}
|
|
if (from == address(0)) {
|
|
// mint nft
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
_totalSupply[ids[i]] += amounts[i];
|
|
if (maxSupply > 0) {
|
|
require(
|
|
_totalSupply[ids[i]] <= maxSupply,
|
|
"Can not mint for exceeds max supply"
|
|
);
|
|
}
|
|
}
|
|
} else if (from != to) {
|
|
// transfer from -> to
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
_removeTokenFromOwnerEnumeration(from, ids[i], amounts[i]);
|
|
}
|
|
}
|
|
|
|
if (to == address(0)) {
|
|
// burn nft
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
_totalSupply[ids[i]] -= amounts[i];
|
|
}
|
|
} else if (to != from) {
|
|
// mint or transfer from -> to
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
_addTokenToOwnerEnumeration(to, ids[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
|
|
if (_ownedTokensIndex[to][tokenId] == 0 && balanceOf(to, tokenId) == 0) {
|
|
_ownedTokensIndex[to][tokenId] = _ownedTokens[to].length + 1;
|
|
_ownedTokens[to].push(tokenId);
|
|
}
|
|
}
|
|
|
|
function _removeTokenFromOwnerEnumeration(
|
|
address from,
|
|
uint256 tokenId,
|
|
uint256 amount
|
|
) private {
|
|
uint256 balance = balanceOf(from, tokenId);
|
|
if (balance == amount) {
|
|
uint256 lastTokenIndex = _ownedTokens[from].length - 1;
|
|
uint256 tokenIndex = _ownedTokensIndex[from][tokenId] - 1;
|
|
|
|
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
|
|
// Move the last token to the slot of the to-delete token
|
|
_ownedTokens[from][tokenIndex] = lastTokenId;
|
|
// Update the moved token's index
|
|
_ownedTokensIndex[from][lastTokenId] = tokenIndex + 1;
|
|
|
|
// This also deletes the contents at the last position of the array
|
|
delete _ownedTokensIndex[from][tokenId];
|
|
_ownedTokens[from].pop();
|
|
}
|
|
}
|
|
|
|
function userTokens(
|
|
address user,
|
|
uint256 start,
|
|
uint256 page
|
|
) external view returns (TokenStruct[] memory) {
|
|
uint256 size = _ownedTokens[user].length;
|
|
uint256 max = size;
|
|
uint256 pageSize = 0;
|
|
if (start < size) {
|
|
if (start + page < size) {
|
|
pageSize = page;
|
|
max = start + page;
|
|
} else {
|
|
pageSize = size - start;
|
|
}
|
|
}
|
|
TokenStruct[] memory results = new TokenStruct[](pageSize);
|
|
if (pageSize > 0) {
|
|
for (uint256 i = start; i < max; ++i) {
|
|
TokenStruct memory dataObj;
|
|
uint256 tokenId = _ownedTokens[user][i];
|
|
dataObj.tokenId = tokenId;
|
|
dataObj.amount = balanceOf(user, tokenId);
|
|
results[i - start] = dataObj;
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
function tokenTypes(address user) external view returns (uint256) {
|
|
return _ownedTokens[user].length;
|
|
}
|
|
}
|