{"sources":{"ShanhaiBeasts.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ninterface IAgentId {\n    function balanceOf(address owner) external view returns (uint256);\n}\n\ninterface IERC721Receiver {\n    function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4);\n}\n\n/// SHANHAI BEASTS (SHB) 山海神兽 - fully on-chain pixel beasts from the Classic of Mountains and Seas.\n/// Rules are fixed in code:\n/// - Only addresses holding a SPACE ID .ai Agent ID can mint\n/// - Total supply 5,000 (bonus beasts included)\n/// - Up to 30 minted slots per wallet. Slot #1 is free (Common).\n///   Paid slots mint Spirit beasts: #2-#5 0.0005 BNB, #6-#15 0.00121 BNB, #16-#30 0.00163 BNB each (all 30 = 0.03855 BNB)\n/// - Bonus (not counted in the 30): reaching 10 slots gives +1 Rare (gold), reaching 30 gives +1 Legendary (holo)\n/// - Species (10 beasts) picked pseudo-randomly per token at mint\n/// - Mint is open for 5 days after deploy, then closes forever\n/// - Payment goes straight to the creator in the same transaction; no admin, no pool, no withdraw, no pause, no setters\n/// - Art and metadata are generated on-chain. 1% secondary royalty (EIP-2981)\ncontract ShanhaiBeasts {\n    string public constant name = \"SHANHAI BEASTS\";\n    string public constant symbol = \"SHB\";\n\n    uint256 public constant MAX_SUPPLY = 5000;\n    uint256 public constant MAX_SLOTS_PER_WALLET = 30;\n    uint256 public constant PRICE_2_TO_5 = 0.0005 ether;\n    uint256 public constant PRICE_6_TO_15 = 0.00121 ether;\n    uint256 public constant PRICE_16_TO_30 = 0.00163 ether;\n    uint256 public constant RARE_AT = 10;\n    uint256 public constant LEGENDARY_AT = 30;\n    uint256 public constant MINT_DURATION = 5 days;\n    uint96 public constant ROYALTY_BPS = 100; // 1%\n\n    IAgentId public constant AGENT_ID = IAgentId(0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F);\n\n    address public immutable creator;\n    uint256 public immutable mintStart;\n    uint256 public immutable mintEnd;\n\n    uint256 public totalSupply;\n    /// slots used by a wallet (free + paid), max 30; bonuses not counted\n    mapping(address => uint256) public slotsMinted;\n    /// minted count per tier: 0 Common, 1 Spirit, 2 Rare, 3 Legendary\n    uint256[4] public tierMinted;\n\n    /// low 160 bits: owner; bits 160..167: kind = tier << 4 | species\n    mapping(uint256 => uint256) private _tok;\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 Milestone(address indexed collector, uint256 reached, uint256 tier);\n\n    error NotAgent();\n    error MintClosed();\n    error SoldOut();\n    error WalletLimit();\n    error WrongPayment();\n    error NothingToMint();\n    error NotOwnerNorApproved();\n    error InvalidToken();\n    error ZeroAddress();\n    error UnsafeRecipient();\n    error TransferFailed();\n\n    constructor(address _creator) {\n        if (_creator == address(0)) revert ZeroAddress();\n        creator = _creator;\n        mintStart = block.timestamp;\n        mintEnd = block.timestamp + MINT_DURATION;\n    }\n\n    /// Collection owner for marketplaces (Element/OpenSea \"claim collection\"). Fixed; there are no owner powers.\n    function owner() external view returns (address) {\n        return creator;\n    }\n\n    // ---------------- Mint ----------------\n\n    /// Price of a wallet's k-th slot (1-based)\n    function slotPrice(uint256 k) public pure returns (uint256) {\n        if (k <= 1) return 0;\n        if (k <= 5) return PRICE_2_TO_5;\n        if (k <= 15) return PRICE_6_TO_15;\n        return PRICE_16_TO_30;\n    }\n\n    /// What `mint(paid)` gives this wallet right now and what it costs (wei). Reverts WalletLimit past 30 slots.\n    function quote(address minter, uint256 paid) public view returns (uint256 tokens, uint256 cost, bool getsFree) {\n        uint256 s = slotsMinted[minter];\n        if (paid > MAX_SLOTS_PER_WALLET) revert WalletLimit();\n        getsFree = s == 0;\n        uint256 z = s + (getsFree ? 1 : 0) + paid;\n        if (z > MAX_SLOTS_PER_WALLET) revert WalletLimit();\n        for (uint256 k = s + 1; k <= z; ++k) cost += slotPrice(k);\n        tokens = z - s;\n        if (s < RARE_AT && z >= RARE_AT) ++tokens;\n        if (s < LEGENDARY_AT && z >= LEGENDARY_AT) ++tokens;\n    }\n\n    /// One call: claims the free Common slot if not yet claimed, plus `paid` Spirit slots (paid may be 0).\n    /// msg.value must equal quote().cost exactly.\n    function mint(uint256 paid) external payable {\n        if (block.timestamp < mintStart || block.timestamp >= mintEnd) revert MintClosed();\n        if (AGENT_ID.balanceOf(msg.sender) == 0) revert NotAgent();\n        (uint256 tokens, uint256 cost, bool getsFree) = quote(msg.sender, paid);\n        if (tokens == 0) revert NothingToMint();\n        if (totalSupply + tokens > MAX_SUPPLY) revert SoldOut();\n        if (msg.value != cost) revert WrongPayment();\n\n        uint256 s = slotsMinted[msg.sender];\n        uint256 z = s + (getsFree ? 1 : 0) + paid;\n        slotsMinted[msg.sender] = z;\n\n        uint256 id = totalSupply;\n        uint256 seed = uint256(keccak256(abi.encodePacked(block.prevrandao, blockhash(block.number - 1), msg.sender, id)));\n        if (getsFree) id = _mintOne(msg.sender, id, 0, seed);\n        for (uint256 k; k < paid; ++k) id = _mintOne(msg.sender, id, 1, seed);\n        if (s < RARE_AT && z >= RARE_AT) {\n            id = _mintOne(msg.sender, id, 2, seed);\n            emit Milestone(msg.sender, RARE_AT, 2);\n        }\n        if (s < LEGENDARY_AT && z >= LEGENDARY_AT) {\n            id = _mintOne(msg.sender, id, 3, seed);\n            emit Milestone(msg.sender, LEGENDARY_AT, 3);\n        }\n        _balances[msg.sender] += tokens;\n        totalSupply = id;\n\n        if (msg.value > 0) {\n            (bool ok, ) = payable(creator).call{value: msg.value}(\"\");\n            if (!ok) revert TransferFailed();\n        }\n    }\n\n    function _mintOne(address to, uint256 id, uint256 tier, uint256 seed) private returns (uint256) {\n        ++id;\n        uint256 sp = uint256(keccak256(abi.encode(seed, id))) % 10;\n        _tok[id] = uint256(uint160(to)) | (((tier << 4) | sp) << 160);\n        ++tierMinted[tier];\n        emit Transfer(address(0), to, id);\n        return id;\n    }\n\n    function remaining() external view returns (uint256) {\n        return MAX_SUPPLY - totalSupply;\n    }\n\n    function mintOpen() external view returns (bool) {\n        return block.timestamp >= mintStart && block.timestamp < mintEnd && totalSupply < MAX_SUPPLY;\n    }\n\n    /// 0 Azure Dragon, 1 White Tiger, 2 Vermilion Bird, 3 Black Tortoise, 4 Qilin,\n    /// 5 Nine-Tailed Fox, 6 Taotie, 7 Kunpeng, 8 Baize, 9 Bifang\n    function speciesOf(uint256 tokenId) public view returns (uint8) {\n        ownerOf(tokenId);\n        return uint8(_tok[tokenId] >> 160) & 15;\n    }\n\n    /// 0 Common, 1 Spirit, 2 Rare, 3 Legendary\n    function tierOf(uint256 tokenId) public view returns (uint8) {\n        ownerOf(tokenId);\n        return uint8(_tok[tokenId] >> 164) & 15;\n    }\n\n    function tokensOfOwner(address o) external view returns (uint256[] memory ids) {\n        uint256 n = _balances[o];\n        ids = new uint256[](n);\n        uint256 j;\n        uint256 ts = totalSupply;\n        for (uint256 i = 1; i <= ts && j < n; ++i) {\n            if (address(uint160(_tok[i])) == o) ids[j++] = i;\n        }\n    }\n\n    // ---------------- ERC-721 ----------------\n\n    function supportsInterface(bytes4 id) external pure returns (bool) {\n        return id == 0x01ffc9a7 || id == 0x80ac58cd || id == 0x5b5e139f || id == 0x2a55205a;\n    }\n\n    function balanceOf(address o) external view returns (uint256) {\n        if (o == address(0)) revert ZeroAddress();\n        return _balances[o];\n    }\n\n    function ownerOf(uint256 tokenId) public view returns (address o) {\n        o = address(uint160(_tok[tokenId]));\n        if (o == address(0)) revert InvalidToken();\n    }\n\n    function approve(address to, uint256 tokenId) external {\n        address o = ownerOf(tokenId);\n        if (msg.sender != o && !_operatorApprovals[o][msg.sender]) revert NotOwnerNorApproved();\n        _tokenApprovals[tokenId] = to;\n        emit Approval(o, to, tokenId);\n    }\n\n    function getApproved(uint256 tokenId) external view returns (address) {\n        ownerOf(tokenId);\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 o, address operator) external view returns (bool) {\n        return _operatorApprovals[o][operator];\n    }\n\n    function transferFrom(address from, address to, uint256 tokenId) public {\n        address o = ownerOf(tokenId);\n        if (o != from) revert NotOwnerNorApproved();\n        if (to == address(0)) revert ZeroAddress();\n        if (msg.sender != o && _tokenApprovals[tokenId] != msg.sender && !_operatorApprovals[o][msg.sender]) {\n            revert NotOwnerNorApproved();\n        }\n        delete _tokenApprovals[tokenId];\n        unchecked {\n            _balances[from] -= 1;\n            _balances[to] += 1;\n        }\n        _tok[tokenId] = (_tok[tokenId] & ~uint256(type(uint160).max)) | uint256(uint160(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            try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 r) {\n                if (r != IERC721Receiver.onERC721Received.selector) revert UnsafeRecipient();\n            } catch {\n                revert UnsafeRecipient();\n            }\n        }\n    }\n\n    function royaltyInfo(uint256, uint256 salePrice) external view returns (address, uint256) {\n        return (creator, (salePrice * ROYALTY_BPS) / 10000);\n    }\n\n    // ---------------- On-chain art / metadata ----------------\n\n    // 10 sprites, 24x24, 4-bit palette indices (0 empty, 1 outline, 2..9 colours), row-major, high nibble first\n    bytes internal constant SPRITES = hex\"000000000000000000000000000000100010000000000000000001910191001010000000000000191910015151000000000001199910155555100000000012222221556551000000000122278222255610000000001222222222225510000000012222222222223551000000017444422222222355100000001717144442222235510000000101011111422223551000000000155100142222351000000001522510014222255100000015222251001422235100000012244225111422235100000192411422555422231000000191410142222222331000000010110144222223310000000000151011444433100000000001551000111111000000000015551000000000000000000001110000000000000000000000000000000000000000000000000000000000000000001100000000000000001100012210011111111110012210124421122222222221124421124422222555555222224421122222222225522222222221155222222555555222222551122222222225522222222221155222222555555222222551122778222222222222877221155227822222222228722551012222222246642222222210015244444446644444442510001244447466664744442100000144444444444444445210000012224444444422211251000155222444444222551521000122222144441222221221001552222144441222252510001222222144441222252100001552222214412222255100001222222214412222222100001464644414414446464100000111111101101111111000010000000100001000000010171000001510015100000171155100015151151510001551156510001551155100015651015651012222222210156510015565122222222221565510001556527822228725655100001555652229922256555100000155564229922465551000000155564422224465551000000015556422224655510000000001556422224655100000000000156442244651000000000000014422224410000000000000001222222100000000000000001265562100000000000000015655556510000000000000156515515651000000000001565115511565100000000015651156651156510000000176510156651015671000000011101561165101110000000000001710017100000000000000000100001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100000000000000000000155510000000000000000000558551100000000000000000555555910000000000000000165561100011111000000000015510001133333110000000015510013322222331000000001551132232223223100000013555132232223222310000132355533333333333331000322235552232223222231100322232255532223222231410333333333555333333334441655444444445554444444841556144411111556144414410166144410000111144411100011011100000000011100000000000000000000000000000000000000000000000000000000000000000000000000000000001000100000000000000000019101910000000000000000191919191000000000000000019919915100000000000000012222255610000000000000122222225561000000000001227822222556100000000012222222222355610000000122222222222235561001100144422422222235556115510014411442222223555615651017100142222222225565561001710142323232322255651000100142232323232222561000000144323232322222210000000014444444444222210000000012212211112212210000000152212251152212210000000012312310012312310000000012312310012312310000000199919991199919991000000011101110011101110000000000000000000000000000000000000000000000000000000000011110000000000000000000177771000000000000001100177771001100000000017710177771017710000000001710177771017100000000001771122221177100000011001333122221333100110177111313122221313111771017771151121121151177710001722162112211261227100001222122222222221222100111121222222222222121111777711428222222824117777177331444222222444133771017333144449944441333710001333314444444413333100111711112444444211117111777722222244442222227777177722222244442222227771011122222224422222221110000011122224422221110000000000014414414410000000000000001101101100000000000000000000000000000000000000000000000000000000000000000000000000000000000110000000000000011000001991000000000000199100019991111111111111199910019915555555555555519910001952222222222222259100000122622222222226221000001522266222222662225100001527778222222877725100001522788222222887225100001522222226622222225100000126222262262222621000001522662222222266225100001521888888888888125100001528787878787878825100001528888888888888825100001528787878787878825100000122888888888888221000000162222222222222261000000016622122221226610000000001166116611661100000000000011001100110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000001177100000000000000000117747100000000000000011774557100000000000001177455571100000000000117745556571710000000001774555655717710000000017455565565717571000000174556556557175571000011745565565557755710001122455655655555557100012222244555555555571000122782222222222222221010222222222232232232222121944222222222222222222221144444222322322322222210014444444444444442222221001144444554444411112210000011115561111100001100000000155610000000000000000000011100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010010000000000000000000191191000000000000000001991991100000000000000001995991510000000000000015522225551000000000000155222222556100000000000152782222255610000000000222222222255561000000000222222222555561000000000144442225555561111110100016444555556222222221510016616555652222222225561001611655622252225225561000100165225552255522610000000014222222222222100000000014444444444222100000000012212211112212210000000012212210012212210000000012212210012212210000000199919991199919991000000011101110011101110000000000000000000000000000000000010000000000000000000000151000000000000000000001551000000000000000000012222100000000000001111122782100000000000017777222222100000000000001111112222100000000000000000001222110000000000000000012222221100000000000000122522252211000000000001222225222225110000000001422522225222551000000001442222522225555100000000144422222255665510000000011444211111156510000000000119910000011100000000000019910000000000000000000119911100000000000000001559995510000000000000015655556551000000000000001566665110000000000000000111111000000000000000000000000000000000\";\n    // 9 RGB colours (indices 1..9) per species\n    bytes internal constant PLAIN = hex\"3b3b3b8fa89b6f8679c9cfc09a9a8a7a7a6ceeeeee222222b8ad8f3b3b3bd8d4cab2ada0efece4555555444444f4f4f43a3a3ac9b38a3b3b3bb47d6e935e51d9c3b4c49a77a17a5aeeeeee2222228a7a6a3b3b3b7c8a765e6b59b8b7a28f9a7f6c775feeeeee222222a0a0903b3b3ba9a27e878061d4cfb2b48f6a8f6f50eeeeee2222228d85723b3b3bc2a07a9e7f5de6dccbd0b896b09a7af2efe82222228b7a663b3b3b8c81706d6455b6ab96a392755d5446e8e2d52222229a8f7a3b3b3b7f93a3627584c5ced496a6b27a8995eeeeee2222229aa0a63b3b3bd6d3ccb3afa6ecebe69fb0b57f9095f4f4f4222222bfae843b3b3b8299a8637a89c9d1d6b9876d966a53eeeeee2222228e8678\";\n    bytes internal constant VIVID = hex\"0e2a3a1fb5a812807abff5e64fd2ff2a8fd6ffffffff3b3bffd54a2a2238ffffffcfd6e6fff7e01c1c2e3a3a5affffff2e8bffffcc333a0a14ff3b30c41e2affd0a0ffb000ff6a00ffffff1a0a0affd54a0d1a242f4a5a1d32409fe0c834c77b1f8a55ffffffffdd338fd3ff2a16062fc47a1b8e57fff0b0ff7a1ae0401affffff2a0a0affd7003a1400ff9a1fd9670afff4e0ffffffffd6e8ffffff2a0a3aff4d8a1a0f05b0702a7a4718e8c0702fbf9a157a62ffffffff2020d9a44106203a2f6bff1c43b8bfe4ff6fe3ff27a6d9ffffff0a0a2affffff1e2a3affffffd2dceff2fbff4fd8ff1f8fe0ffffff7a2affffd23a1a10302f9bd91d6aa6d5f1ffff4a2affb02affffff111111ffffff\";\n    bytes internal constant GOLD = hex\"3d2400ffcf3ad99a10fff2b0ffe680c98a08fffbe67a1a00ffffff\";\n    // aura / glow / frame colour per species (Spirit)\n    bytes internal constant AURA = hex\"4fd2ff8fc4ffffb00034e08bff7a1aff7ab82fe0b06fe3ff4fd8ffff5a2a\";\n    bytes internal constant HEXCH = \"0123456789abcdef\";\n\n    function _beast(uint256 sp) internal pure returns (string memory en, string memory zh) {\n        if (sp == 0) return (\"Azure Dragon\", unicode\"青龙\");\n        if (sp == 1) return (\"White Tiger\", unicode\"白虎\");\n        if (sp == 2) return (\"Vermilion Bird\", unicode\"朱雀\");\n        if (sp == 3) return (\"Black Tortoise\", unicode\"玄武\");\n        if (sp == 4) return (\"Qilin\", unicode\"麒麟\");\n        if (sp == 5) return (\"Nine-Tailed Fox\", unicode\"九尾狐\");\n        if (sp == 6) return (\"Taotie\", unicode\"饕餮\");\n        if (sp == 7) return (\"Kunpeng\", unicode\"鲲鹏\");\n        if (sp == 8) return (\"Baize\", unicode\"白泽\");\n        return (\"Bifang\", unicode\"毕方\");\n    }\n\n    function _tierName(uint256 t) internal pure returns (string memory) {\n        if (t == 0) return \"Common\";\n        if (t == 1) return \"Spirit\";\n        if (t == 2) return \"Rare\";\n        return \"Legendary\";\n    }\n\n    // ---- tiny byte writer (buffer has slack; copies 32-byte words) ----\n    function _w(bytes memory b, uint256 p, bytes memory s) private pure returns (uint256) {\n        uint256 n = s.length;\n        assembly {\n            let dst := add(add(b, 32), p)\n            let src := add(s, 32)\n            for { let i := 0 } lt(i, n) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) }\n        }\n        return p + n;\n    }\n\n    function _n(bytes memory b, uint256 p, uint256 v) private pure returns (uint256) {\n        if (v >= 100) { b[p++] = bytes1(uint8(48 + v / 100)); v %= 100; b[p++] = bytes1(uint8(48 + v / 10)); }\n        else if (v >= 10) b[p++] = bytes1(uint8(48 + v / 10));\n        b[p++] = bytes1(uint8(48 + v % 10));\n        return p;\n    }\n\n    function _col(bytes memory pal, uint256 off) private pure returns (bytes memory c) {\n        c = new bytes(6);\n        bytes memory h = HEXCH;\n        for (uint256 i; i < 3; ++i) {\n            uint8 x = uint8(pal[off + i]);\n            c[2 * i] = h[x >> 4];\n            c[2 * i + 1] = h[x & 15];\n        }\n    }\n\n    /// all cells equal to v, as one path of 1-pixel-high runs\n    function _paths(bytes memory b, uint256 p, bytes memory g, uint8 v, bytes memory fill) private pure returns (uint256) {\n        uint256 start = p;\n        p = _w(b, p, '<path fill=\"');\n        p = _w(b, p, fill);\n        p = _w(b, p, '\" d=\"');\n        uint256 mark = p;\n        bytes1 vv = bytes1(v);\n        for (uint256 y; y < 32; ++y) {\n            uint256 x;\n            while (x < 32) {\n                if (g[y * 32 + x] == vv) {\n                    uint256 x0 = x;\n                    while (x < 32 && g[y * 32 + x] == vv) ++x;\n                    b[p++] = \"M\";\n                    p = _n(b, p, x0);\n                    b[p++] = \" \";\n                    p = _n(b, p, y);\n                    b[p++] = \"h\";\n                    p = _n(b, p, x - x0);\n                    p = _w(b, p, \"v1h-\");\n                    p = _n(b, p, x - x0);\n                    b[p++] = \"z\";\n                } else {\n                    ++x;\n                }\n            }\n        }\n        if (p == mark) return start;\n        return _w(b, p, '\"/>');\n    }\n\n    /// sprite placed at (4,4) of a 32x32 grid; tier > 0 adds two aura rings (values 10, 11)\n    function _grid(uint256 sp, uint256 tier) private pure returns (bytes memory g, uint256 bb) {\n        g = new bytes(1024);\n        bytes memory spr = SPRITES;\n        uint256 base = sp * 288;\n        uint256 minX = 99;\n        uint256 maxX;\n        uint256 maxY;\n        for (uint256 i; i < 576; ++i) {\n            uint8 byt = uint8(spr[base + i / 2]);\n            uint8 v = (i & 1) == 0 ? byt >> 4 : byt & 15;\n            if (v != 0) {\n                uint256 x = (i % 24) + 4;\n                uint256 y = (i / 24) + 4;\n                g[y * 32 + x] = bytes1(v);\n                if (x < minX) minX = x;\n                if (x > maxX) maxX = x;\n                if (y > maxY) maxY = y;\n            }\n        }\n        bb = (minX << 16) | (maxX << 8) | maxY;\n        if (tier == 0) return (g, bb);\n        for (uint256 r = 1; r <= 2; ++r) {\n            for (uint256 y = 1; y < 31; ++y) {\n                for (uint256 x = 1; x < 31; ++x) {\n                    uint256 c = y * 32 + x;\n                    if (g[c] != 0) continue;\n                    if (_ring(g[c - 1], r) || _ring(g[c + 1], r) || _ring(g[c - 32], r) || _ring(g[c + 32], r)) {\n                        g[c] = bytes1(uint8(9 + r));\n                    }\n                }\n            }\n        }\n    }\n\n    function _ring(bytes1 v, uint256 r) private pure returns (bool) {\n        uint8 u = uint8(v);\n        return r == 1 ? (u >= 1 && u <= 9) : u == 10;\n    }\n\n    function _bg(bytes memory b, uint256 p, uint256 tier, bytes memory accent) private pure returns (uint256) {\n        if (tier == 0) {\n            return _w(b, p, '<rect width=\"32\" height=\"32\" fill=\"#efe7d4\"/><path fill=\"#d8ccb0\" fill-rule=\"evenodd\" d=\"M0 0h32v32H0zM1 1v30h30V1z\"/>');\n        }\n        if (tier == 1) {\n            p = _w(b, p, '<defs><radialGradient id=\"g\"><stop offset=\"0\" stop-color=\"#');\n            p = _w(b, p, accent);\n            p = _w(b, p, '\" stop-opacity=\".55\"/><stop offset=\"1\" stop-color=\"#');\n            p = _w(b, p, accent);\n            p = _w(b, p, '\" stop-opacity=\"0\"/></radialGradient></defs><rect width=\"32\" height=\"32\" fill=\"#0b1426\"/><rect width=\"32\" height=\"32\" fill=\"url(#g)\"/>');\n            return _w(b, p, '<path fill=\"#fff\" fill-opacity=\".13\" d=\"M2 27h2v-1h3v1h2v1h1v1H1v-1h1zM22 4h2v-1h3v1h2v1h1v1h-9v-1h1z\"/>');\n        }\n        if (tier == 2) {\n            p = _w(b, p, '<defs><radialGradient id=\"g\"><stop offset=\"0\" stop-color=\"#ffcf3a\" stop-opacity=\".5\"/><stop offset=\"1\" stop-color=\"#ffcf3a\" stop-opacity=\"0\"/></radialGradient></defs>');\n            p = _w(b, p, '<rect width=\"32\" height=\"32\" fill=\"#2b0609\"/><rect width=\"32\" height=\"32\" fill=\"url(#g)\"/>');\n            return _w(b, p, '<path fill=\"#ffcf3a\" fill-opacity=\".22\" d=\"M2 27h2v-1h3v1h2v1h1v1H1v-1h1zM22 4h2v-1h3v1h2v1h1v1h-9v-1h1z\"/>');\n        }\n        p = _w(b, p, '<defs><linearGradient id=\"h\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#ff4f7b\"/><stop offset=\".2\" stop-color=\"#ffa64d\"/><stop offset=\".38\" stop-color=\"#ffef5c\"/><stop offset=\".56\" stop-color=\"#4dffa6\"/><stop offset=\".74\" stop-color=\"#4dc3ff\"/><stop offset=\".9\" stop-color=\"#9a6bff\"/><stop offset=\"1\" stop-color=\"#ff6bd8\"/></linearGradient>');\n        p = _w(b, p, '<radialGradient id=\"g\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".5\"/><stop offset=\".6\" stop-color=\"#fff\" stop-opacity=\"0\"/></radialGradient></defs>');\n        p = _w(b, p, '<rect width=\"32\" height=\"32\" fill=\"url(#h)\"/><rect width=\"32\" height=\"32\" fill=\"url(#g)\"/>');\n        return _w(b, p, '<path fill=\"#fff\" fill-opacity=\".25\" d=\"M0 22L22 0h5L0 27zM9 32L32 9v3L12 32z\"/>');\n    }\n\n    function _frame(bytes memory b, uint256 p, uint256 tier, bytes memory accent) private pure returns (uint256) {\n        if (tier == 1) {\n            p = _w(b, p, '<path fill=\"#');\n            p = _w(b, p, accent);\n            return _w(b, p, '\" fill-opacity=\".85\" fill-rule=\"evenodd\" d=\"M1 1h30v30H1zM2 2v28h28V2z\"/>');\n        }\n        if (tier == 2) {\n            return _w(b, p, '<path fill=\"#ffcf3a\" fill-rule=\"evenodd\" d=\"M0 0h32v32H0zM1 1v30h30V1zM2 2h28v28H2zM3 3v26h26V3z\"/><path fill=\"#fff6c4\" d=\"M0 0h3v3H0zM29 0h3v3h-3zM0 29h3v3H0zM29 29h3v3h-3z\"/>');\n        }\n        if (tier == 3) {\n            return _w(b, p, '<path fill=\"#150a2e\" fill-rule=\"evenodd\" d=\"M0 0h32v32H0zM1 1v30h30V1z\"/><path fill=\"#fff\" fill-rule=\"evenodd\" d=\"M1 1h30v30H1zM2 2v28h28V2z\"/><path fill=\"#fff\" d=\"M0 0h3v3H0zM29 0h3v3h-3zM0 29h3v3H0zM29 29h3v3h-3z\"/><path fill=\"#ffd700\" d=\"M1 1h1v1H1zM30 1h1v1h-1zM1 30h1v1H1zM30 30h1v1h-1z\"/>');\n        }\n        return p;\n    }\n\n    /// stars / sparkles on empty background cells, positions from the token seed\n    function _stars(bytes memory b, uint256 p, bytes memory g, uint256 tier, uint256 seed) private pure returns (uint256) {\n        if (tier == 0) return p;\n        p = _w(b, p, tier == 2 ? bytes('<path fill=\"#ffe680\" d=\"') : bytes('<path fill=\"#fff\" fill-opacity=\".85\" d=\"'));\n        uint256 cnt = tier == 1 ? 7 : 9;\n        for (uint256 k; k < cnt; ++k) {\n            uint256 r = uint256(keccak256(abi.encode(seed, k)));\n            uint256 x = 4 + (r & 0xff) % 24;\n            uint256 y = 4 + ((r >> 8) & 0xff) % 24;\n            if (g[y * 32 + x] != 0) continue;\n            bool big = tier > 1 && k % 3 == 0 && g[y * 32 + x - 1] == 0 && g[y * 32 + x + 1] == 0 && g[y * 32 + x - 32] == 0 && g[y * 32 + x + 32] == 0;\n            b[p++] = \"M\";\n            p = _n(b, p, big ? x - 1 : x);\n            b[p++] = \" \";\n            p = _n(b, p, y);\n            p = _w(b, p, big ? bytes(\"h3v1h-3zM\") : bytes(\"h1v1h-1z\"));\n            if (big) {\n                p = _n(b, p, x);\n                b[p++] = \" \";\n                p = _n(b, p, y - 1);\n                p = _w(b, p, \"h1v3h-1z\");\n            }\n        }\n        return _w(b, p, '\"/>');\n    }\n\n    function _svg(uint256 seed, uint256 sp, uint256 tier) internal pure returns (bytes memory b) {\n        (bytes memory g, uint256 bb) = _grid(sp, tier);\n        bytes memory pal = tier == 0 ? PLAIN : (tier == 2 ? GOLD : VIVID);\n        uint256 po = tier == 2 ? 0 : sp * 27;\n        bytes memory accent = _col(AURA, sp * 3);\n        b = new bytes(32000);\n        uint256 p = _w(b, 0, '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" shape-rendering=\"crispEdges\">');\n        p = _bg(b, p, tier, accent);\n        p = _stars(b, p, g, tier, seed);\n        {\n            // ground shadow under the beast\n            uint256 minX = bb >> 16;\n            uint256 w = ((bb >> 8) & 0xff) - minX + 1;\n            uint256 y = (bb & 0xff) + 1;\n            if (w > 6 && y < 31) {\n                p = _w(b, p, '<path fill=\"#000\" fill-opacity=\".18\" d=\"M');\n                p = _n(b, p, minX + 2);\n                b[p++] = \" \";\n                p = _n(b, p, y);\n                b[p++] = \"h\";\n                p = _n(b, p, w - 4);\n                p = _w(b, p, 'v1h-');\n                p = _n(b, p, w - 4);\n                p = _w(b, p, 'z\"/>');\n            }\n        }\n        if (tier > 0) {\n            if (tier == 3) {\n                p = _paths(b, p, g, 11, '#fff\" fill-opacity=\".6');\n                p = _paths(b, p, g, 10, \"#fff\");\n            } else {\n                bytes memory aura = tier == 1 ? bytes.concat(\"#\", accent) : bytes(\"#ffe680\");\n                p = _paths(b, p, g, 11, bytes.concat(aura, '\" fill-opacity=\".35'));\n                p = _paths(b, p, g, 10, bytes.concat(aura, '\" fill-opacity=\".9'));\n            }\n        }\n        for (uint8 v = 1; v <= 9; ++v) {\n            p = _paths(b, p, g, v, bytes.concat(\"#\", _col(pal, po + (v - 1) * 3)));\n        }\n        p = _frame(b, p, tier, accent);\n        p = _w(b, p, \"</svg>\");\n        assembly { mstore(b, p) }\n    }\n\n    /// Raw SVG for any species (0-9) and tier (0-3), for previews and mint pages\n    function previewSVG(uint256 species, uint256 tier) external pure returns (string memory) {\n        return string(_svg(0, species % 10, tier % 4));\n    }\n\n    function tokenURI(uint256 tokenId) external view returns (string memory) {\n        ownerOf(tokenId);\n        uint256 kind = (_tok[tokenId] >> 160) & 0xff;\n        uint256 sp = kind & 15;\n        uint256 tier = kind >> 4;\n        bytes memory json = abi.encodePacked(_head(tokenId, sp, tier), '\"image\":\"data:image/svg+xml;base64,', _b64(_svg(uint256(keccak256(abi.encode(tokenId))), sp, tier)), '\"}');\n        return string.concat(\"data:application/json;base64,\", _b64(json));\n    }\n\n    function _head(uint256 tokenId, uint256 sp, uint256 tier) internal pure returns (bytes memory) {\n        (string memory en, string memory zh) = _beast(sp);\n        return abi.encodePacked(\n            '{\"name\":\"SHANHAI BEASTS #', _dec(tokenId), \" - \", en, \" \", zh,\n            '\",\"description\":\"SHANHAI BEASTS ', unicode\"山海神兽\", ': 5,000 fully on-chain pixel beasts of the Classic of Mountains and Seas, for .ai Agent ID holders. First mint free (Common), paid mints are Spirit, a Rare bonus at 10 and a Legendary bonus at 30 per wallet. No admin. A collectible, not an investment.\",',\n            '\"attributes\":[{\"trait_type\":\"Beast\",\"value\":\"', en, '\"},{\"trait_type\":\"Chinese Name\",\"value\":\"', zh,\n            '\"},{\"trait_type\":\"Tier\",\"value\":\"', _tierName(tier), '\"}],'\n        );\n    }\n\n    /// Collection-level metadata (name, image, royalty) for marketplaces\n    function contractURI() external view returns (string memory) {\n        bytes memory json = abi.encodePacked(\n            '{\"name\":\"SHANHAI BEASTS ', unicode\"山海神兽\",\n            '\",\"description\":\"5,000 fully on-chain pixel beasts of the Classic of Mountains and Seas for .ai Agent ID holders. Free first mint, Spirit paid mints, Rare and Legendary bonuses. No admin.\",\"image\":\"data:image/svg+xml;base64,',\n            _b64(_svg(0, 0, 3)), '\",\"seller_fee_basis_points\":100,\"fee_recipient\":\"', _addr(creator), '\"}'\n        );\n        return string.concat(\"data:application/json;base64,\", _b64(json));\n    }\n\n    function _dec(uint256 v) internal pure returns (string memory) {\n        if (v == 0) return \"0\";\n        uint256 t = v;\n        uint256 len;\n        while (t != 0) { len++; t /= 10; }\n        bytes memory o = new bytes(len);\n        while (v != 0) { len--; o[len] = bytes1(uint8(48 + v % 10)); v /= 10; }\n        return string(o);\n    }\n\n    function _addr(address a) internal pure returns (string memory) {\n        bytes memory o = new bytes(42);\n        bytes memory h = HEXCH;\n        o[0] = \"0\";\n        o[1] = \"x\";\n        uint160 v = uint160(a);\n        for (uint256 i; i < 20; ++i) {\n            uint8 x = uint8(v >> (8 * (19 - i)));\n            o[2 + 2 * i] = h[x >> 4];\n            o[3 + 2 * i] = h[x & 15];\n        }\n        return string(o);\n    }\n\n    /// Base64 (adapted from OpenZeppelin Contracts, MIT)\n    function _b64(bytes memory data) internal pure returns (string memory result) {\n        if (data.length == 0) return \"\";\n        string memory table = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n        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            let afterCache := mload(add(endPtr, 0x20))\n            mstore(add(endPtr, 0x20), 0x00)\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                mstore8(add(resultPtr, 1), mload(add(tablePtr, and(shr(12, input), 0x3F))))\n                mstore8(add(resultPtr, 2), mload(add(tablePtr, and(shr(6, input), 0x3F))))\n                mstore8(add(resultPtr, 3), mload(add(tablePtr, and(input, 0x3F))))\n                resultPtr := add(resultPtr, 4)\n            }\n            mstore(add(endPtr, 0x20), afterCache)\n            switch mod(mload(data), 3)\n            case 1 {\n                mstore8(sub(resultPtr, 1), 0x3d)\n                mstore8(sub(resultPtr, 2), 0x3d)\n            }\n            case 2 {\n                mstore8(sub(resultPtr, 1), 0x3d)\n            }\n        }\n    }\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"_creator","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"name":"InvalidToken","type":"error","inputs":[]},{"name":"MintClosed","type":"error","inputs":[]},{"name":"NotAgent","type":"error","inputs":[]},{"name":"NotOwnerNorApproved","type":"error","inputs":[]},{"name":"NothingToMint","type":"error","inputs":[]},{"name":"SoldOut","type":"error","inputs":[]},{"name":"TransferFailed","type":"error","inputs":[]},{"name":"UnsafeRecipient","type":"error","inputs":[]},{"name":"WalletLimit","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":"Milestone","type":"event","inputs":[{"name":"collector","type":"address","indexed":true,"internalType":"address"},{"name":"reached","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"tier","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":"AGENT_ID","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAgentId"}],"stateMutability":"view"},{"name":"LEGENDARY_AT","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_SLOTS_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_16_TO_30","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"PRICE_2_TO_5","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"PRICE_6_TO_15","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"RARE_AT","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"ROYALTY_BPS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint96","internalType":"uint96"}],"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":"o","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"contractURI","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"creator","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"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":"o","type":"address","internalType":"address"},{"name":"operator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"mint","type":"function","inputs":[{"name":"paid","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"name":"mintEnd","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"mintOpen","type":"function","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"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":"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":"o","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"previewSVG","type":"function","inputs":[{"name":"species","type":"uint256","internalType":"uint256"},{"name":"tier","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"pure"},{"name":"quote","type":"function","inputs":[{"name":"minter","type":"address","internalType":"address"},{"name":"paid","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"tokens","type":"uint256","internalType":"uint256"},{"name":"cost","type":"uint256","internalType":"uint256"},{"name":"getsFree","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"remaining","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"royaltyInfo","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"},{"name":"salePrice","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"},{"name":"","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":"slotPrice","type":"function","inputs":[{"name":"k","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"},{"name":"slotsMinted","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"speciesOf","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"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":"tierMinted","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"tierOf","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":"tokensOfOwner","type":"function","inputs":[{"name":"o","type":"address","internalType":"address"}],"outputs":[{"name":"ids","type":"uint256[]","internalType":"uint256[]"}],"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":"57647539","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-10-30T07:12:22Z","match":"exact_match","chainId":"56","address":"0xFF47FebcdbA7A9c263999c7FAaC60D01ddA0ee15"}