{"sources":{"Mihao.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 Mihao — 2,999 orange cats with the two legendary whisker strands, on BNB Smart Chain.\n/// @notice Two mint lanes, open SIMULTANEOUSLY from openMint() and sharing one supply:\n///         - FREE lane: Binance Agent ID holders only (same gate as NULLSECTOR / PERSONA /\n///           SYNTH). Max 1 free per wallet, ever. Globally rate-limited FCFS: at most one\n///           free mint per 1-second slot (block.timestamp based). No tranche cap — the free\n///           lane runs until the window ends or the shared supply sells out.\n///         - PAID lane: PUBLIC, no Agent ID needed. Exactly 0.0003 BNB per unit,\n///           max 10 paid per wallet, instant (no slot).\n///         Free stacks outside paid: a wallet can hold at most 1 free + 10 paid = 11.\n///         Royalties: ERC-2981, 5% (500 bps) to the royalty receiver (the deployer;\n///         owner-adjustable) so marketplaces like Element read them on-chain.\n/// @dev The rules are constants — they cannot be changed by anyone, owner included:\n///      - The mint window ENDS 10 DAYS after openMint() (mintEnd = mintStart + 10 days);\n///        both lanes reject after mintEnd. openMint() can be called exactly once.\n///      - No royalty (no ERC-2981). Paid proceeds are withdrawable by the owner only.\n///      Token ids are sequential from 1. Instant reveal via baseURI.\ncontract Mihao {\n    string public name = \"Mihao\";\n    string public symbol = \"MIHAO\";\n\n    uint256 public constant MAX_SUPPLY = 2999;\n    uint256 public constant MAX_FREE_PER_WALLET = 1;\n    uint256 public constant SLOT_SECONDS = 1;\n    uint256 public constant PRICE = 0.0003 ether; // 300000000000000 wei — FIXED, no setter\n    uint256 public constant MAX_PAID_PER_WALLET = 10;\n    uint256 public constant MINT_DURATION = 10 days;\n    uint256 public constant ROYALTY_BPS = 500; // 5% ERC-2981 royalty\n\n    /// @notice Binance Agent ID registry on BSC — copied exactly from the\n    /// verified NULLSECTOR / SYNTH contracts.\n    IAgentID public constant AGENT_ID = IAgentID(0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F);\n\n    address public owner;\n    address public royaltyReceiver; // ERC-2981 receiver — the deployer at launch (farm1)\n    string public baseURI = \"https://mihao-6zg.pages.dev/metadata/\";\n    bool public mintOpen;\n    uint256 public mintStart; // block.timestamp of openMint()\n    uint256 public mintEnd; // mintStart + MINT_DURATION; both lanes reject after this\n    uint256 public totalSupply;\n\n    uint256 public freeCount; // free mints so far (global)\n    uint256 public lastFreeSlot; // last consumed free-mint slot (block.timestamp / SLOT_SECONDS)\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();\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        royaltyReceiver = msg.sender;\n        emit OwnershipTransferred(address(0), msg.sender);\n    }\n\n    // --- Admin ---\n    /// @notice Opens BOTH mint lanes exactly once and starts the 10-day window:\n    /// mintEnd = mintStart + 10 days. The window can never be extended or reopened.\n    function openMint() external onlyOwner {\n        require(!mintOpen, \"already opened\");\n        mintOpen = true;\n        mintStart = block.timestamp;\n        mintEnd = block.timestamp + MINT_DURATION;\n        emit MintOpened();\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    /// @notice Withdraw the paid-lane proceeds (free mints bring in no funds).\n    function withdraw() external onlyOwner {\n        (bool ok, ) = payable(owner).call{value: address(this).balance}(\"\");\n        require(ok, \"withdraw failed\");\n    }\n\n    function setRoyaltyReceiver(address receiver) external onlyOwner {\n        require(receiver != address(0), \"zero address\");\n        royaltyReceiver = receiver;\n    }\n\n    // --- ERC-2981 royalties (read on-chain by Element and other marketplaces) ---\n    /// @notice 5% of any sale price goes to the royalty receiver (the deployer,\n    /// farm1, at launch), for every token id.\n    function royaltyInfo(uint256, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount) {\n        return (royaltyReceiver, (salePrice * ROYALTY_BPS) / 10000);\n    }\n\n    // --- Public mint ---\n    /// @notice Free mint — Agent ID holders only, 1 per wallet ever, globally\n    /// FCFS: at most one free mint per 1-second slot. No tranche cap.\n    function mintFree() external {\n        require(mintOpen, \"mint not open\");\n        require(block.timestamp <= mintEnd, \"mint ended\");\n        if (AGENT_ID.balanceOf(msg.sender) == 0) revert NoAgentID();\n        require(freeMinted[msg.sender] < MAX_FREE_PER_WALLET, \"free cap\");\n        require(totalSupply < MAX_SUPPLY, \"sold out\");\n        uint256 slot = block.timestamp / SLOT_SECONDS;\n        require(slot > lastFreeSlot, \"slot taken\");\n        lastFreeSlot = slot;\n        freeMinted[msg.sender] += 1;\n        freeCount += 1;\n        totalSupply += 1;\n        _safeMint(msg.sender, totalSupply);\n        emit Minted(msg.sender, totalSupply, true);\n    }\n\n    /// @notice Paid mint — PUBLIC (no Agent ID needed), open from openMint()\n    /// simultaneously with the free lane, until sellout or mintEnd.\n    /// Exactly PRICE (0.0003 BNB) per unit, max 10 paid per wallet.\n    /// Independent of the free lane: free stacks outside paid.\n    function mintPaid(uint256 count) external payable {\n        require(mintOpen, \"mint not open\");\n        require(block.timestamp <= mintEnd, \"mint ended\");\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            totalSupply += 1;\n            _safeMint(msg.sender, totalSupply);\n            emit Minted(msg.sender, totalSupply, 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 (totalSupply >= MAX_SUPPLY) return 3; // sold out\n        if (block.timestamp > mintEnd) return 2; // window ended\n        return 1; // open — free + paid lanes live\n    }\n\n    /// @notice Front-end / agent helper: everything needed to mint in one call.\n    /// Read the fields BY NAME (order: open, supply, maxSupply, price, eligible,\n    /// globalFreeCount, callerFreeMinted, callerPaidMinted, callerPaidLeft,\n    /// nextFreeSlotAt, start, end).\n    function status(address a) external view returns (\n        bool open,\n        uint256 supply,\n        uint256 maxSupply,\n        uint256 price,\n        bool eligible,\n        uint256 globalFreeCount,\n        bool callerFreeMinted,\n        uint256 callerPaidMinted,\n        uint256 callerPaidLeft,\n        uint256 nextFreeSlotAt,\n        uint256 start,\n        uint256 end\n    ) {\n        open = mintOpen;\n        supply = totalSupply;\n        maxSupply = MAX_SUPPLY;\n        price = PRICE;\n        eligible = AGENT_ID.balanceOf(a) > 0;\n        globalFreeCount = freeCount;\n        callerFreeMinted = freeMinted[a] > 0;\n        callerPaidMinted = paidMinted[a];\n        callerPaidLeft = MAX_PAID_PER_WALLET - paidMinted[a];\n        nextFreeSlotAt = (lastFreeSlot + 1) * SLOT_SECONDS;\n        start = mintStart;\n        end = mintEnd;\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    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {\n        return interfaceId == 0x01ffc9a7 // ERC165\n            || interfaceId == 0x80ac58cd // ERC721\n            || interfaceId == 0x5b5e139f // ERC721Metadata\n            || interfaceId == 0x2a55205a; // ERC-2981\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":[],"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":"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":"MINT_DURATION","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":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"SLOT_SECONDS","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":"freeCount","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"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":"lastFreeSlot","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"mintEnd","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"mintFree","type":"function","inputs":[],"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":"mintStart","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"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":"receiver","type":"address","internalType":"address"},{"name":"royaltyAmount","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"royaltyReceiver","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"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":"setRoyaltyReceiver","type":"function","inputs":[{"name":"receiver","type":"address","internalType":"address"}],"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":"eligible","type":"bool","internalType":"bool"},{"name":"globalFreeCount","type":"uint256","internalType":"uint256"},{"name":"callerFreeMinted","type":"bool","internalType":"bool"},{"name":"callerPaidMinted","type":"uint256","internalType":"uint256"},{"name":"callerPaidLeft","type":"uint256","internalType":"uint256"},{"name":"nextFreeSlotAt","type":"uint256","internalType":"uint256"},{"name":"start","type":"uint256","internalType":"uint256"},{"name":"end","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":"59204187","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-13T07:14:54Z","match":"exact_match","chainId":"56","address":"0xEa4EF9DA567F59ea7b5f5B7E08305F4e613bab73"}