{"sources":{"FenghuoBageng.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\n/// @title FenghuoBageng — 烽火八更\n/// @notice Agent-ID-only survival watch game on BSC.\n/// Pods are ERC-721 beacon towers. Boarding: first 500 free (1 per Agent ID\n/// wallet), then 0.001 BNB per pod, hard cap 3,000 pods (fleets of 1,000).\n/// The game runs 8 watches of 12 hours from gameStart. Each pod must light\n/// its fire (heartbeat) once per watch, starting from its boarding watch.\n/// A missed watch is final once its 4-hour ember grace (inside the next\n/// watch) expires. Oil reserves auto-cover final misses; a dead pod can be\n/// revived for 0.002 BNB during the game. Every payment is split in the same\n/// transaction: 70% to the prize pot, 30% forwarded to the treasury.\n/// After the final watch plus grace, anyone seals the game; pods still\n/// burning split the pot equally. No admin functions anywhere.\ninterface IAgentIdRegistry {\n    function balanceOf(address account) external view returns (uint256);\n}\n\ncontract FenghuoBageng {\n    // ---------------- ERC-721 ----------------\n    string public name = unicode\"烽火八更\";\n    string public symbol = unicode\"烽火\";\n\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    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\n    function balanceOf(address owner) public view returns (uint256) {\n        require(owner != address(0), \"zero address\");\n        return _balances[owner];\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) external {\n        address o = ownerOf(tokenId);\n        require(msg.sender == o || _operatorApprovals[o][msg.sender], \"not authorized\");\n        _tokenApprovals[tokenId] = to;\n        emit Approval(o, to, tokenId);\n    }\n\n    function getApproved(uint256 tokenId) external view returns (address) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\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 owner, address operator) external view returns (bool) {\n        return _operatorApprovals[owner][operator];\n    }\n\n    function transferFrom(address from, address to, uint256 tokenId) public {\n        address o = ownerOf(tokenId);\n        require(o == from, \"wrong from\");\n        require(\n            msg.sender == o || _tokenApprovals[tokenId] == msg.sender || _operatorApprovals[o][msg.sender],\n            \"not authorized\"\n        );\n        require(to != address(0), \"zero to\");\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 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            (bool ok, bytes memory ret) = to.call(\n                abi.encodeWithSignature(\"onERC721Received(address,address,uint256,bytes)\", msg.sender, from, tokenId, data)\n            );\n            require(ok && ret.length == 32 && abi.decode(ret, (bytes4)) == bytes4(0x150b7a02), \"unsafe recipient\");\n        }\n    }\n\n    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {\n        return interfaceId == 0x80ac58cd || interfaceId == 0x01ffc9a7;\n    }\n\n    function tokenURI(uint256 tokenId) external view returns (string memory) {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        Pod storage p = pods[tokenId - 1];\n        string memory json = string(\n            abi.encodePacked(\n                unicode'{\"name\":\"烽火台 #',\n                _toString(tokenId),\n                unicode'\",\"description\":\"烽火八更守夜烽火台。八更守夜，烽火不熄，天明之时，生者分金。\",\"attributes\":[{\"trait_type\":\"登塔更次\",\"value\":',\n                _toString(p.boardWatch + 1),\n                unicode'},{\"trait_type\":\"舰队\",\"value\":',\n                _toString((tokenId - 1) / FLEET_SIZE + 1),\n                \"}]}\"\n            )\n        );\n        return string(abi.encodePacked(\"data:application/json;base64,\", _b64(bytes(json))));\n    }\n\n    // ---------------- Game constants ----------------\n    uint256 public constant WATCH_SECONDS = 12 hours;\n    uint256 public constant GRACE_SECONDS = 4 hours;\n    uint8 public constant TOTAL_WATCHES = 8;\n    uint256 public constant FREE_QUOTA = 500;\n    uint256 public constant MAX_PODS = 3000;\n    uint256 public constant FLEET_SIZE = 1000;\n    uint256 public constant PRICE = 0.001 ether;\n    uint256 public constant REVIVE_PRICE = 0.002 ether;\n    uint256 public constant OIL_PRICE = 0.0005 ether;\n    uint8 public constant RELIEF_LAST_WATCH = 5;\n\n    IAgentIdRegistry public immutable registry;\n    address public immutable treasury;\n    uint256 public immutable gameStart;\n\n    struct Pod {\n        uint8 boardWatch; // first watch this pod must light (0 for pre-game boarders)\n        uint8 beats;      // bitmask of lit watches\n        uint8 oil;        // oil reserves bought (each auto-covers one final miss)\n        uint8 forgiven;   // misses forgiven via revives\n        bool claimed;\n    }\n\n    Pod[] public pods;\n\n    uint256 public totalBoarded;\n    uint256 public freeUsed;\n    mapping(address => bool) public freeClaimed;\n    uint256 public pot;\n    bool public sealedFlag;\n    uint256 public survivors;\n    uint256 public sharePerPod;\n\n    event Boarded(uint256 indexed tokenId, address indexed owner, uint8 boardWatch, bool free);\n    event Heartbeat(uint256 indexed tokenId, uint8 watch);\n    event OilBought(uint256 indexed tokenId, uint8 oilTotal);\n    event Revived(uint256 indexed tokenId, uint256 forgivenAdded);\n    event Sealed(uint256 survivors, uint256 sharePerPod);\n    event Claimed(uint256 indexed tokenId, address indexed owner, uint256 amount);\n\n    bool private _lock;\n    modifier nonReentrant() {\n        require(!_lock, \"reentrant\");\n        _lock = true;\n        _;\n        _lock = false;\n    }\n\n    constructor(address registry_, address treasury_, uint256 gameStart_) {\n        require(registry_ != address(0), \"registry zero\");\n        require(treasury_ != address(0), \"treasury zero\");\n        require(gameStart_ > block.timestamp + 1 hours, \"start too soon\");\n        registry = IAgentIdRegistry(registry_);\n        treasury = treasury_;\n        gameStart = gameStart_;\n    }\n\n    // ---------------- Time helpers ----------------\n    function gameEnd() public view returns (uint256) {\n        return gameStart + uint256(TOTAL_WATCHES) * WATCH_SECONDS;\n    }\n\n    function sealTime() public view returns (uint256) {\n        return gameEnd() + GRACE_SECONDS;\n    }\n\n    function currentWatch() public view returns (uint8) {\n        if (block.timestamp < gameStart) return 0;\n        uint256 w = (block.timestamp - gameStart) / WATCH_SECONDS;\n        if (w > 7) w = 7;\n        return uint8(w);\n    }\n\n    /// @notice The watch whose ember grace is open right now, or -1.\n    function _graceWatch() internal view returns (int256) {\n        if (block.timestamp < gameStart) return -1;\n        if (block.timestamp >= gameEnd()) {\n            if (block.timestamp < sealTime()) return 7;\n            return -1;\n        }\n        uint256 elapsed = block.timestamp - gameStart;\n        uint256 w = elapsed / WATCH_SECONDS;\n        uint256 within = elapsed % WATCH_SECONDS;\n        if (w >= 1 && within < GRACE_SECONDS) return int256(w - 1);\n        return -1;\n    }\n\n    function _missesIn(Pod storage p, uint8 fromW, uint8 toW) internal view returns (uint256) {\n        uint256 m = 0;\n        for (uint8 w = fromW; w <= toW; w++) {\n            if ((p.beats & (uint8(1) << w)) == 0) m++;\n            if (w == 7) break;\n        }\n        return m;\n    }\n\n    /// @notice Misses whose grace has fully expired at the current time.\n    function _finalMisses(Pod storage p) internal view returns (uint256) {\n        if (block.timestamp < gameStart) return 0;\n        int256 lastFinal;\n        if (block.timestamp >= sealTime()) {\n            lastFinal = 7;\n        } else {\n            int256 gw = _graceWatch();\n            if (gw >= 0) lastFinal = gw - 1;\n            else lastFinal = int256(uint256(currentWatch())) - 1;\n        }\n        if (lastFinal < int256(uint256(p.boardWatch))) return 0;\n        return _missesIn(p, p.boardWatch, uint8(uint256(lastFinal)));\n    }\n\n    function _cover(Pod storage p) internal view returns (uint256) {\n        return uint256(p.oil) + uint256(p.forgiven);\n    }\n\n    function _isAlive(Pod storage p) internal view returns (bool) {\n        return _finalMisses(p) <= _cover(p);\n    }\n\n    function _isSurvivor(Pod storage p) internal view returns (bool) {\n        return _missesIn(p, p.boardWatch, 7) <= _cover(p);\n    }\n\n    // ---------------- Payments ----------------\n    /// @notice Splits a payment: 70% stays as pot, 30% to treasury now; refunds excess.\n    function _collect(uint256 price) internal {\n        require(msg.value >= price, \"insufficient payment\");\n        uint256 potPart = (price * 70) / 100;\n        pot += potPart;\n        (bool ok, ) = treasury.call{value: price - potPart}(\"\");\n        require(ok, \"treasury transfer failed\");\n        uint256 excess = msg.value - price;\n        if (excess > 0) {\n            (bool ok2, ) = msg.sender.call{value: excess}(\"\");\n            require(ok2, \"refund failed\");\n        }\n    }\n\n    // ---------------- Boarding ----------------\n    function _mintPod(address to, uint8 boardWatch, bool free_) internal returns (uint256 id) {\n        id = pods.length + 1;\n        pods.push(Pod(boardWatch, 0, 0, 0, false));\n        _owners[id] = to;\n        _balances[to] += 1;\n        totalBoarded += 1;\n        emit Transfer(address(0), to, id);\n        emit Boarded(id, to, boardWatch, free_);\n    }\n\n    function boardFree() external nonReentrant {\n        require(block.timestamp < gameStart, \"boarding closed\");\n        require(!freeClaimed[msg.sender], \"free already used\");\n        require(freeUsed < FREE_QUOTA, \"free quota full\");\n        require(totalBoarded < MAX_PODS, \"all fleets full\");\n        require(registry.balanceOf(msg.sender) > 0, \"no agent id\");\n        freeClaimed[msg.sender] = true;\n        freeUsed += 1;\n        _mintPod(msg.sender, 0, true);\n    }\n\n    function boardPaid() external payable nonReentrant {\n        require(registry.balanceOf(msg.sender) > 0, \"no agent id\");\n        require(totalBoarded < MAX_PODS, \"all fleets full\");\n        uint8 bw;\n        if (block.timestamp < gameStart) {\n            bw = 0;\n        } else {\n            require(block.timestamp < gameEnd(), \"game over\");\n            uint8 cw = currentWatch();\n            require(cw <= RELIEF_LAST_WATCH, \"relief closed\");\n            bw = cw;\n        }\n        _collect(PRICE);\n        _mintPod(msg.sender, bw, false);\n    }\n\n    // ---------------- Gameplay ----------------\n    function heartbeat(uint256 tokenId) external {\n        require(_owners[tokenId] == msg.sender, \"not owner\");\n        require(block.timestamp >= gameStart, \"not started\");\n        require(block.timestamp < sealTime(), \"game sealed\");\n        Pod storage p = pods[tokenId - 1];\n        require(_isAlive(p), \"pod is dead\");\n        int256 gw = _graceWatch();\n        if (gw >= 0 && gw >= int256(uint256(p.boardWatch))) {\n            uint8 g = uint8(uint256(gw));\n            if ((p.beats & (uint8(1) << g)) == 0) {\n                p.beats |= (uint8(1) << g);\n                emit Heartbeat(tokenId, g);\n                return;\n            }\n        }\n        uint8 cw = currentWatch();\n        require(block.timestamp < gameEnd(), \"only final grace remains\");\n        require(cw >= p.boardWatch, \"not boarded yet\");\n        require((p.beats & (uint8(1) << cw)) == 0, \"already lit\");\n        p.beats |= (uint8(1) << cw);\n        emit Heartbeat(tokenId, cw);\n    }\n\n    function buyOil(uint256 tokenId) external payable nonReentrant {\n        require(_owners[tokenId] == msg.sender, \"not owner\");\n        require(block.timestamp < gameEnd(), \"game over\");\n        Pod storage p = pods[tokenId - 1];\n        require(_isAlive(p), \"pod is dead\");\n        require(p.oil < TOTAL_WATCHES, \"oil full\");\n        p.oil += 1;\n        _collect(OIL_PRICE);\n        emit OilBought(tokenId, p.oil);\n    }\n\n    function revive(uint256 tokenId) external payable nonReentrant {\n        require(_owners[tokenId] == msg.sender, \"not owner\");\n        require(block.timestamp >= gameStart && block.timestamp < gameEnd(), \"not in game\");\n        Pod storage p = pods[tokenId - 1];\n        uint256 fm = _finalMisses(p);\n        uint256 cover = _cover(p);\n        require(fm > cover, \"pod is alive\");\n        uint256 add = fm - cover;\n        p.forgiven += uint8(add);\n        _collect(REVIVE_PRICE);\n        emit Revived(tokenId, add);\n    }\n\n    // ---------------- Seal & claim ----------------\n    function finalize() external {\n        require(block.timestamp >= sealTime(), \"too early\");\n        require(!sealedFlag, \"already sealed\");\n        sealedFlag = true;\n        uint256 count = 0;\n        for (uint256 i = 0; i < pods.length; i++) {\n            if (_isSurvivor(pods[i])) count++;\n        }\n        survivors = count;\n        sharePerPod = count > 0 ? pot / count : 0;\n        emit Sealed(count, sharePerPod);\n    }\n\n    function claim(uint256 tokenId) external nonReentrant {\n        require(sealedFlag, \"not sealed\");\n        require(_owners[tokenId] == msg.sender, \"not owner\");\n        Pod storage p = pods[tokenId - 1];\n        require(!p.claimed, \"already claimed\");\n        require(_isSurvivor(p), \"not a survivor\");\n        p.claimed = true;\n        uint256 amt = sharePerPod;\n        pot -= amt;\n        (bool ok, ) = msg.sender.call{value: amt}(\"\");\n        require(ok, \"claim transfer failed\");\n        emit Claimed(tokenId, msg.sender, amt);\n    }\n\n    // ---------------- Views ----------------\n    /// @notice phase: 0 boarding, 1 playing, 2 final grace, 3 claimable\n    function status()\n        external\n        view\n        returns (uint8 phase, uint256 boarded, uint256 freeLeft, uint256 fleet, uint256 potAmount, uint256 start, uint256 seal, bool isSealed, uint256 surv)\n    {\n        if (block.timestamp < gameStart) phase = 0;\n        else if (block.timestamp < gameEnd()) phase = 1;\n        else if (block.timestamp < sealTime()) phase = 2;\n        else phase = 3;\n        boarded = totalBoarded;\n        freeLeft = FREE_QUOTA - freeUsed;\n        fleet = totalBoarded >= MAX_PODS ? 3 : (totalBoarded / FLEET_SIZE) + 1;\n        potAmount = pot;\n        start = gameStart;\n        seal = sealTime();\n        isSealed = sealedFlag;\n        surv = survivors;\n    }\n\n    /// @notice state: 0 burning, 1 embers (grace open on an unlit watch), 2 dead, 3 survivor (after end), 4 fallen (after end)\n    function podInfo(uint256 tokenId)\n        external\n        view\n        returns (address owner, uint8 boardWatch, uint8 beats, uint8 oil, uint8 forgiven, uint8 state, bool claimed)\n    {\n        require(_owners[tokenId] != address(0), \"nonexistent token\");\n        Pod storage p = pods[tokenId - 1];\n        owner = _owners[tokenId];\n        boardWatch = p.boardWatch;\n        beats = p.beats;\n        oil = p.oil;\n        forgiven = p.forgiven;\n        claimed = p.claimed;\n        if (block.timestamp >= sealTime()) {\n            state = _isSurvivor(p) ? 3 : 4;\n        } else if (!_isAlive(p)) {\n            state = 2;\n        } else {\n            int256 gw = _graceWatch();\n            if (gw >= 0 && gw >= int256(uint256(p.boardWatch)) && (p.beats & (uint8(1) << uint8(uint256(gw)))) == 0) {\n                state = 1;\n            } else {\n                state = 0;\n            }\n        }\n    }\n\n    function podsCount() external view returns (uint256) {\n        return pods.length;\n    }\n\n    // ---------------- Utils ----------------\n    function _toString(uint256 v) internal pure returns (string memory) {\n        if (v == 0) return \"0\";\n        uint256 digits;\n        uint256 t = v;\n        while (t > 0) { digits++; t /= 10; }\n        bytes memory b = new bytes(digits);\n        while (v > 0) { b[--digits] = 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        uint256 encodedLen = 4 * ((data.length + 2) / 3);\n        string memory result = new string(encodedLen + 32);\n        assembly {\n            let tablePtr := add(table, 1)\n            let resultPtr := add(result, 32)\n            let dataPtr := data\n            let endPtr := add(dataPtr, 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 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }\n            case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }\n            mstore(result, encodedLen)\n        }\n        return result;\n    }\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"registry_","type":"address","internalType":"address"},{"name":"treasury_","type":"address","internalType":"address"},{"name":"gameStart_","type":"uint256","internalType":"uint256"}],"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":"Boarded","type":"event","inputs":[{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"owner","type":"address","indexed":true,"internalType":"address"},{"name":"boardWatch","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"free","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"name":"Claimed","type":"event","inputs":[{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"owner","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"Heartbeat","type":"event","inputs":[{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"watch","type":"uint8","indexed":false,"internalType":"uint8"}],"anonymous":false},{"name":"OilBought","type":"event","inputs":[{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"oilTotal","type":"uint8","indexed":false,"internalType":"uint8"}],"anonymous":false},{"name":"Revived","type":"event","inputs":[{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"forgivenAdded","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"Sealed","type":"event","inputs":[{"name":"survivors","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"sharePerPod","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":"FLEET_SIZE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"FREE_QUOTA","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"GRACE_SECONDS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_PODS","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"OIL_PRICE","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":"RELIEF_LAST_WATCH","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"REVIVE_PRICE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"TOTAL_WATCHES","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"WATCH_SECONDS","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":"owner","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"boardFree","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"boardPaid","type":"function","inputs":[],"outputs":[],"stateMutability":"payable"},{"name":"buyOil","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"name":"claim","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"currentWatch","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"finalize","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"freeClaimed","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"freeUsed","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"gameEnd","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"gameStart","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":"heartbeat","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"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":"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":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"podInfo","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"owner","type":"address","internalType":"address"},{"name":"boardWatch","type":"uint8","internalType":"uint8"},{"name":"beats","type":"uint8","internalType":"uint8"},{"name":"oil","type":"uint8","internalType":"uint8"},{"name":"forgiven","type":"uint8","internalType":"uint8"},{"name":"state","type":"uint8","internalType":"uint8"},{"name":"claimed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"pods","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"boardWatch","type":"uint8","internalType":"uint8"},{"name":"beats","type":"uint8","internalType":"uint8"},{"name":"oil","type":"uint8","internalType":"uint8"},{"name":"forgiven","type":"uint8","internalType":"uint8"},{"name":"claimed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"podsCount","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"pot","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":"revive","type":"function","inputs":[{"name":"tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"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":"sealTime","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"sealedFlag","type":"function","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"setApprovalForAll","type":"function","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"approved","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"sharePerPod","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"status","type":"function","inputs":[],"outputs":[{"name":"phase","type":"uint8","internalType":"uint8"},{"name":"boarded","type":"uint256","internalType":"uint256"},{"name":"freeLeft","type":"uint256","internalType":"uint256"},{"name":"fleet","type":"uint256","internalType":"uint256"},{"name":"potAmount","type":"uint256","internalType":"uint256"},{"name":"start","type":"uint256","internalType":"uint256"},{"name":"seal","type":"uint256","internalType":"uint256"},{"name":"isSealed","type":"bool","internalType":"bool"},{"name":"surv","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"supportsInterface","type":"function","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"pure"},{"name":"survivors","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"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":"totalBoarded","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":"treasury","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"}],"matchId":"59053651","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-10T18:25:50Z","match":"exact_match","chainId":"56","address":"0x05f23bfeB3115d6E1905d039f2C70E6928846956"}