{"language":"Solidity","sources":{"src/v2/BidBoard.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ninterface IERC721B {\n    function ownerOf(uint256 id) external view returns (address);\n    function transferFrom(address from, address to, uint256 id) external;\n}\n\n/// @title BidBoard — 求购板：买家挂 BNB 出价，卖家一键成交\n/// @notice 钱托管在合约里，随时可撤；成交时 2.5% 进金库。\n///         一个动物同一时间只有一条最高求购，被超过时上一笔立即退回。\ncontract BidBoard {\n    IERC721B public immutable animals;\n    address public constant TREASURY = 0x29F64d560bB694963750F7F1822155DBaF440388;\n    uint256 public constant FEE_BPS = 250;\n    uint256 public constant BPS = 10_000;\n    uint256 public constant MIN_STEP_BPS = 500;   // 加价至少 5%\n\n    struct Bid {\n        address bidder;\n        uint96 amount;\n        uint64 at;\n    }\n\n    mapping(uint256 => Bid) public bids;\n    uint256[] public bidIds;\n\n    event BidPlaced(uint256 indexed id, address indexed bidder, uint256 amount);\n    event BidCancelled(uint256 indexed id, address indexed bidder, uint256 refund);\n    event Filled(uint256 indexed id, address indexed seller, address indexed buyer, uint256 amount, uint256 fee);\n\n    error NotOwner();\n    error OwnAnimal();\n    error NoBid();\n    error TooLow();\n    error NotBidder();\n\n    constructor(IERC721B animals_) {\n        animals = animals_;\n    }\n\n    function bidCount() external view returns (uint256) { return bidIds.length; }\n\n    /// @notice 挂求购。同一只动物再出价必须比上一笔高至少 5%。\n    function placeBid(uint256 id) external payable {\n        if (msg.value == 0) revert TooLow();\n        if (animals.ownerOf(id) == msg.sender) revert OwnAnimal();\n\n        Bid memory prev = bids[id];\n        if (prev.bidder != address(0)) {\n            uint256 min = uint256(prev.amount) * (BPS + MIN_STEP_BPS) / BPS;\n            if (msg.value < min) revert TooLow();\n            // 先清空再退款，防重入\n            delete bids[id];\n            (bool ok, ) = prev.bidder.call{value: prev.amount}(\"\");\n            require(ok, \"refund\");\n        } else {\n            bidIds.push(id);\n        }\n\n        bids[id] = Bid(msg.sender, uint96(msg.value), uint64(block.timestamp));\n        emit BidPlaced(id, msg.sender, msg.value);\n    }\n\n    function cancelBid(uint256 id) external {\n        Bid memory b = bids[id];\n        if (b.bidder != msg.sender) revert NotBidder();\n        delete bids[id];\n        (bool ok, ) = msg.sender.call{value: b.amount}(\"\");\n        require(ok, \"refund\");\n        emit BidCancelled(id, msg.sender, b.amount);\n    }\n\n    /// @notice 卖家接受求购：动物转给买家，97.5% 给你，2.5% 进金库。\n    function fillBid(uint256 id) external {\n        Bid memory b = bids[id];\n        if (b.bidder == address(0)) revert NoBid();\n        if (animals.ownerOf(id) != msg.sender) revert NotOwner();\n\n        delete bids[id];\n\n        uint256 fee = (uint256(b.amount) * FEE_BPS) / BPS;\n        uint256 toSeller = uint256(b.amount) - fee;\n\n        animals.transferFrom(msg.sender, b.bidder, id);\n\n        (bool a, ) = TREASURY.call{value: fee}(\"\");\n        require(a, \"fee\");\n        (bool c, ) = msg.sender.call{value: toSeller}(\"\");\n        require(c, \"seller\");\n\n        emit Filled(id, msg.sender, b.bidder, b.amount, fee);\n    }\n\n    function isLive(uint256 id) external view returns (bool) {\n        return bids[id].bidder != address(0);\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":{}}}
