{"sources":{"RadarSignals.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.24;\n\n/// @title RADAR SIGNALS (SIGNAL)\n/// @notice 2,222 fully on-chain radar signals on BNB Chain. Only wallets holding a\n/// SPACE ID Agent ID (.ai) can mint, and every mint needs a proof of work:\n/// keccak256(abi.encodePacked(address(this), minter, mintedBy[minter], nonce)) must start\n/// with at least 20 zero bits. More zero bits = rarer signal.\n/// First mint per wallet is free (gas only); mints 2 and 3 cost PRICE each. Max 3 per wallet.\n/// Mint opens at MINT_START and closes for good at MINT_END (72 hours later).\n/// No owner, no pause, no setters: price, supply, window and payout are immutable.\ninterface IAgentId {\n    function balanceOf(address owner) external view returns (uint256);\n}\n\ninterface IERC721Receiver {\n    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);\n}\n\ncontract RadarSignals {\n    string public constant name = \"RADAR SIGNALS\";\n    string public constant symbol = \"SIGNAL\";\n    uint256 public constant MAX_SUPPLY = 2222;\n    uint256 public constant MAX_PER_WALLET = 3;\n    uint256 public constant PRICE = 0.0003 ether;\n    uint256 public constant MIN_BITS = 20;\n    uint256 public constant WINDOW = 72 hours;\n\n    IAgentId public immutable agentId;\n    address payable public immutable payout;\n    uint256 public immutable MINT_START;\n    uint256 public immutable MINT_END;\n\n    uint256 public totalSupply;\n    mapping(address => uint256) public mintedBy;\n    mapping(uint256 => bytes32) public signalOf; // proof hash of each token\n\n    mapping(uint256 => address) private _owner;\n    mapping(address => uint256) private _balance;\n    mapping(uint256 => address) private _approved;\n    mapping(address => mapping(address => bool)) private _operator;\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 SignalLocked(address indexed minter, uint256 indexed tokenId, uint256 bits);\n\n    error NotOpen();\n    error Closed();\n    error SoldOut();\n    error WalletLimit();\n    error NoAgentId();\n    error WeakProof();\n    error WrongPayment();\n    error NotAuthorized();\n    error NonexistentToken();\n    error ZeroAddress();\n    error UnsafeRecipient();\n    error PayoutFailed();\n\n    constructor(address agentId_, address payable payout_, uint256 mintStart_) {\n        if (agentId_ == address(0) || payout_ == address(0)) revert ZeroAddress();\n        agentId = IAgentId(agentId_);\n        payout = payout_;\n        MINT_START = mintStart_;\n        MINT_END = mintStart_ + WINDOW;\n    }\n\n    // ---------------------------------------------------------------- mint\n\n    /// The proof is bound to this contract, the minter and how many the minter already has,\n    /// so a solution cannot be reused or copied by another wallet.\n    function proofHash(address minter, uint256 nonce) public view returns (bytes32) {\n        return keccak256(abi.encodePacked(address(this), minter, mintedBy[minter], nonce));\n    }\n\n    function leadingZeroBits(bytes32 h) public pure returns (uint256 n) {\n        uint256 x = uint256(h);\n        if (x == 0) return 256;\n        while (x & (1 << 255) == 0) {\n            x <<= 1;\n            unchecked { ++n; }\n        }\n    }\n\n    function priceFor(address wallet) public view returns (uint256) {\n        return mintedBy[wallet] == 0 ? 0 : PRICE;\n    }\n\n    /// @param nonce any number that makes proofHash(msg.sender, nonce) start with >= MIN_BITS zero bits\n    function mint(uint256 nonce) external payable returns (uint256 tokenId) {\n        if (block.timestamp < MINT_START) revert NotOpen();\n        if (block.timestamp >= MINT_END) revert Closed();\n        if (totalSupply >= MAX_SUPPLY) revert SoldOut();\n        uint256 done = mintedBy[msg.sender];\n        if (done >= MAX_PER_WALLET) revert WalletLimit();\n        if (agentId.balanceOf(msg.sender) == 0) revert NoAgentId();\n\n        bytes32 h = proofHash(msg.sender, nonce);\n        uint256 bits = leadingZeroBits(h);\n        if (bits < MIN_BITS) revert WeakProof();\n\n        uint256 cost = done == 0 ? 0 : PRICE;\n        if (msg.value != cost) revert WrongPayment();\n\n        mintedBy[msg.sender] = done + 1;\n        tokenId = ++totalSupply;\n        signalOf[tokenId] = h;\n        _owner[tokenId] = msg.sender;\n        unchecked { ++_balance[msg.sender]; }\n        emit Transfer(address(0), msg.sender, tokenId);\n        emit SignalLocked(msg.sender, tokenId, bits);\n\n        if (cost != 0) {\n            (bool ok, ) = payout.call{value: cost}(\"\");\n            if (!ok) revert PayoutFailed();\n        }\n    }\n\n    // ---------------------------------------------------------------- traits\n\n    function rarityOf(uint256 tokenId) public view returns (string memory) {\n        uint256 b = leadingZeroBits(_signal(tokenId));\n        if (b >= 26) return \"Legendary\";\n        if (b >= 24) return \"Epic\";\n        if (b >= 22) return \"Rare\";\n        return \"Common\";\n    }\n\n    function _signal(uint256 tokenId) internal view returns (bytes32 h) {\n        h = signalOf[tokenId];\n        if (h == bytes32(0)) revert NonexistentToken();\n    }\n\n    function _color(uint256 b) internal pure returns (string memory) {\n        if (b >= 26) return \"#FFD54A\";\n        if (b >= 24) return \"#C06BFF\";\n        if (b >= 22) return \"#3FE0FF\";\n        return \"#39FF7A\";\n    }\n\n    function _orbit(uint256 v) internal pure returns (string memory) {\n        uint256 i = v % 4;\n        if (i == 0) return \"LEO\";\n        if (i == 1) return \"MEO\";\n        if (i == 2) return \"GEO\";\n        return \"HEO\";\n    }\n\n    function tokenURI(uint256 tokenId) external view returns (string memory) {\n        bytes32 h = _signal(tokenId);\n        string memory json = string.concat(\n            '{\"name\":\"RADAR SIGNAL #', _u(tokenId),\n            '\",\"description\":\"A radar signal locked on-chain by an AI agent with proof of work on BNB Chain.\",\"attributes\":', _attrs(tokenId, h),\n            ',\"image\":\"data:image/svg+xml;base64,', _b64(bytes(_svg(tokenId, h))), '\"}'\n        );\n        return string.concat(\"data:application/json;base64,\", _b64(bytes(json)));\n    }\n\n    function _freq(uint256 s) internal pure returns (string memory) {\n        uint256 f = 1000 + (s % 9000); // 100.0 - 999.9 MHz\n        return string.concat(_u(f / 10), \".\", _u(f % 10));\n    }\n\n    function _attrs(uint256 tokenId, bytes32 h) internal view returns (string memory) {\n        uint256 s = uint256(h);\n        string memory a = string.concat(\n            '[{\"trait_type\":\"Rarity\",\"value\":\"', rarityOf(tokenId),\n            '\"},{\"trait_type\":\"Lock Bits\",\"value\":', _u(leadingZeroBits(h)), '}'\n        );\n        return string.concat(\n            a, ',{\"trait_type\":\"Frequency\",\"value\":\"', _freq(s), ' MHz\"},{\"trait_type\":\"Orbit\",\"value\":\"', _orbit(s >> 40),\n            '\"},{\"trait_type\":\"Blips\",\"value\":', _u(1 + ((s >> 16) % 5)), '}]'\n        );\n    }\n\n    function _svg(uint256 tokenId, bytes32 h) internal pure returns (string memory) {\n        uint256 b = leadingZeroBits(h);\n        uint256 s = uint256(h);\n        string memory col = _color(b);\n        string memory head = string.concat(\n            '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 320\"><rect width=\"320\" height=\"320\" fill=\"#05070A\"/><g fill=\"none\" stroke=\"',\n            col,\n            '\" stroke-opacity=\".35\"><circle cx=\"160\" cy=\"160\" r=\"40\"/><circle cx=\"160\" cy=\"160\" r=\"80\"/><circle cx=\"160\" cy=\"160\" r=\"120\"/><path d=\"M160 30V290M30 160H290\"/></g>'\n        );\n        string memory body = string.concat(head, _sweep(s, col), _blips(s, 1 + ((s >> 16) % 5), col));\n        string memory t1 = string.concat('<text x=\"16\" y=\"24\" font-family=\"monospace\" font-size=\"12\" fill=\"', col, '\">SIGNAL #', _u(tokenId), '</text>');\n        string memory t2 = string.concat('<text x=\"16\" y=\"306\" font-family=\"monospace\" font-size=\"12\" fill=\"', col, '\">', _freq(s), ' MHz / ', _orbit(s >> 40));\n        return string.concat(body, t1, t2, ' / ', _u(b), ' bits</text></svg>');\n    }\n\n    function _sweep(uint256 s, string memory col) internal pure returns (string memory) {\n        uint256 ang = (s >> 64) % 360;\n        return string.concat(\n            '<g transform=\"rotate(', _u(ang), ' 160 160)\"><path d=\"M160 160L160 40A120 120 0 0 1 245 75Z\" fill=\"', col,\n            '\" fill-opacity=\".18\"/><path d=\"M160 160V40\" stroke=\"', col, '\" stroke-width=\"2\"/></g>'\n        );\n    }\n\n    function _blips(uint256 s, uint256 n, string memory col) internal pure returns (string memory out) {\n        for (uint256 i = 0; i < n; i++) {\n            uint256 r = uint256(keccak256(abi.encode(s, i)));\n            uint256 x = 60 + (r % 200);\n            uint256 y = 60 + ((r >> 32) % 200);\n            out = string.concat(out, '<circle cx=\"', _u(x), '\" cy=\"', _u(y), '\" r=\"', _u(3 + ((r >> 64) % 4)), '\" fill=\"', col, '\"/>');\n        }\n    }\n\n    function _u(uint256 v) internal pure returns (string memory) {\n        if (v == 0) return \"0\";\n        uint256 j = v;\n        uint256 len;\n        while (j != 0) { len++; j /= 10; }\n        bytes memory b = new bytes(len);\n        while (v != 0) { len--; b[len] = bytes1(uint8(48 + (v % 10))); v /= 10; }\n        return string(b);\n    }\n\n    function _b64(bytes memory data) internal pure returns (string memory) {\n        if (data.length == 0) return \"\";\n        string memory table = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n        string memory result = new string(4 * ((data.length + 2) / 3));\n        assembly {\n            let tablePtr := add(table, 1)\n            let resultPtr := add(result, 32)\n            let dataPtr := data\n            let endPtr := add(data, mload(data))\n            for {} lt(dataPtr, endPtr) {} {\n                dataPtr := add(dataPtr, 3)\n                let input := mload(dataPtr)\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))\n                resultPtr := add(resultPtr, 1)\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))\n                resultPtr := add(resultPtr, 1)\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))\n                resultPtr := add(resultPtr, 1)\n                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))\n                resultPtr := add(resultPtr, 1)\n            }\n            switch mod(mload(data), 3)\n            case 1 { mstore8(sub(resultPtr, 1), 0x3d) mstore8(sub(resultPtr, 2), 0x3d) }\n            case 2 { mstore8(sub(resultPtr, 1), 0x3d) }\n        }\n        return result;\n    }\n\n    // ---------------------------------------------------------------- ERC721\n\n    function supportsInterface(bytes4 id) external pure returns (bool) {\n        return id == 0x01ffc9a7 || id == 0x80ac58cd || id == 0x5b5e139f;\n    }\n\n    function balanceOf(address owner) external view returns (uint256) {\n        if (owner == address(0)) revert ZeroAddress();\n        return _balance[owner];\n    }\n\n    function ownerOf(uint256 tokenId) public view returns (address o) {\n        o = _owner[tokenId];\n        if (o == address(0)) revert NonexistentToken();\n    }\n\n    function approve(address to, uint256 tokenId) external {\n        address o = ownerOf(tokenId);\n        if (msg.sender != o && !_operator[o][msg.sender]) revert NotAuthorized();\n        _approved[tokenId] = to;\n        emit Approval(o, to, tokenId);\n    }\n\n    function getApproved(uint256 tokenId) external view returns (address) {\n        ownerOf(tokenId);\n        return _approved[tokenId];\n    }\n\n    function setApprovalForAll(address operator, bool approved) external {\n        _operator[msg.sender][operator] = approved;\n        emit ApprovalForAll(msg.sender, operator, approved);\n    }\n\n    function isApprovedForAll(address owner, address operator) external view returns (bool) {\n        return _operator[owner][operator];\n    }\n\n    function transferFrom(address from, address to, uint256 tokenId) public {\n        address o = ownerOf(tokenId);\n        if (o != from) revert NotAuthorized();\n        if (to == address(0)) revert ZeroAddress();\n        if (msg.sender != o && !_operator[o][msg.sender] && _approved[tokenId] != msg.sender) revert NotAuthorized();\n        delete _approved[tokenId];\n        unchecked { --_balance[from]; ++_balance[to]; }\n        _owner[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        if (to.code.length != 0 &&\n            IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) != IERC721Receiver.onERC721Received.selector)\n            revert UnsafeRecipient();\n    }\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"agentId_","type":"address","internalType":"address"},{"name":"payout_","type":"address","internalType":"address payable"},{"name":"mintStart_","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"name":"Closed","type":"error","inputs":[]},{"name":"NoAgentId","type":"error","inputs":[]},{"name":"NonexistentToken","type":"error","inputs":[]},{"name":"NotAuthorized","type":"error","inputs":[]},{"name":"NotOpen","type":"error","inputs":[]},{"name":"PayoutFailed","type":"error","inputs":[]},{"name":"SoldOut","type":"error","inputs":[]},{"name":"UnsafeRecipient","type":"error","inputs":[]},{"name":"WalletLimit","type":"error","inputs":[]},{"name":"WeakProof","type":"error","inputs":[]},{"name":"WrongPayment","type":"error","inputs":[]},{"name":"ZeroAddress","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":"SignalLocked","type":"event","inputs":[{"name":"minter","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"bits","type":"uint256","indexed":false,"internalType":"uint256"}],"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_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_END","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MINT_START","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MIN_BITS","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":"WINDOW","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"agentId","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAgentId"}],"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":"owner","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":"owner","type":"address","internalType":"address"},{"name":"operator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"leadingZeroBits","type":"function","inputs":[{"name":"h","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"n","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"},{"name":"mint","type":"function","inputs":[{"name":"nonce","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"stateMutability":"payable"},{"name":"mintedBy","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":"ownerOf","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"o","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"payout","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address payable"}],"stateMutability":"view"},{"name":"priceFor","type":"function","inputs":[{"name":"wallet","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"proofHash","type":"function","inputs":[{"name":"minter","type":"address","internalType":"address"},{"name":"nonce","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"name":"rarityOf","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"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":"signalOf","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"name":"supportsInterface","type":"function","inputs":[{"name":"id","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"}],"matchId":"58053025","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-02T08:11:38Z","match":"exact_match","chainId":"56","address":"0x6Bb421E8ef7f62F3DD1076b48FBa15441506251d"}