{"language":"Solidity","sources":{"src/v2/Market.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ninterface IERC721Like {\n    function ownerOf(uint256 id) external view returns (address);\n    function transferFrom(address from, address to, uint256 id) external;\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n    function getApproved(uint256 id) external view returns (address);\n}\n\n/// @title Market — BNBFARM 官方二级市场（挂单 + 2.5% 手续费进金库）\n/// @notice 动物**留在卖家钱包里**，挂单期间照常产毛；成交时才转移。\n///         手续费是常量，合约没有 owner，改不了。\ncontract Market {\n    IERC721Like public immutable animals;\n    address public constant TREASURY = 0x29F64d560bB694963750F7F1822155DBaF440388;\n    uint256 public constant FEE_BPS = 250;   // 2.5%\n    uint256 public constant BPS = 10_000;\n\n    struct Listing {\n        address seller;\n        uint96 price;      // wei\n        uint64 listedAt;\n    }\n\n    mapping(uint256 => Listing) public listings;\n    uint256[] public listedIds;\n\n    event Listed(uint256 indexed id, address indexed seller, uint256 price);\n    event PriceChanged(uint256 indexed id, uint256 price);\n    event Cancelled(uint256 indexed id);\n    event Bought(uint256 indexed id, address indexed buyer, address indexed seller, uint256 price, uint256 fee);\n\n    error NotOwner();\n    error NotListed();\n    error NotApproved();\n    error BadPrice();\n    error WrongValue();\n    error Gone();\n    error SelfBuy();\n\n    constructor(IERC721Like animals_) {\n        animals = animals_;\n    }\n\n    function listedCount() external view returns (uint256) {\n        return listedIds.length;\n    }\n\n    /// @notice 挂单。动物不离开你的钱包，还能继续产毛。\n    function list(uint256 id, uint96 price) external {\n        if (animals.ownerOf(id) != msg.sender) revert NotOwner();\n        if (price == 0) revert BadPrice();\n        if (!_approved(msg.sender, id)) revert NotApproved();\n\n        if (listings[id].seller == address(0)) listedIds.push(id);\n        listings[id] = Listing(msg.sender, price, uint64(block.timestamp));\n        emit Listed(id, msg.sender, price);\n    }\n\n    function relist(uint256 id, uint96 price) external {\n        Listing storage l = listings[id];\n        if (l.seller != msg.sender) revert NotListed();\n        if (price == 0) revert BadPrice();\n        l.price = price;\n        emit PriceChanged(id, price);\n    }\n\n    function cancel(uint256 id) external {\n        Listing storage l = listings[id];\n        if (l.seller != msg.sender) revert NotListed();\n        delete listings[id];\n        emit Cancelled(id);\n    }\n\n    /// @notice 按挂单价买入。多付不退，少付 revert —— 前端永远传准确金额。\n    function buy(uint256 id) external payable {\n        Listing memory l = listings[id];\n        if (l.seller == address(0)) revert NotListed();\n        if (msg.sender == l.seller) revert SelfBuy();\n        if (msg.value != l.price) revert WrongValue();\n        if (animals.ownerOf(id) != l.seller) revert Gone();\n        if (!_approved(l.seller, id)) revert NotApproved();\n\n        delete listings[id];\n\n        uint256 fee = (msg.value * FEE_BPS) / BPS;\n        uint256 toSeller = msg.value - fee;\n\n        animals.transferFrom(l.seller, msg.sender, id);\n\n        (bool a, ) = TREASURY.call{value: fee}(\"\");\n        require(a, \"fee\");\n        (bool b, ) = l.seller.call{value: toSeller}(\"\");\n        require(b, \"seller\");\n\n        emit Bought(id, msg.sender, l.seller, msg.value, fee);\n    }\n\n    /// @notice 挂单还有效吗（动物没被转走、授权还在）\n    function isLive(uint256 id) public view returns (bool) {\n        Listing memory l = listings[id];\n        if (l.seller == address(0)) return false;\n        if (animals.ownerOf(id) != l.seller) return false;\n        return _approved(l.seller, id);\n    }\n\n    function _approved(address owner, uint256 id) internal view returns (bool) {\n        return animals.isApprovedForAll(owner, address(this)) || animals.getApproved(id) == address(this);\n    }\n}\n"}},"settings":{"remappings":["lib/openzeppelin-contracts/:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/","lib/openzeppelin-contracts/:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]}},"evmVersion":"cancun","viaIR":true,"libraries":{}}}
