147 lines
3.7 KiB
Solidity
147 lines
3.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.10;
|
|
|
|
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
|
import "@openzeppelin/contracts/utils/Counters.sol";
|
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
import "../../interfaces/IMetaData.sol";
|
|
|
|
contract NFTSbt is AccessControl, ERC721Enumerable {
|
|
using Counters for Counters.Counter;
|
|
address private _metaAddress;
|
|
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
|
uint256 public immutable supplyLimit;
|
|
|
|
Counters.Counter private _tokenIdCounter;
|
|
|
|
uint256 public maxBatchSize = 500;
|
|
|
|
constructor(
|
|
string memory _name,
|
|
string memory _symbol,
|
|
uint256 _supplyLimt
|
|
) ERC721(_name, _symbol) {
|
|
supplyLimit = _supplyLimt;
|
|
_setRoleAdmin(BURN_ROLE, DEFAULT_ADMIN_ROLE);
|
|
_setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
|
|
|
|
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
|
_setupRole(MINTER_ROLE, msg.sender);
|
|
_setupRole(BURN_ROLE, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev See {IERC165-supportsInterface}.
|
|
*/
|
|
function supportsInterface(
|
|
bytes4 interfaceId
|
|
)
|
|
public
|
|
view
|
|
virtual
|
|
override(AccessControl, ERC721Enumerable)
|
|
returns (bool)
|
|
{
|
|
return super.supportsInterface(interfaceId);
|
|
}
|
|
|
|
function _beforeTokenTransfer(
|
|
address from,
|
|
address to,
|
|
uint256 firstTokenId,
|
|
uint256 batchSize
|
|
) internal virtual override {
|
|
require(from == address(0) || to == address(0), "Token not transferable");
|
|
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
|
|
}
|
|
|
|
/**
|
|
* @dev Set token URI
|
|
*/
|
|
function updateMetaAddress(
|
|
address metaAddress
|
|
) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
_metaAddress = metaAddress;
|
|
}
|
|
|
|
/**
|
|
* @dev one type badge has same tokenURI
|
|
*/
|
|
function tokenURI(
|
|
uint256 tokenId
|
|
) public view override returns (string memory) {
|
|
require(_exists(tokenId), "URI query for nonexistent token");
|
|
return IMetaData(_metaAddress).getMetaData(address(this), tokenId);
|
|
}
|
|
|
|
function batchMint(
|
|
address to,
|
|
uint256 count
|
|
) external onlyRole(MINTER_ROLE) returns (uint256[] memory) {
|
|
require(count > 0, "tokenIds too small");
|
|
require(count <= maxBatchSize, "tokenIds too many");
|
|
if (supplyLimit > 0) {
|
|
require(
|
|
(totalSupply() + count) <= supplyLimit,
|
|
"Exceed the total supply"
|
|
);
|
|
}
|
|
uint256[] memory tokenIds = new uint256[](count);
|
|
for (uint256 i = 0; i < count; i++) {
|
|
_tokenIdCounter.increment();
|
|
uint256 tokenId = _tokenIdCounter.current();
|
|
_safeMint(to, tokenId);
|
|
tokenIds[i] = tokenId;
|
|
}
|
|
return tokenIds;
|
|
}
|
|
|
|
function mint(address to) external onlyRole(MINTER_ROLE) returns (uint256) {
|
|
require(to != address(0), "Cannot mint to zero address");
|
|
if (supplyLimit > 0) {
|
|
require((totalSupply() + 1) <= supplyLimit, "Exceed the total supply");
|
|
}
|
|
_tokenIdCounter.increment();
|
|
uint256 tokenId = _tokenIdCounter.current();
|
|
_safeMint(to, tokenId);
|
|
return tokenId;
|
|
}
|
|
|
|
function burn(uint256 tokenId) external onlyRole(BURN_ROLE) {
|
|
require(
|
|
_isApprovedOrOwner(_msgSender(), tokenId),
|
|
"Caller is not owner nor approved"
|
|
);
|
|
_burn(tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Grant mint role to address
|
|
*/
|
|
function setMintRole(address to) external {
|
|
grantRole(MINTER_ROLE, to);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove mint role to address
|
|
*/
|
|
function removeMintRole(address to) external {
|
|
revokeRole(MINTER_ROLE, to);
|
|
}
|
|
|
|
/**
|
|
* @dev Grant burn role to address
|
|
*/
|
|
function setBurnRole(address to) external {
|
|
grantRole(BURN_ROLE, to);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove burn role to address
|
|
*/
|
|
function removeBurnRole(address to) external {
|
|
revokeRole(BURN_ROLE, to);
|
|
}
|
|
}
|