// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/interfaces/IERC2981.sol"; /** * @title AereNFT * @notice Native ERC-721 collection for AERE Network with on-chain royalties (EIP-2981). * @dev Permissionless minting: anyone can mint by paying the mint fee in native AERE. * Per-token royalty (creator-set, capped at 10%) flows to the original minter. * Designed to work with AereNFTMarketplace, but conforms to EIP-2981 for any market. */ contract AereNFT is ERC721URIStorage, ERC721Enumerable, Ownable, IERC2981 { uint96 public constant MAX_ROYALTY_BPS = 1000; // 10% uint256 public mintFee = 0.01 ether; // 0.01 AERE address public feeRecipient; uint256 private _nextId = 1; struct RoyaltyInfo { address receiver; uint96 royaltyBps; } mapping(uint256 => RoyaltyInfo) private _royalties; mapping(uint256 => address) public minterOf; event Minted(uint256 indexed tokenId, address indexed minter, string uri, uint96 royaltyBps); event MintFeeUpdated(uint256 newFee); event FeeRecipientUpdated(address newRecipient); constructor(address _feeRecipient) ERC721("AERE NFT", "AERENFT") { feeRecipient = _feeRecipient; } function mint(string calldata uri, uint96 royaltyBps) external payable returns (uint256) { require(msg.value >= mintFee, "fee"); require(royaltyBps <= MAX_ROYALTY_BPS, "royalty too high"); uint256 id = _nextId++; _safeMint(msg.sender, id); _setTokenURI(id, uri); _royalties[id] = RoyaltyInfo(msg.sender, royaltyBps); minterOf[id] = msg.sender; if (msg.value > 0) { (bool ok, ) = feeRecipient.call{value: msg.value}(""); require(ok, "fee transfer"); } emit Minted(id, msg.sender, uri, royaltyBps); return id; } function setMintFee(uint256 newFee) external onlyOwner { mintFee = newFee; emit MintFeeUpdated(newFee); } function setFeeRecipient(address newRecipient) external onlyOwner { require(newRecipient != address(0), "zero"); feeRecipient = newRecipient; emit FeeRecipientUpdated(newRecipient); } // EIP-2981 function royaltyInfo(uint256 tokenId, uint256 salePrice) external view override returns (address receiver, uint256 royaltyAmount) { RoyaltyInfo memory r = _royalties[tokenId]; return (r.receiver, (salePrice * r.royaltyBps) / 10000); } // Required overrides for ERC721Enumerable + ERC721URIStorage function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal override(ERC721, ERC721Enumerable) { super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { super._burn(tokenId); } function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); } function supportsInterface(bytes4 interfaceId) public view override(ERC721Enumerable, ERC721URIStorage, IERC165) returns (bool) { return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId); } }