aere-contracts/contracts/AereNFT.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

107 lines
3.6 KiB
Solidity

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