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