{"sources":{"PersonaNFT.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\ninterface IAgentID {\n    function balanceOf(address owner) external view returns (uint256);\n}\n\n/// @title PERSONA — 3,333 human portrait units on BNB Smart Chain.\n/// @notice Mint is open to Binance Agent ID holders only (same gate as NULLSECTOR).\n/// @dev The rules are constants — they cannot be changed by anyone, owner included:\n///      - FREE phase: while totalSupply < 1,000, mints are free,\n///        max 2 free per wallet.\n///      - PAID phase: the remaining 2,333 units at exactly 0.00012 BNB,\n///        max 5 paid per wallet. The paid phase opens only once the free\n///        tranche is exhausted.\n///      - Mint window: 10 days from openMint(). After mintEndsAt (or sellout)\n///        no unit can ever be minted again; unminted supply stays unminted.\n///      Token id = mint order, sequential from 1. Instant reveal via baseURI.\ncontract PersonaNFT {\n    string public name = \"PERSONA\";\n    string public symbol = \"PERSONA\";\n\n    uint256 public constant MAX_SUPPLY = 3333;\n    uint256 public constant FREE_SUPPLY = 1000;\n    uint256 public constant PRICE = 0.00012 ether; // 120000000000000 wei — FIXED, no setter\n    uint256 public constant MAX_FREE_PER_WALLET = 2;\n    uint256 public constant MAX_PAID_PER_WALLET = 5;\n    uint256 public constant WINDOW = 10 days;\n    uint96 public constant ROYALTY_BPS = 500; // 5%\n\n    /// @notice Binance Agent ID registry on BSC — copied exactly from the\n    /// verified NULLSECTOR contract (0x9395e207F9928657237DE96ffB12340094c073cd).\n    IAgentID public constant AGENT_ID = IAgentID(0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F);\n\n    address public owner;\n    string public baseURI;\n    bool public mintOpen;\n    uint256 public mintEndsAt; // 0 until openMint()\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public freeMinted;\n    mapping(address => uint256) public paidMinted;\n\n    // --- ERC721 storage ---\n    mapping(uint256 => address) private _owners;\n    mapping(address => uint256) private _balances;\n    mapping(uint256 => address) private _tokenApprovals;\n    mapping(address => mapping(address => bool)) private _operatorApprovals;\n\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n    event Approval(address indexed owner_, address indexed approved, uint256 indexed tokenId);\n    event ApprovalForAll(address indexed owner_, address indexed operator, bool approved);\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n    event MintOpened(uint256 endsAt);\n    event Minted(address indexed to, uint256 indexed tokenId, bool free);\n\n    error NoAgentID();\n\n    modifier onlyOwner() {\n        require(msg.sender == owner, \"not owner\");\n        _;\n    }\n\n    constructor() {\n        owner = msg.sender;\n        emit OwnershipTransferred(address(0), msg.sender);\n    }\n\n    // --- Admin ---\n    /// @notice Opens the mint exactly once. The window closes 10 days later\n    /// and can never be reopened or extended.\n    function openMint() external onlyOwner {\n        require(!mintOpen, \"already opened\");\n        mintOpen = true;\n        mintEndsAt = block.timestamp + WINDOW;\n        emit MintOpened(mintEndsAt);\n    }\n\n    function setBaseURI(string calldata uri) external onlyOwner {\n        baseURI = uri;\n    }\n\n    function transferOwnership(address newOwner) external onlyOwner {\n        require(newOwner != address(0), \"zero address\");\n        emit OwnershipTransferred(owner, newOwner);\n        owner = newOwner;\n    }\n\n    function withdraw() external onlyOwner {\n        (bool ok, ) = payable(owner).call{value: address(this).balance}(\"\");\n        require(ok, \"withdraw failed\");\n    }\n\n    // --- Mint ---\n    function _mintOpen() internal view returns (bool) {\n        return mintOpen && block.timestamp < mintEndsAt;\n    }\n\n    /// @notice Free mint — only while totalSupply < FREE_SUPPLY (first 1,000).\n    /// Max 2 free per wallet, counted separately from paid mints.\n    function mintFree(uint256 count) external {\n        require(_mintOpen(), \"mint not open\");\n        if (AGENT_ID.balanceOf(msg.sender) == 0) revert NoAgentID();\n        require(count > 0, \"zero count\");\n        require(totalSupply + count <= FREE_SUPPLY, \"free tranche over\");\n        require(freeMinted[msg.sender] + count <= MAX_FREE_PER_WALLET, \"free cap\");\n        freeMinted[msg.sender] += count;\n        for (uint256 i = 0; i < count; i++) {\n            uint256 id = totalSupply + 1;\n            _safeMint(msg.sender, id);\n            totalSupply += 1;\n            emit Minted(msg.sender, id, true);\n        }\n    }\n\n    /// @notice Paid mint — only after the free tranche is exhausted.\n    /// Exactly PRICE (0.00012 BNB) per unit, max 5 paid per wallet.\n    function mintPaid(uint256 count) external payable {\n        require(_mintOpen(), \"mint not open\");\n        if (AGENT_ID.balanceOf(msg.sender) == 0) revert NoAgentID();\n        require(totalSupply >= FREE_SUPPLY, \"paid phase not open\");\n        require(count > 0 && paidMinted[msg.sender] + count <= MAX_PAID_PER_WALLET, \"paid cap\");\n        require(totalSupply + count <= MAX_SUPPLY, \"sold out\");\n        require(msg.value == PRICE * count, \"wrong value\");\n        paidMinted[msg.sender] += count;\n        for (uint256 i = 0; i < count; i++) {\n            uint256 id = totalSupply + 1;\n            _safeMint(msg.sender, id);\n            totalSupply += 1;\n            emit Minted(msg.sender, id, false);\n        }\n    }\n\n    // --- Views for the mint page ---\n    function phase() external view returns (uint8) {\n        if (!mintOpen) return 0; // not opened\n        if (block.timestamp >= mintEndsAt || totalSupply >= MAX_SUPPLY) return 3; // closed\n        if (totalSupply < FREE_SUPPLY) return 1; // free\n        return 2; // paid\n    }\n\n    /// @notice Front-end / agent helper: everything needed to mint in one call.\n    function status(address a) external view returns (\n        bool open,\n        uint256 supply,\n        uint256 maxSupply,\n        uint256 price,\n        uint256 endsAt,\n        bool eligible,\n        uint256 freeCount,\n        uint256 freeLeft,\n        uint256 paidCount,\n        uint256 paidLeft\n    ) {\n        uint256 fc = freeMinted[a];\n        uint256 pc = paidMinted[a];\n        open = _mintOpen();\n        supply = totalSupply;\n        maxSupply = MAX_SUPPLY;\n        price = PRICE;\n        endsAt = mintEndsAt;\n        eligible = AGENT_ID.balanceOf(a) > 0;\n        freeCount = fc;\n        freeLeft = MAX_FREE_PER_WALLET - fc;\n        paidCount = pc;\n        paidLeft = MAX_PAID_PER_WALLET - pc;\n    }\n\n    // --- ERC721 ---\n    function balanceOf(address account) external view returns (uint256) {\n        require(account != address(0), \"zero address\");\n        return _balances[account];\n    }\n\n    function ownerOf(uint256 tokenId) public view returns (address) {\n        address o = _owners[tokenId];\n        require(o != address(0), \"nonexistent token\");\n        return o;\n    }\n\n    function tokenURI(uint256 tokenId) external view returns (string memory) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return string(abi.encodePacked(baseURI, _toString(tokenId), \".json\"));\n    }\n\n    function approve(address to, uint256 tokenId) external {\n        address o = ownerOf(tokenId);\n        require(msg.sender == o || _operatorApprovals[o][msg.sender], \"not authorized\");\n        _tokenApprovals[tokenId] = to;\n        emit Approval(o, to, tokenId);\n    }\n\n    function getApproved(uint256 tokenId) external view returns (address) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return _tokenApprovals[tokenId];\n    }\n\n    function setApprovalForAll(address operator, bool approved) external {\n        _operatorApprovals[msg.sender][operator] = approved;\n        emit ApprovalForAll(msg.sender, operator, approved);\n    }\n\n    function isApprovedForAll(address account, address operator) external view returns (bool) {\n        return _operatorApprovals[account][operator];\n    }\n\n    function transferFrom(address from, address to, uint256 tokenId) public {\n        require(_isAuthorized(msg.sender, tokenId), \"not authorized\");\n        require(ownerOf(tokenId) == from, \"wrong from\");\n        require(to != address(0), \"zero address\");\n        delete _tokenApprovals[tokenId];\n        _balances[from] -= 1;\n        _balances[to] += 1;\n        _owners[tokenId] = to;\n        emit Transfer(from, to, tokenId);\n    }\n\n    function safeTransferFrom(address from, address to, uint256 tokenId) external {\n        safeTransferFrom(from, to, tokenId, \"\");\n    }\n\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {\n        transferFrom(from, to, tokenId);\n        require(_checkOnERC721Received(from, to, tokenId, data), \"unsafe recipient\");\n    }\n\n    function _isAuthorized(address spender, uint256 tokenId) internal view returns (bool) {\n        address o = _owners[tokenId];\n        return spender == o || _tokenApprovals[tokenId] == spender || _operatorApprovals[o][spender];\n    }\n\n    function _safeMint(address to, uint256 tokenId) internal {\n        require(to != address(0), \"zero address\");\n        require(_owners[tokenId] == address(0), \"already minted\");\n        _balances[to] += 1;\n        _owners[tokenId] = to;\n        emit Transfer(address(0), to, tokenId);\n        require(_checkOnERC721Received(address(0), to, tokenId, \"\"), \"unsafe recipient\");\n    }\n\n    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private returns (bool) {\n        if (to.code.length == 0) return true;\n        try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {\n            return retval == IERC721Receiver.onERC721Received.selector;\n        } catch {\n            return false;\n        }\n    }\n\n    // --- ERC2981 ---\n    function royaltyInfo(uint256, uint256 salePrice) external view returns (address, uint256) {\n        return (owner, (salePrice * ROYALTY_BPS) / 10000);\n    }\n\n    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {\n        return interfaceId == 0x01ffc9a7 // ERC165\n            || interfaceId == 0x80ac58cd // ERC721\n            || interfaceId == 0x5b5e139f // ERC721Metadata\n            || interfaceId == 0x2a55205a; // ERC2981\n    }\n\n    function _toString(uint256 value) internal pure returns (string memory) {\n        if (value == 0) return \"0\";\n        uint256 temp = value;\n        uint256 digits;\n        while (temp != 0) { digits++; temp /= 10; }\n        bytes memory buffer = new bytes(digits);\n        while (value != 0) {\n            digits -= 1;\n            buffer[digits] = bytes1(uint8(48 + (value % 10)));\n            value /= 10;\n        }\n        return string(buffer);\n    }\n}\n\ninterface IERC721Receiver {\n    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);\n}\n"}},"abi":[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"},{"name":"NoAgentID","type":"error","inputs":[]},{"name":"Approval","type":"event","inputs":[{"name":"owner_","type":"address","indexed":true,"internalType":"address"},{"name":"approved","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"name":"ApprovalForAll","type":"event","inputs":[{"name":"owner_","type":"address","indexed":true,"internalType":"address"},{"name":"operator","type":"address","indexed":true,"internalType":"address"},{"name":"approved","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"name":"MintOpened","type":"event","inputs":[{"name":"endsAt","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"Minted","type":"event","inputs":[{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"free","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"name":"OwnershipTransferred","type":"event","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"name":"Transfer","type":"event","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"name":"AGENT_ID","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAgentID"}],"stateMutability":"view"},{"name":"FREE_SUPPLY","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_FREE_PER_WALLET","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_PAID_PER_WALLET","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_SUPPLY","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"PRICE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"ROYALTY_BPS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint96","internalType":"uint96"}],"stateMutability":"view"},{"name":"WINDOW","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"approve","type":"function","inputs":[{"name":"to","type":"address","internalType":"address"},{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"balanceOf","type":"function","inputs":[{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"baseURI","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"freeMinted","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"getApproved","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"isApprovedForAll","type":"function","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"operator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"mintEndsAt","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"mintFree","type":"function","inputs":[{"name":"count","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"mintOpen","type":"function","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"mintPaid","type":"function","inputs":[{"name":"count","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"name":"name","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"openMint","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"owner","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"ownerOf","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"paidMinted","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"phase","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"royaltyInfo","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"},{"name":"salePrice","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"safeTransferFrom","type":"function","inputs":[{"name":"from","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"safeTransferFrom","type":"function","inputs":[{"name":"from","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"tokenId","type":"uint256","internalType":"uint256"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setApprovalForAll","type":"function","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"approved","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setBaseURI","type":"function","inputs":[{"name":"uri","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"status","type":"function","inputs":[{"name":"a","type":"address","internalType":"address"}],"outputs":[{"name":"open","type":"bool","internalType":"bool"},{"name":"supply","type":"uint256","internalType":"uint256"},{"name":"maxSupply","type":"uint256","internalType":"uint256"},{"name":"price","type":"uint256","internalType":"uint256"},{"name":"endsAt","type":"uint256","internalType":"uint256"},{"name":"eligible","type":"bool","internalType":"bool"},{"name":"freeCount","type":"uint256","internalType":"uint256"},{"name":"freeLeft","type":"uint256","internalType":"uint256"},{"name":"paidCount","type":"uint256","internalType":"uint256"},{"name":"paidLeft","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"supportsInterface","type":"function","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"pure"},{"name":"symbol","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"tokenURI","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"totalSupply","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"transferFrom","type":"function","inputs":[{"name":"from","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"transferOwnership","type":"function","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"withdraw","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"matchId":"58395402","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-04T18:20:37Z","match":"exact_match","chainId":"56","address":"0xA930C4466431B0331C911846f297D7D4e063249D"}