Random-Deadline Sealed-Bid Auction

random-deadline-sealed-bid-auction · auction-allocation · 2619 B runtime · funds-movement

scores

source

pragma solidity ^0.8.13;

contract RandomDeadlineAuction {
    uint256 public constant MAX_LIFETIME = 256;
    uint256 public constant REVEAL_WINDOW = 64;
    uint256 public constant MAX_BIDS = 256;

    struct Bid {
        address payable bidder;
        uint96 amount;
        bytes32 commitment;
        bool revealed;
        bool refunded;
    }

    address payable public beneficiary;
    uint256 public closingBlock;
    uint256 public revealEnd;
    Bid[] public bids;
    bool public settled;
    address public winner;
    uint256 public winningAmount;

    event Committed(address indexed bidder, uint256 indexed id);
    event Revealed(address indexed bidder, uint256 indexed id, uint256 amount);
    event Refunded(address indexed bidder, uint256 indexed id, uint256 amount);
    event Settled(address indexed winner, uint256 amount);

    constructor(address payable _beneficiary) {
        beneficiary = _beneficiary;

        uint256 random = uint256(blockhash(block.number));
        if (random == 0) {
            random = uint256(blockhash(block.number - 1));
        }
        closingBlock = block.number + (random % MAX_LIFETIME) + 1;
        revealEnd = closingBlock + REVEAL_WINDOW;
    }

    function commit(bytes32 commitment) external payable {
        require(block.number < closingBlock);
        require(bids.length < MAX_BIDS);
        require(msg.value > 0);

        bids.push(Bid(payable(msg.sender), uint96(msg.value), commitment, false, false));
        emit Committed(msg.sender, bids.length - 1);
    }

    function reveal(uint256 id, uint256 amount, string calldata secret) external {
        require(block.number >= closingBlock && block.number <= revealEnd);
        require(id < bids.length);
        Bid storage b = bids[id];
        require(msg.sender == b.bidder);
        require(!b.revealed);
        require(amount == b.amount);
        require(keccak256(abi.encodePacked(msg.sender, amount, secret)) == b.commitment);
        b.revealed = true;
        emit Revealed(msg.sender, id, amount);
    }

    function settle() external {
        require(block.number > revealEnd);
        require(!settled);

        uint256 best;
        for (uint256 i = 0; i < bids.length; i++) {
            if (bids[i].revealed && bids[i].amount > best) {
                best = bids[i].amount;
                winner = bids[i].bidder;
                winningAmount = best;
            }
        }

        settled = true;
        if (winningAmount > 0) {
            (bool ok, ) = beneficiary.call{value: winningAmount}("");
            require(ok);
        }
        emit Settled(winner, winningAmount);
    }

    function refund(uint256 id) external {
        require(settled);
        require(id < bids.length);
        Bid storage b = bids[id];
        require(b.bidder != winner);
        require(!b.refunded);

        b.refunded = true;
        (bool ok, ) = b.bidder.call{value: b.amount}("");
        require(ok);
        emit Refunded(b.bidder, id, b.amount);
    }
}