175 lines
5.5 KiB
Solidity
175 lines
5.5 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.10;
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/utils/Strings.sol";
|
|
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
|
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
|
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
|
|
|
|
abstract contract BEBase is ERC721, AccessControlEnumerable, ERC721Enumerable, Ownable {
|
|
using Strings for uint256;
|
|
|
|
mapping(address => bool) public approvalWhitelists;
|
|
mapping(uint256 => bool) public lockedTokens;
|
|
string private _baseTokenURI;
|
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
|
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
|
bool public minter_factory_initialized = false;
|
|
bool public burn_proxy_initialized = false;
|
|
|
|
|
|
function _baseURI() internal view virtual override returns (string memory) {
|
|
return _baseTokenURI;
|
|
}
|
|
|
|
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
|
|
require(_exists(tokenId), "BEBase: URI query for nonexistent token");
|
|
|
|
string memory baseURI = _baseURI();
|
|
uint256 tokenSub = tokenId % 1000;
|
|
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenSub.toString(), '.png')) : "";
|
|
}
|
|
|
|
/**
|
|
* @dev Creates a new token for `to`. Its token ID will be automatically
|
|
* assigned (and available on the emitted {IERC721-Transfer} event), and the token
|
|
* URI autogenerated based on the base URI passed at construction.
|
|
*
|
|
* See {ERC721-_mint}.
|
|
*
|
|
* Requirements:
|
|
*
|
|
* - the caller must have the `MINTER_ROLE`.
|
|
*/
|
|
function mint(address to, uint256 tokenId) external virtual {
|
|
require(
|
|
hasRole(MINTER_ROLE, _msgSender()),
|
|
"Must have minter role to mint"
|
|
);
|
|
require(!_exists(tokenId), "Must have unique tokenId");
|
|
// We cannot just use balanceOf to create the new tokenId because tokens
|
|
// can be burned (destroyed), so we need a separate counter.
|
|
_mint(to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Allow operation to approval list.
|
|
*/
|
|
function addApprovalWhitelist(address proxy) external onlyOwner {
|
|
require(!approvalWhitelists[proxy], "Invalid proxy address");
|
|
|
|
approvalWhitelists[proxy] = true;
|
|
}
|
|
|
|
/**
|
|
* @dev Remove operation from approval list.
|
|
*/
|
|
function removeApprovalWhitelist(address proxy) external onlyOwner {
|
|
approvalWhitelists[proxy] = false;
|
|
}
|
|
|
|
/**
|
|
* @dev Add factory to mint item
|
|
*/
|
|
function setMintFactory(address factory) external onlyOwner {
|
|
require(!minter_factory_initialized, "BEBase::setMintFactory: can not change minter factory");
|
|
_setupRole(MINTER_ROLE, factory);
|
|
minter_factory_initialized = true;
|
|
}
|
|
|
|
/**
|
|
* @dev Lock token to use in game or for rental
|
|
*/
|
|
function lock(uint256 tokenId) external {
|
|
require(
|
|
approvalWhitelists[_msgSender()],
|
|
"Must be valid approval whitelist"
|
|
);
|
|
require(_exists(tokenId), "Must be valid tokenId");
|
|
require(!lockedTokens[tokenId], "Token has already locked");
|
|
lockedTokens[tokenId] = true;
|
|
}
|
|
|
|
/**
|
|
* @dev Unlock token to use blockchain or sale on marketplace
|
|
*/
|
|
function unlock(uint256 tokenId) external {
|
|
require(
|
|
approvalWhitelists[_msgSender()],
|
|
"Must be valid approval whitelist"
|
|
);
|
|
require(_exists(tokenId), "Must be valid tokenId");
|
|
require(lockedTokens[tokenId], "Token has already unlocked");
|
|
lockedTokens[tokenId] = false;
|
|
}
|
|
|
|
/**
|
|
* @dev Get lock status
|
|
*/
|
|
function isLocked(uint256 tokenId) external view returns (bool) {
|
|
return lockedTokens[tokenId];
|
|
}
|
|
|
|
/**
|
|
* @dev Set token URI
|
|
*/
|
|
function updateBaseURI(string calldata baseTokenURI) external onlyOwner {
|
|
_baseTokenURI = baseTokenURI;
|
|
}
|
|
|
|
/**
|
|
* @dev See {IERC165-_beforeTokenTransfer}.
|
|
*/
|
|
function _beforeTokenTransfer(
|
|
address from,
|
|
address to,
|
|
uint256 tokenId
|
|
) internal virtual override(ERC721, ERC721Enumerable) {
|
|
require(!lockedTokens[tokenId], "Can not transfer locked token");
|
|
super._beforeTokenTransfer(from, to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev See {IERC165-supportsInterface}.
|
|
*/
|
|
function supportsInterface(bytes4 interfaceId)
|
|
public
|
|
view
|
|
virtual
|
|
override(AccessControlEnumerable, ERC721, ERC721Enumerable)
|
|
returns (bool)
|
|
{
|
|
return super.supportsInterface(interfaceId);
|
|
}
|
|
|
|
|
|
/**
|
|
* @dev Burns `tokenId`.
|
|
*
|
|
* Requirements:
|
|
*
|
|
* - The caller must own `tokenId` or be an approved operator.
|
|
*/
|
|
function burn(address owner, uint256 tokenId) external virtual {
|
|
require(
|
|
hasRole(BURN_ROLE, _msgSender()),
|
|
"Must have burn role to burn"
|
|
);
|
|
require(_exists(tokenId), "TokenId not exists");
|
|
require(!lockedTokens[tokenId], "Can not burn locked token");
|
|
require(
|
|
ownerOf(tokenId) == owner,
|
|
"current address is not owner of this item now"
|
|
);
|
|
_burn(tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Add factory to burn item
|
|
*/
|
|
function setBurnProxy(address proxy) external onlyOwner {
|
|
require(!burn_proxy_initialized, "BEBase::setBurnProxy: can not change burn proxy");
|
|
_setupRole(BURN_ROLE, proxy);
|
|
burn_proxy_initialized = true;
|
|
}
|
|
} |