contract NFT is
  AccessControl,
  ERC2981,
  ERC721Enumerable,
  ERC721Burnable,
  ERC721Pausable
{
  event RoyaltyWalletChanged(
    address indexed previousWallet,
    address indexed newWallet
  );
  event RoyaltyFeeChanged(uint256 previousFee, uint256 newFee);
  event BaseURIChanged(string previousURI, string newURI);

  bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

  uint256 public constant ROYALTY_FEE_DENOMINATOR = 100000;
  uint256 public royaltyFee;
  address public royaltyWallet;

  string private _baseTokenURI;

  /**
   * @param _name ERC721 token name
   * @param _symbol ERC721 token symbol
   * @param _uri Base token uri
   * @param _royaltyWallet Wallet where royalties should be sent
   * @param _royaltyFee Fee numerator to be used for fees
   */
  constructor(
    string memory _name,
    string memory _symbol,
    string memory _uri,
    address _royaltyWallet,
    uint256 _royaltyFee
  ) ERC721(_name, _symbol) {
    _setBaseTokenURI(_uri);
    _setRoyaltyWallet(_royaltyWallet);
    _setRoyaltyFee(_royaltyFee);
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _setupRole(OWNER_ROLE, msg.sender);
    _setupRole(MINTER_ROLE, msg.sender);
  }

  /**
   * @dev Throws if called by any account other than owners. Implemented using the underlying AccessControl methods.
   */
  modifier onlyOwners() {
    require(
      hasRole(OWNER_ROLE, _msgSender()),
      "Caller does not have the OWNER_ROLE"
    );
    _;
  }

  /**
   * @dev Throws if called by any account other than minters. Implemented using the underlying AccessControl methods.
   */
  modifier onlyMinters() {
    require(
      hasRole(MINTER_ROLE, _msgSender()),
      "Caller does not have the MINTER_ROLE"
    );
    _;
  }

  /**
   * @dev Mints the specified token ids to the recipient addresses
   * @param recipient Address that will receive the tokens
   * @param tokenIds Array of tokenIds to be minted
   */
  function mint(address recipient, uint256[] calldata tokenIds)
    external
    onlyMinters
  {
    for (uint256 i = 0; i < tokenIds.length; i++) {
      _mint(recipient, tokenIds[i]);
    }
  }

  /**
   * @dev Mints the specified token id to the recipient addresses
   * @dev The unused string parameter exists to support the API used by ChainBridge.
   * @param recipient Address that will receive the tokens
   * @param tokenId tokenId to be minted
   */
  function mint(
    address recipient,
    uint256 tokenId,
    string calldata
  ) external onlyMinters {
    _mint(recipient, tokenId);
  }

  /**
   * @dev Pauses token transfers
   */
  function pause() external onlyOwners {
    _pause();
  }

  /**
   * @dev Unpauses token transfers
   */
  function unpause() external onlyOwners {
    _unpause();
  }

  /**
   * @dev Sets the base token URI
   * @param uri Base token URI
   */
  function setBaseTokenURI(string calldata uri) external onlyOwners {
    _setBaseTokenURI(uri);
  }

  /**
   * @dev Sets the wallet to which royalties should be sent
   * @param _royaltyWallet Address that should receive the royalties
   */
  function setRoyaltyWallet(address _royaltyWallet) external onlyOwners {
    _setRoyaltyWallet(_royaltyWallet);
  }

  /**
   * @dev Sets the fee percentage for royalties
   * @param _royaltyFee Basis points to compute royalty percentage
   */
  function setRoyaltyFee(uint256 _royaltyFee) external onlyOwners {
    _setRoyaltyFee(_royaltyFee);
  }

  /**
   * @dev Function defined by ERC2981, which provides information about fees.
   * @param value Price being paid for the token (in base units)
   */
  function royaltyInfo(
    uint256, // tokenId is not used in this case as all tokens take the same fee
    uint256 value
  )
    external
    view
    override
    returns (
      address, // receiver
      uint256 // royaltyAmount
    )
  {
    return (royaltyWallet, (value * royaltyFee) / ROYALTY_FEE_DENOMINATOR);
  }

  /**
   * @dev For each existing tokenId, it returns the URI where metadata is stored
   * @param tokenId Token id
   */
  function tokenURI(uint256 tokenId)
    public
    view
    override
    returns (string memory)
  {
    string memory uri = super.tokenURI(tokenId);
    return bytes(uri).length > 0 ? string(abi.encodePacked(uri, ".json")) : "";
  }

  function supportsInterface(bytes4 interfaceId)
    public
    view
    override(AccessControl, ERC2981, ERC721, ERC721Enumerable)
    returns (bool)
  {
    return super.supportsInterface(interfaceId);
  }

  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal override(ERC721, ERC721Enumerable, ERC721Pausable) {
    super._beforeTokenTransfer(from, to, tokenId);
  }

  function _setBaseTokenURI(string memory newURI) internal {
    emit BaseURIChanged(_baseTokenURI, newURI);
    _baseTokenURI = newURI;
  }

  function _setRoyaltyWallet(address _royaltyWallet) internal {
    require(_royaltyWallet != address(0), "INVALID_WALLET");
    emit RoyaltyWalletChanged(royaltyWallet, _royaltyWallet);
    royaltyWallet = _royaltyWallet;
  }

  function _setRoyaltyFee(uint256 _royaltyFee) internal {
    require(_royaltyFee <= ROYALTY_FEE_DENOMINATOR, "INVALID_FEE");
    emit RoyaltyFeeChanged(royaltyFee, _royaltyFee);
    royaltyFee = _royaltyFee;
  }

  function _baseURI() internal view override returns (string memory) {
    return _baseTokenURI;
  }
}
function mint(
  address recipient,
  uint256 tokenId,
  string calldata
) external onlyMinters {
  _mint(recipient, tokenId);
}

/**
 * @dev Mints the specified token id to the recipient addresses
 * @dev The unused string parameter exists to support the API used by ChainBridge.
 * @param recipient Address that will receive the tokens
 * @param tokenId tokenId to be minted
 */