{"sources":{"src/AgentRadarPass.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.24;\n\n/// @notice Read-only view of the Binance Agent ID handle registry used by Agent ID mints on BNB Chain.\ninterface IAgentHandles {\n    function handleOf(address account) external view returns (string memory);\n}\n\n/// @title Agent Radar Pass\n/// @notice A free, soulbound pass for wallets that hold a Binance Agent ID. One per Agent ID handle and one per wallet,\n///         fixed supply, fixed mint window. The contract has no owner, no admin function, no withdraw and holds no funds:\n///         every rule below is set in the constructor and can never change.\n/// @dev Minimal ERC-721 + ERC-5192 (soulbound). Metadata and art are generated on chain.\ncontract AgentRadarPass {\n    string public constant name = \"Agent Radar Pass\";\n    string public constant symbol = \"RPASS\";\n    uint256 public constant GENESIS = 222;\n\n    IAgentHandles public immutable registrar;\n    uint256 public immutable maxSupply;\n    uint64 public immutable mintStart;\n    uint64 public immutable mintEnd;\n\n    uint256 public totalSupply;\n    mapping(uint256 => address) private _ownerOf;\n    mapping(uint256 => string) private _handleOf;\n    /// @notice Pass number held by a wallet (0 = none).\n    mapping(address => uint256) public passOf;\n    /// @notice Pass number minted for keccak256(handle) (0 = none).\n    mapping(bytes32 => uint256) public passOfHandle;\n\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n    event Locked(uint256 tokenId);\n\n    error NotStarted(uint64 start);\n    error Ended(uint64 end);\n    error SoldOut();\n    error NoAgentId();\n    error AlreadyMinted(uint256 tokenId);\n    error Soulbound();\n    error NotMinted(uint256 tokenId);\n    error BadConfig();\n\n    constructor(address registrar_, uint256 maxSupply_, uint64 mintStart_, uint64 mintEnd_) {\n        if (registrar_ == address(0) || maxSupply_ == 0 || mintEnd_ <= mintStart_) revert BadConfig();\n        registrar = IAgentHandles(registrar_);\n        maxSupply = maxSupply_;\n        mintStart = mintStart_;\n        mintEnd = mintEnd_;\n    }\n\n    // ------------------------------------------------------------------ mint\n\n    /// @notice Mint your pass. Free (gas only). Requires an Agent ID handle on the calling wallet.\n    function mint() external returns (uint256 id) {\n        if (block.timestamp < mintStart) revert NotStarted(mintStart);\n        if (block.timestamp >= mintEnd) revert Ended(mintEnd);\n        if (totalSupply >= maxSupply) revert SoldOut();\n        string memory h = registrar.handleOf(msg.sender);\n        if (bytes(h).length == 0) revert NoAgentId();\n        bytes32 hk = keccak256(bytes(h));\n        uint256 ada = passOf[msg.sender];\n        if (ada == 0) ada = passOfHandle[hk];\n        if (ada != 0) revert AlreadyMinted(ada);\n\n        id = ++totalSupply;\n        _ownerOf[id] = msg.sender;\n        _handleOf[id] = h;\n        passOf[msg.sender] = id;\n        passOfHandle[hk] = id;\n        emit Transfer(address(0), msg.sender, id);\n        emit Locked(id);\n    }\n\n    /// @notice 0 = not open yet, 1 = open, 2 = closed (time) , 3 = sold out.\n    function status() external view returns (uint8) {\n        if (totalSupply >= maxSupply) return 3;\n        if (block.timestamp < mintStart) return 0;\n        if (block.timestamp >= mintEnd) return 2;\n        return 1;\n    }\n\n    // ------------------------------------------------------------------ ERC-721 views (soulbound)\n\n    function ownerOf(uint256 id) public view returns (address o) {\n        o = _ownerOf[id];\n        if (o == address(0)) revert NotMinted(id);\n    }\n\n    function balanceOf(address account) external view returns (uint256) {\n        return passOf[account] == 0 ? 0 : 1;\n    }\n\n    function handleOfPass(uint256 id) external view returns (string memory) {\n        ownerOf(id);\n        return _handleOf[id];\n    }\n\n    function locked(uint256 id) external view returns (bool) {\n        ownerOf(id);\n        return true;\n    }\n\n    function getApproved(uint256 id) external view returns (address) {\n        ownerOf(id);\n        return address(0);\n    }\n\n    function isApprovedForAll(address, address) external pure returns (bool) {\n        return false;\n    }\n\n    function supportsInterface(bytes4 i) external pure returns (bool) {\n        return i == 0x01ffc9a7 // ERC-165\n            || i == 0x80ac58cd // ERC-721\n            || i == 0x5b5e139f // ERC-721 Metadata\n            || i == 0xb45a3c0e; // ERC-5192\n    }\n\n    // Every way to move or approve a pass reverts: it stays with the wallet that minted it.\n    function approve(address, uint256) external pure {\n        revert Soulbound();\n    }\n\n    function setApprovalForAll(address, bool) external pure {\n        revert Soulbound();\n    }\n\n    function transferFrom(address, address, uint256) external pure {\n        revert Soulbound();\n    }\n\n    function safeTransferFrom(address, address, uint256) external pure {\n        revert Soulbound();\n    }\n\n    function safeTransferFrom(address, address, uint256, bytes calldata) external pure {\n        revert Soulbound();\n    }\n\n    // ------------------------------------------------------------------ metadata (on chain)\n\n    function tokenURI(uint256 id) external view returns (string memory) {\n        ownerOf(id);\n        string memory h = _clean(_handleOf[id]);\n        string memory n = _pad(id);\n        string memory wave = id <= GENESIS ? \"Genesis\" : \"Pass\";\n        string memory json = string.concat(\n            '{\"name\":\"Agent Radar Pass #', n,\n            '\",\"description\":\"Soulbound pass for Binance Agent ID holders. Free, one per Agent ID, ', _str(maxSupply),\n            ' total. Holders get +1 ticket in the daily $RADAR social mining draw. https://datt.my/pass/\",',\n            '\"image\":\"data:image/svg+xml;base64,', _b64(bytes(_svg(n, h, id <= GENESIS))),\n            '\",\"attributes\":[{\"trait_type\":\"Agent\",\"value\":\"', h,\n            '\"},{\"trait_type\":\"Wave\",\"value\":\"', wave,\n            '\"},{\"display_type\":\"number\",\"trait_type\":\"Number\",\"value\":', _str(id), \"}]}\"\n        );\n        return string.concat(\"data:application/json;base64,\", _b64(bytes(json)));\n    }\n\n    function _svg(string memory n, string memory h, bool genesis) internal view returns (string memory) {\n        string memory accent = genesis ? \"#FFD23F\" : \"#FFA62B\";\n        return string.concat(\n            '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 600 600\" font-family=\"ui-monospace,Menlo,Consolas,monospace\">',\n            '<rect width=\"600\" height=\"600\" fill=\"#08090A\"/>',\n            '<rect width=\"600\" height=\"10\" fill=\"', accent, '\"/>',\n            '<rect x=\"36\" y=\"40\" width=\"528\" height=\"44\" fill=\"#1E3FBF\"/>',\n            '<text x=\"52\" y=\"69\" fill=\"#fff\" font-size=\"18\" font-weight=\"700\" letter-spacing=\"2\">AGENT RADAR PASS</text>',\n            '<text x=\"548\" y=\"69\" fill=\"#fff\" font-size=\"18\" font-weight=\"700\" text-anchor=\"end\">',\n            genesis ? \"GENESIS\" : \"BNB CHAIN\",\n            \"</text>\",\n            '<g fill=\"none\" stroke=\"', accent, '\"><circle cx=\"458\" cy=\"300\" r=\"104\" stroke-width=\"6\"/>',\n            '<circle cx=\"458\" cy=\"300\" r=\"62\" stroke-width=\"3\" stroke-opacity=\".5\"/></g>',\n            '<path d=\"M458 300 L548 248\" stroke=\"#FFD23F\" stroke-width=\"7\"/>',\n            '<rect x=\"448\" y=\"290\" width=\"20\" height=\"20\" fill=\"', accent, '\"/>',\n            '<rect x=\"492\" y=\"336\" width=\"18\" height=\"18\" fill=\"#38E07B\"/>',\n            '<text x=\"52\" y=\"270\" fill=\"', accent, '\" font-size=\"74\" font-weight=\"700\">#', n, \"</text>\",\n            '<text x=\"52\" y=\"310\" fill=\"#8C8A84\" font-size=\"20\">OF ', _str(maxSupply), \"</text>\",\n            '<text x=\"52\" y=\"470\" fill=\"#F4F1EA\" font-size=\"28\" font-weight=\"700\">', h, \"</text>\",\n            '<text x=\"52\" y=\"505\" fill=\"#8C8A84\" font-size=\"16\" letter-spacing=\"1\">AGENT ID - SOULBOUND - FREE MINT</text>',\n            '<text x=\"548\" y=\"560\" fill=\"#FFA62B\" font-size=\"18\" font-weight=\"700\" text-anchor=\"end\">datt.my</text>',\n            \"</svg>\"\n        );\n    }\n\n    /// @dev Handles are short ids; anything outside [A-Za-z0-9._-] becomes \"_\" so it is safe inside JSON and SVG.\n    function _clean(string memory s) internal pure returns (string memory) {\n        bytes memory b = bytes(s);\n        uint256 len = b.length > 32 ? 32 : b.length;\n        bytes memory out = new bytes(len);\n        for (uint256 i; i < len; ++i) {\n            bytes1 c = b[i];\n            bool ok = (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c == 0x2e || c == 0x5f\n                || c == 0x2d;\n            out[i] = ok ? c : bytes1(0x5f);\n        }\n        return string(out);\n    }\n\n    function _pad(uint256 v) internal pure returns (string memory) {\n        string memory s = _str(v);\n        uint256 l = bytes(s).length;\n        if (l >= 4) return s;\n        return string.concat(l == 1 ? \"000\" : l == 2 ? \"00\" : \"0\", s);\n    }\n\n    function _str(uint256 v) internal pure returns (string memory) {\n        if (v == 0) return \"0\";\n        uint256 j = v;\n        uint256 len;\n        while (j != 0) {\n            ++len;\n            j /= 10;\n        }\n        bytes memory b = new bytes(len);\n        while (v != 0) {\n            b[--len] = bytes1(uint8(48 + v % 10));\n            v /= 10;\n        }\n        return string(b);\n    }\n\n    bytes internal constant _T = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n    function _b64(bytes memory data) internal pure returns (string memory) {\n        if (data.length == 0) return \"\";\n        bytes memory out = new bytes(4 * ((data.length + 2) / 3));\n        uint256 o;\n        for (uint256 i; i < data.length; i += 3) {\n            uint256 a = uint8(data[i]);\n            uint256 b = i + 1 < data.length ? uint8(data[i + 1]) : 0;\n            uint256 c = i + 2 < data.length ? uint8(data[i + 2]) : 0;\n            uint256 t = (a << 16) | (b << 8) | c;\n            out[o++] = _T[(t >> 18) & 63];\n            out[o++] = _T[(t >> 12) & 63];\n            out[o++] = i + 1 < data.length ? _T[(t >> 6) & 63] : bytes1(\"=\");\n            out[o++] = i + 2 < data.length ? _T[t & 63] : bytes1(\"=\");\n        }\n        return string(out);\n    }\n}\n"}},"matchId":"57822647","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-10-31T12:20:48Z","match":"exact_match","chainId":"56","address":"0x0CE20E838CDa5f6ccc8A59ef78Bc0E9bF4628d89"}