{"sources":{"contracts/Agent888Season2.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.20;\r\n\r\n/// @title AGENT 888 SEASON 2 (A888S2) — Season 2 NFT on BSC\r\n/// Rules: FREE mint, hard cap 888. Eligibility is a LIVE check against the\r\n/// Season 1 contract: the minter must hold at least 1 Season 1 NFT, and the\r\n/// amount they have minted here may never exceed their Season 1 balance\r\n/// (Season 1 caps at 2 per wallet, so Season 2 effectively caps at 2 too).\r\ncontract Agent888Season2 {\r\n    string public name;\r\n    string public symbol;\r\n    address public owner;\r\n\r\n    uint256 public constant MAX_SUPPLY = 888;\r\n    uint256 public totalSupply;\r\n    string public baseURI;\r\n    bool public mintOpen = true;\r\n\r\n    /// Season 1 contract — the eligibility source. Immutable: set once in the\r\n    /// constructor and never changeable, so eligibility can never be quietly\r\n    /// re-pointed at a different (fake) collection after deployment.\r\n    IERC721Like public immutable season1;\r\n\r\n    mapping(address => uint256) public mintedByWallet;\r\n    mapping(uint256 => address) private _owners;\r\n    mapping(address => uint256) private _balances;\r\n    mapping(uint256 => address) private _tokenApprovals;\r\n    mapping(address => mapping(address => bool)) private _operatorApprovals;\r\n\r\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\r\n    event Approval(address indexed owner_, address indexed approved, uint256 indexed tokenId);\r\n    event ApprovalForAll(address indexed owner_, address indexed operator, bool approved);\r\n    event OwnershipTransferred(address indexed from, address indexed to);\r\n\r\n    modifier onlyOwner() {\r\n        require(msg.sender == owner, \"not owner\");\r\n        _;\r\n    }\r\n\r\n    constructor(address season1_) {\r\n        require(season1_ != address(0), \"zero address\");\r\n        name = \"AGENT 888 SEASON 2\";\r\n        symbol = \"A888S2\";\r\n        owner = msg.sender;\r\n        season1 = IERC721Like(season1_);\r\n    }\r\n\r\n    // ---------- minting ----------\r\n    /// Mint 1 or 2, always free (any attached value reverts).\r\n    /// Gates, in order: mint open, quantity, zero value, supply cap,\r\n    /// Season 1 balance >= 1, and minted-so-far + quantity <= Season 1 balance.\r\n    /// The balance check is live: if the minter later receives more Season 1\r\n    /// NFTs they can mint more here (up to 2); if their Season 1 balance drops\r\n    /// they cannot mint further until it is back.\r\n    function mint(uint256 quantity) external payable {\r\n        require(mintOpen, \"mint closed\");\r\n        require(quantity == 1 || quantity == 2, \"mint 1 or 2\");\r\n        require(msg.value == 0, \"mint is free\");\r\n        require(totalSupply + quantity <= MAX_SUPPLY, \"sold out\");\r\n        uint256 s1Balance = season1.balanceOf(msg.sender);\r\n        require(s1Balance >= 1, \"not eligible\");\r\n        require(mintedByWallet[msg.sender] + quantity <= s1Balance, \"exceeds season 1 balance\");\r\n        require(_balances[msg.sender] + quantity <= s1Balance, \"exceeds season 1 balance\");\r\n        mintedByWallet[msg.sender] += quantity;\r\n        for (uint256 i = 0; i < quantity; i++) {\r\n            totalSupply += 1;\r\n            _owners[totalSupply] = msg.sender;\r\n            _balances[msg.sender] += 1;\r\n            emit Transfer(address(0), msg.sender, totalSupply);\r\n        }\r\n    }\r\n\r\n    // ---------- owner ----------\r\n    function setBaseURI(string calldata newBaseURI) external onlyOwner {\r\n        baseURI = newBaseURI;\r\n    }\r\n\r\n    function setMintOpen(bool open_) external onlyOwner {\r\n        mintOpen = open_;\r\n    }\r\n\r\n    function transferOwnership(address newOwner) external onlyOwner {\r\n        require(newOwner != address(0), \"zero address\");\r\n        emit OwnershipTransferred(owner, newOwner);\r\n        owner = newOwner;\r\n    }\r\n\r\n    /// Withdraw anything the contract happens to hold to the owner wallet.\r\n    /// Minting is free, so the balance should normally be zero.\r\n    function withdraw() external onlyOwner {\r\n        uint256 amount = address(this).balance;\r\n        require(amount > 0, \"nothing to withdraw\");\r\n        (bool ok, ) = payable(owner).call{value: amount}(\"\");\r\n        require(ok, \"withdraw failed\");\r\n    }\r\n\r\n    // ---------- ERC-721 ----------\r\n    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {\r\n        return interfaceId == 0x01ffc9a7 || interfaceId == 0x80ac58cd || interfaceId == 0x5b5e139f;\r\n    }\r\n\r\n    function balanceOf(address account) external view returns (uint256) {\r\n        require(account != address(0), \"zero address\");\r\n        return _balances[account];\r\n    }\r\n\r\n    function ownerOf(uint256 tokenId) public view returns (address) {\r\n        address o = _owners[tokenId];\r\n        require(o != address(0), \"nonexistent token\");\r\n        return o;\r\n    }\r\n\r\n    function tokenURI(uint256 tokenId) external view returns (string memory) {\r\n        ownerOf(tokenId);\r\n        if (bytes(baseURI).length == 0) return \"\";\r\n        return string(abi.encodePacked(baseURI, _toString(tokenId), \".json\"));\r\n    }\r\n\r\n    function approve(address to, uint256 tokenId) external {\r\n        address o = ownerOf(tokenId);\r\n        require(msg.sender == o || _operatorApprovals[o][msg.sender], \"not authorized\");\r\n        _tokenApprovals[tokenId] = to;\r\n        emit Approval(o, to, tokenId);\r\n    }\r\n\r\n    function getApproved(uint256 tokenId) external view returns (address) {\r\n        ownerOf(tokenId);\r\n        return _tokenApprovals[tokenId];\r\n    }\r\n\r\n    function setApprovalForAll(address operator, bool approved) external {\r\n        _operatorApprovals[msg.sender][operator] = approved;\r\n        emit ApprovalForAll(msg.sender, operator, approved);\r\n    }\r\n\r\n    function isApprovedForAll(address account, address operator) external view returns (bool) {\r\n        return _operatorApprovals[account][operator];\r\n    }\r\n\r\n    function transferFrom(address from, address to, uint256 tokenId) public {\r\n        require(_isApprovedOrOwner(msg.sender, tokenId), \"not authorized\");\r\n        require(ownerOf(tokenId) == from, \"wrong from\");\r\n        require(to != address(0), \"zero address\");\r\n        delete _tokenApprovals[tokenId];\r\n        _balances[from] -= 1;\r\n        _balances[to] += 1;\r\n        _owners[tokenId] = to;\r\n        emit Transfer(from, to, tokenId);\r\n    }\r\n\r\n    function safeTransferFrom(address from, address to, uint256 tokenId) external {\r\n        safeTransferFrom(from, to, tokenId, \"\");\r\n    }\r\n\r\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {\r\n        transferFrom(from, to, tokenId);\r\n        require(_checkOnERC721Received(from, to, tokenId, data), \"receiver rejected\");\r\n    }\r\n\r\n    // ---------- internals ----------\r\n    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {\r\n        address o = ownerOf(tokenId);\r\n        return spender == o || _tokenApprovals[tokenId] == spender || _operatorApprovals[o][spender];\r\n    }\r\n\r\n    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) internal returns (bool) {\r\n        if (to.code.length == 0) return true;\r\n        try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {\r\n            return retval == IERC721Receiver.onERC721Received.selector;\r\n        } catch {\r\n            return false;\r\n        }\r\n    }\r\n\r\n    function _toString(uint256 value) internal pure returns (string memory) {\r\n        if (value == 0) return \"0\";\r\n        uint256 temp = value;\r\n        uint256 digits;\r\n        while (temp != 0) {\r\n            digits++;\r\n            temp /= 10;\r\n        }\r\n        bytes memory buffer = new bytes(digits);\r\n        while (value != 0) {\r\n            digits -= 1;\r\n            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\r\n            value /= 10;\r\n        }\r\n        return string(buffer);\r\n    }\r\n}\r\n\r\n/// Minimal read-only view of the Season 1 contract: only what eligibility needs.\r\ninterface IERC721Like {\r\n    function balanceOf(address account) external view returns (uint256);\r\n}\r\n\r\ninterface IERC721Receiver {\r\n    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);\r\n}"}},"abi":[{"type":"constructor","inputs":[{"name":"season1_","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":"OwnershipTransferred","type":"event","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","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":"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":"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":"mint","type":"function","inputs":[{"name":"quantity","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"name":"mintOpen","type":"function","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"mintedByWallet","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"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":"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":"season1","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC721Like"}],"stateMutability":"view"},{"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":"newBaseURI","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setMintOpen","type":"function","inputs":[{"name":"open_","type":"bool","internalType":"bool"}],"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":"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":"58880487","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-09T11:53:58Z","match":"exact_match","chainId":"56","address":"0x1c963Acf3743A2b2A3Ee8359100e9cbEcA82D424"}