191 lines
5.9 KiB
Solidity
191 lines
5.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.10;
|
|
|
|
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
|
|
contract BELuckyBox is ERC1155, AccessControl {
|
|
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");
|
|
|
|
struct TokenStruct {
|
|
uint256 tokenId;
|
|
uint256 amount;
|
|
}
|
|
|
|
mapping(address => uint256[]) private _ownedTokens;
|
|
mapping(address => mapping(uint256 => uint256)) private _ownedTokensIndex;
|
|
|
|
mapping(uint256 => uint256) private _totalSupply;
|
|
|
|
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 mint(address account, uint256 id, uint256 amount, bytes memory data)
|
|
external
|
|
onlyRole(MINTER_ROLE)
|
|
{
|
|
_mint(account, id, amount, data);
|
|
}
|
|
|
|
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
|
|
external
|
|
onlyRole(MINTER_ROLE)
|
|
{
|
|
_mintBatch(to, ids, amounts, data);
|
|
}
|
|
|
|
function burn(
|
|
address account,
|
|
uint256 id,
|
|
uint256 value
|
|
) external onlyRole(BURN_ROLE) {
|
|
_burn(account, id, value);
|
|
}
|
|
|
|
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 onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
_grantRole(MINTER_ROLE, factory);
|
|
_grantRole(BURN_ROLE, factory);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove factory
|
|
*/
|
|
function removeMintFactory(address factory) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
_revokeRole(MINTER_ROLE, factory);
|
|
_revokeRole(BURN_ROLE, factory);
|
|
}
|
|
|
|
/**
|
|
* @dev Total amount of tokens in with a given id.
|
|
*/
|
|
function totalSupply(uint256 id) public 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 BELuckyBox.totalSupply(id) > 0;
|
|
}
|
|
|
|
/**
|
|
* @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);
|
|
|
|
if (from == address(0)) {
|
|
// mint nft
|
|
for (uint256 i = 0; i < ids.length; ++i) {
|
|
_totalSupply[ids[i]] += amounts[i];
|
|
}
|
|
} else if (from != to){
|
|
// transfer from -> to
|
|
|
|
for (uint256 i = 0; i < ids.length; ++i) {
|
|
_removeTokenFromOwnerEnumeration(from, ids[i], amounts[i]);
|
|
}
|
|
}
|
|
|
|
if (to == address(0)) {
|
|
// burn nft
|
|
for (uint256 i = 0; i < ids.length; ++i) {
|
|
_totalSupply[ids[i]] -= amounts[i];
|
|
}
|
|
|
|
} else if (to != from) {
|
|
// mint or transfer from -> to
|
|
for (uint256 i = 0; i < ids.length; ++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;
|
|
_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];
|
|
|
|
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
|
|
|
|
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
|
|
_ownedTokensIndex[from][lastTokenId] = tokenIndex; // Update the moved token's index
|
|
|
|
// 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;
|
|
TokenStruct[] memory results = new TokenStruct[](page);
|
|
if (start < size) {
|
|
uint256 max = size;
|
|
if (start + page < size) {
|
|
max = start + page;
|
|
}
|
|
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;
|
|
}
|
|
}
|