{"sources":{"contracts/DraftCollection.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n// ============================================================================\n//  DraftCollection.sol — \"AGENTIC\" — DRAFT LOKAL, BELUM DI-DEPLOY DI MANA PUN\n// ============================================================================\n//  Koleksi NFT \"Agentic\": robot AI agent bergaya Binance (kuning/hitam),\n//  mekanik bergaya Agentite:\n//    - Free mint 1x per wallet, khusus pemegang Agent ID (cek registry).\n//    - Paid mint setelah fase gratis habis.\n//    - Tier/rarity (Grade + Core) ditentukan on-chain saat mint.\n//\n//  File ini SENGAJA self-contained: implementasi ERC-721 minimal ditulis\n//  langsung di file ini (tanpa import OpenZeppelin) supaya bisa di-compile\n//  berdiri sendiri dengan solc/forge tanpa unduh dependency apa pun.\n//\n//  KEJUJURAN DESAIN (penting, jangan dihapus):\n//    - TIDAK ADA fungsi seize/ambil NFT orang lain (pola backdoor).\n//    - TIDAK ADA mint tersembunyi: satu-satunya jalan token lahir adalah\n//      mintFree() dan mint() di bawah, keduanya tercatat sebagai event.\n//    - Owner hanya bisa: ganti baseURI, ganti harga paid mint, atur royalti\n//      secondary ERC-2981 (maks 10%), dan tarik saldo BNB hasil paid mint\n//      (withdraw). Owner TIDAK bisa memindahkan NFT milik orang lain.\n//    - Pseudo-random dari blockhash TIDAK aman untuk nilai besar (miner /\n//      validator bisa memengaruhi). Cukup untuk draft/testnet & koleksi\n//      receh; untuk mainnet serius pertimbangkan VRF (mis. Chainlink).\n// ============================================================================\n\n/// @notice Interface registry Agent ID (di mainnet BSC alamat resminya\n///         0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F).\n///         Kontrak ini hanya memanggil balanceOf(address) — read-only.\ninterface IAgentIdRegistry {\n    function balanceOf(address account) external view returns (uint256);\n}\n\ncontract DraftCollection {\n    // ------------------------------------------------------------------\n    //  Konstanta koleksi — nama & simbol FINAL pilihan Claude (2026-11-09):\n    //  \"Agentic\" / AGTC (AGT sudah dipakai Agentite, jadi AGTC).\n    // ------------------------------------------------------------------\n    string public constant name = \"Agentic\";\n    string public constant symbol = \"AGTC\";\n\n    uint256 public constant MAX_SUPPLY = 3333; // total cap semua fase\n    uint256 public constant FREE_SUPPLY = 1000; // jatah fase gratis\n\n    // Harga paid mint default: 0.0008 BNB (bisa diganti owner via setPrice).\n    uint256 public constant DEFAULT_PAID_PRICE = 0.0008 ether;\n\n    // Royalti secondary ERC-2981: default 5% (500 bps), cap keras 10%\n    // (1000 bps) — owner bisa mengubahnya via setRoyaltyBps tapi tidak\n    // pernah di atas cap ini. Penerima royalti selalu = owner kontrak.\n    uint16 public royaltyBps = 500;\n    uint16 public constant MAX_ROYALTY_BPS = 1000;\n\n    // ------------------------------------------------------------------\n    //  State\n    // ------------------------------------------------------------------\n    address public owner; // deployer; satu-satunya alamat onlyOwner\n    IAgentIdRegistry public immutable registry; // gate Agent ID\n    string public baseURI; // awalan tokenURI, diakhiri \"/\"\n    uint256 public paidPrice = DEFAULT_PAID_PRICE;\n\n    uint256 public totalMinted; // semua token yang sudah lahir\n    uint256 public freeMinted; // token yang lahir dari jalur gratis\n\n    mapping(address => bool) public hasFreeMinted; // kuota free 1/wallet\n    mapping(uint256 => uint8) private _grades; // Grade per tokenId\n    mapping(uint256 => uint8) private _variants; // Core variant per tokenId\n\n    // --- state ERC-721 standar ---\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    // ------------------------------------------------------------------\n    //  Events (standar ERC-721 + event khusus koleksi ini)\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 Minted(address indexed minter, uint256 indexed tokenId, uint8 grade, uint8 variant, bool free);\n    event RoyaltyUpdated(uint16 bps);\n\n    // ------------------------------------------------------------------\n    //  Constructor\n    //  _registry : alamat registry Agent ID (mainnet: alamat resmi di atas;\n    //              testnet: alamat MockRegistry yang di-deploy sendiri).\n    //  _baseURI  : lokasi folder metadata, mis. \"https://example.com/meta/\"\n    //              sehingga tokenURI(17) = baseURI + \"17.json\".\n    // ------------------------------------------------------------------\n    constructor(address _registry, string memory _baseURI, address _initialOwner) {\n        require(_registry != address(0), \"registry = 0\");\n        require(_initialOwner != address(0), \"owner = 0\");\n        owner = _initialOwner;\n        registry = IAgentIdRegistry(_registry);\n        baseURI = _baseURI;\n    }\n\n    modifier onlyOwner() {\n        require(msg.sender == owner, \"not owner\");\n        _;\n    }\n\n    // ==================================================================\n    //  MINT — hanya ada dua jalan, keduanya publik & transparan\n    // ==================================================================\n\n    /// @notice Free mint: GRATIS (value harus 0), 1x per wallet,\n    ///         hanya untuk wallet yang memegang Agent ID (registry > 0),\n    ///         hanya selama freeMinted < FREE_SUPPLY.\n    ///         Grade di fase gratis DIBATASI Common / Uncommon (pola Agentite).\n    function mintFree() external {\n        require(!hasFreeMinted[msg.sender], \"free mint already used\");\n        require(freeMinted < FREE_SUPPLY, \"free supply exhausted\");\n        require(totalMinted < MAX_SUPPLY, \"sold out\");\n        require(registry.balanceOf(msg.sender) > 0, \"no Agent ID\");\n\n        hasFreeMinted[msg.sender] = true;\n        freeMinted += 1;\n        _mint(msg.sender, true);\n    }\n\n    /// @notice Paid mint: dibuka SETELAH fase gratis habis\n    ///         (freeMinted >= FREE_SUPPLY). Harga = paidPrice, dibayar pas\n    ///         (kelebihan dikembalikan). Grade memakai odds penuh.\n    function mint() external payable {\n        require(freeMinted >= FREE_SUPPLY, \"free phase still open\");\n        require(totalMinted < MAX_SUPPLY, \"sold out\");\n        require(msg.value >= paidPrice, \"underpaid\");\n\n        _mint(msg.sender, false);\n\n        // Kembalikan kelebihan pembayaran ke minter.\n        uint256 excess = msg.value - paidPrice;\n        if (excess > 0) {\n            (bool ok, ) = msg.sender.call{value: excess}(\"\");\n            require(ok, \"refund failed\");\n        }\n    }\n\n    /// @dev Inti mint: tokenId berikutnya = totalMinted + 1 (mulai dari 1),\n    ///      tentukan Grade + Variant, simpan, lalu catat kepemilikan.\n    function _mint(address to, bool free) internal {\n        uint256 tokenId = totalMinted + 1;\n        totalMinted = tokenId;\n\n        uint8 grade = free ? _rollFreeGrade(tokenId) : _rollGrade(tokenId);\n        uint8 variant = _rollVariant(tokenId);\n        _grades[tokenId] = grade;\n        _variants[tokenId] = variant;\n\n        _owners[tokenId] = to;\n        _balances[to] += 1;\n        emit Transfer(address(0), to, tokenId);\n        emit Minted(to, tokenId, grade, variant, free);\n    }\n\n    // ==================================================================\n    //  TIER / RARITY — tema Agentic (robot AI agent Binance)\n    //  Grade:   0 Common 50% | 1 Uncommon 25% | 2 Rare 14% | 3 Epic 7%\n    //           4 Legendary 3.5% | 5 Mythic 0.5%\n    //  Core:    0 Spark Core 40% | 1 Pulse Core 25% | 2 Neon Core 15%\n    //           3 Plasma Core 12% | 4 Solar Core 6% | 5 Cosmic Core 2%\n    //  (Core = sumber energi di dada robot; makin langka core-nya,\n    //   makin terang & kompleks robotnya)\n    // ==================================================================\n\n    /// @notice Grade on-chain milik token (0..5). Revert bila token belum ada.\n    function tokenGrade(uint256 tokenId) public view returns (uint8) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return _grades[tokenId];\n    }\n\n    /// @notice Core variant on-chain milik token (0..5). Revert bila token belum ada.\n    function tokenVariant(uint256 tokenId) public view returns (uint8) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return _variants[tokenId];\n    }\n\n    /// @notice Nama grade untuk tampilan (bantu frontend/metadata).\n    function gradeName(uint8 g) public pure returns (string memory) {\n        if (g == 0) return \"Common\";\n        if (g == 1) return \"Uncommon\";\n        if (g == 2) return \"Rare\";\n        if (g == 3) return \"Epic\";\n        if (g == 4) return \"Legendary\";\n        return \"Mythic\";\n    }\n\n    /// @notice Nama core variant untuk tampilan.\n    function variantName(uint8 v) public pure returns (string memory) {\n        if (v == 0) return \"Spark Core\";\n        if (v == 1) return \"Pulse Core\";\n        if (v == 2) return \"Neon Core\";\n        if (v == 3) return \"Plasma Core\";\n        if (v == 4) return \"Solar Core\";\n        return \"Cosmic Core\";\n    }\n\n    /// @dev Angka acak semu 0..9999 dari blockhash blok sebelumnya +\n    ///      minter + tokenId + salt (salt membedakan roll grade/variant).\n    ///      CUKUP untuk draft — bukan randomness yang aman dari manipulasi.\n    function _roll(uint256 tokenId, uint256 salt) internal view returns (uint256) {\n        bytes32 h = blockhash(block.number - 1);\n        return uint256(keccak256(abi.encodePacked(h, msg.sender, tokenId, salt))) % 10000;\n    }\n\n    /// @dev Odds penuh (dipakai paid mint): ambang per 10.000.\n    function _rollGrade(uint256 tokenId) internal view returns (uint8) {\n        uint256 r = _roll(tokenId, 1);\n        if (r < 5000) return 0; // Common    50%\n        if (r < 7500) return 1; // Uncommon  25%\n        if (r < 8900) return 2; // Rare      14%\n        if (r < 9600) return 3; // Epic       7%\n        if (r < 9950) return 4; // Legendary  3.5%\n        return 5; //               Mythic     0.5%\n    }\n\n    /// @dev Fase gratis: hanya Common/Uncommon, rasio tetap 50:25\n    ///      (roll 0..7499: <5000 Common, sisanya Uncommon).\n    function _rollFreeGrade(uint256 tokenId) internal view returns (uint8) {\n        uint256 r = _roll(tokenId, 1) % 7500;\n        return r < 5000 ? 0 : 1;\n    }\n\n    /// @dev Core variant, sumbu kedua independen dari grade (salt berbeda).\n    function _rollVariant(uint256 tokenId) internal view returns (uint8) {\n        uint256 r = _roll(tokenId, 2);\n        if (r < 4000) return 0; // Spark Core  40%\n        if (r < 6500) return 1; // Pulse Core  25%\n        if (r < 8000) return 2; // Neon Core   15%\n        if (r < 9200) return 3; // Plasma Core 12%\n        if (r < 9800) return 4; // Solar Core   6%\n        return 5; //               Cosmic Core  2%\n    }\n\n    // ==================================================================\n    //  METADATA\n    // ==================================================================\n\n    /// @notice tokenURI = baseURI + \"<tokenId>.json\" (pola Agentite persis).\n    function tokenURI(uint256 tokenId) public view returns (string memory) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return string(abi.encodePacked(baseURI, _toString(tokenId), \".json\"));\n    }\n\n    // ==================================================================\n    //  ROYALTI SECONDARY — ERC-2981 (EIP-2981), dibaca marketplace\n    //  (Element dll.) untuk memotong royalti dari penjualan secondary.\n    // ==================================================================\n\n    /// @notice ERC-2981: untuk token apa pun yang sudah mint, penerima\n    ///         royalti = owner kontrak, jumlah = salePrice * royaltyBps\n    ///         / 10.000. Revert untuk token yang belum ada (sesuai EIP:\n    ///         marketplace hanya memanggil ini untuk token valid).\n    function royaltyInfo(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 royaltyAmount) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return (owner, (salePrice * royaltyBps) / 10000);\n    }\n\n    /// @notice Ubah persen royalti (basis points, 100 = 1%). Cap keras\n    ///         MAX_ROYALTY_BPS (1.000 = 10%) ditegakkan di kode.\n    function setRoyaltyBps(uint16 _bps) external onlyOwner {\n        require(_bps <= MAX_ROYALTY_BPS, \"royalty above 10% cap\");\n        royaltyBps = _bps;\n        emit RoyaltyUpdated(_bps);\n    }\n\n    // ==================================================================\n    //  OWNER — administratif saja, TIDAK menyentuh NFT milik orang lain\n    // ==================================================================\n\n    /// @notice Ganti lokasi metadata (mis. pindah hosting / reveal).\n    function setBaseURI(string memory _baseURI) external onlyOwner {\n        baseURI = _baseURI;\n    }\n\n    /// @notice Ganti harga paid mint (wei). Berlaku untuk mint berikutnya.\n    function setPrice(uint256 _price) external onlyOwner {\n        paidPrice = _price;\n    }\n\n    /// @notice Tarik seluruh saldo BNB kontrak (hasil paid mint) ke owner.\n    function withdraw() external onlyOwner {\n        uint256 amount = address(this).balance;\n        require(amount > 0, \"nothing to withdraw\");\n        (bool ok, ) = owner.call{value: amount}(\"\");\n        require(ok, \"withdraw failed\");\n    }\n\n    // ==================================================================\n    //  ERC-721 MINIMAL (standar, ditulis inline — tanpa library luar)\n    // ==================================================================\n\n    function balanceOf(address account) public 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 approve(address to, uint256 tokenId) public {\n        address o = ownerOf(tokenId);\n        require(to != o, \"approve to owner\");\n        require(msg.sender == o || isApprovedForAll(o, msg.sender), \"not authorized\");\n        _tokenApprovals[tokenId] = to;\n        emit Approval(o, to, tokenId);\n    }\n\n    function getApproved(uint256 tokenId) public view returns (address) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        return _tokenApprovals[tokenId];\n    }\n\n    function setApprovalForAll(address operator, bool approved) public {\n        require(operator != msg.sender, \"approve to caller\");\n        _operatorApprovals[msg.sender][operator] = approved;\n        emit ApprovalForAll(msg.sender, operator, approved);\n    }\n\n    function isApprovedForAll(address account, address operator) public view returns (bool) {\n        return _operatorApprovals[account][operator];\n    }\n\n    function transferFrom(address from, address to, uint256 tokenId) public {\n        require(_isApprovedOrOwner(msg.sender, tokenId), \"not authorized\");\n        _transfer(from, to, tokenId);\n    }\n\n    function safeTransferFrom(address from, address to, uint256 tokenId) public {\n        safeTransferFrom(from, to, tokenId, \"\");\n    }\n\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {\n        require(_isApprovedOrOwner(msg.sender, tokenId), \"not authorized\");\n        _transfer(from, to, tokenId);\n        require(_checkOnERC721Received(from, to, tokenId, data), \"receiver rejected\");\n    }\n\n    /// @notice ERC-165: nyatakan dukungan ERC-721 + Metadata + ERC-2981 + ERC-165.\n    function supportsInterface(bytes4 interfaceId) public pure returns (bool) {\n        return\n            interfaceId == 0x80ac58cd || // ERC-721\n            interfaceId == 0x5b5e139f || // ERC-721 Metadata\n            interfaceId == 0x2a55205a || // ERC-2981 (royalti)\n            interfaceId == 0x01ffc9a7; // ERC-165\n    }\n\n    // --- internal ERC-721 ---\n\n    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {\n        address o = ownerOf(tokenId);\n        return (spender == o || getApproved(tokenId) == spender || isApprovedForAll(o, spender));\n    }\n\n    function _transfer(address from, address to, uint256 tokenId) internal {\n        require(ownerOf(tokenId) == from, \"from is not owner\");\n        require(to != address(0), \"transfer to zero\");\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 _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data)\n        private\n        returns (bool)\n    {\n        if (to.code.length == 0) return true; // alamat biasa (EOA) selalu ok\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    /// @dev uint256 -> string desimal (untuk tokenURI).\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) {\n            digits++;\n            temp /= 10;\n        }\n        bytes memory buffer = new bytes(digits);\n        while (value != 0) {\n            digits -= 1;\n            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n            value /= 10;\n        }\n        return string(buffer);\n    }\n}\n\n/// @notice Interface penerima safeTransfer (standar ERC-721).\ninterface IERC721Receiver {\n    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)\n        external\n        returns (bytes4);\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"_registry","type":"address","internalType":"address"},{"name":"_baseURI","type":"string","internalType":"string"},{"name":"_initialOwner","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"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":"Minted","type":"event","inputs":[{"name":"minter","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"grade","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"variant","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"free","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"name":"RoyaltyUpdated","type":"event","inputs":[{"name":"bps","type":"uint16","indexed":false,"internalType":"uint16"}],"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":"DEFAULT_PAID_PRICE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"FREE_SUPPLY","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_ROYALTY_BPS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint16","internalType":"uint16"}],"stateMutability":"view"},{"name":"MAX_SUPPLY","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":[],"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":"gradeName","type":"function","inputs":[{"name":"g","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"pure"},{"name":"hasFreeMinted","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"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":"mint","type":"function","inputs":[],"outputs":[],"stateMutability":"payable"},{"name":"mintFree","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"name","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"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":"paidPrice","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"registry","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAgentIdRegistry"}],"stateMutability":"view"},{"name":"royaltyBps","type":"function","inputs":[],"outputs":[{"name":"","type":"uint16","internalType":"uint16"}],"stateMutability":"view"},{"name":"royaltyInfo","type":"function","inputs":[{"name":"tokenId","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":"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":"_baseURI","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setPrice","type":"function","inputs":[{"name":"_price","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setRoyaltyBps","type":"function","inputs":[{"name":"_bps","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"},{"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":"tokenGrade","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"tokenURI","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"tokenVariant","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"totalMinted","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":"variantName","type":"function","inputs":[{"name":"v","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"pure"},{"name":"withdraw","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"matchId":"58883428","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-09T12:26:25Z","match":"exact_match","chainId":"56","address":"0x7BcC29c5405286b4Dd8b5b23FE7097F9EE65e4e5"}