becrypto/contracts/tokens/erc1155/BEBase1155.sol
2022-08-18 10:54:42 +08:00

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;
}
}