Priority Queue Ticket Auction

priority-queue-ticket-auction · auction-allocation · 3766 B runtime · funds-movement

scores

source

pragma solidity ^0.8.13;

interface IQueue {
    function consumeSlot(address user) external;
}

contract PriorityQueueTicketAuction {
    // ERC-721 state
    mapping(uint256 => address) private _owners;
    mapping(uint256 => address) private _tokenApprovals;
    mapping(address => uint256) private _balances;

    // Auction state
    address public queueContract;
    address public beneficiary;
    uint256 public endBlock;
    uint256 public ticketId = 1;
    address public highestBidder;
    uint256 public highestBid;
    bool public finalized;

    event Bid(address indexed bidder, uint256 amount);
    event Finalized(address indexed winner, uint256 amount);
    event PriorityConsumed(address indexed user, uint256 ticketId);

    constructor(address _queueContract, uint256 _endBlock) {
        require(_queueContract != address(0), "Invalid queue");
        require(_endBlock > block.number, "End block must be future");
        queueContract = _queueContract;
        beneficiary = msg.sender;
        endBlock = _endBlock;
        _mint(address(this), ticketId);
    }

    // --- ERC-721 ---
    function balanceOf(address owner) external view returns (uint256) {
        require(owner != address(0), "Ownership query for zero address");
        return _balances[owner];
    }

    function ownerOf(uint256 tokenId) public view returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "Owner query for nonexistent token");
        return owner;
    }

    function approve(address to, uint256 tokenId) external {
        address owner = ownerOf(tokenId);
        require(msg.sender == owner, "Not owner");
        _tokenApprovals[tokenId] = to;
    }

    function getApproved(uint256 tokenId) public view returns (address) {
        require(_exists(tokenId), "Approval query for nonexistent token");
        return _tokenApprovals[tokenId];
    }

    function transferFrom(address from, address to, uint256 tokenId) public {
        require(_isApprovedOrOwner(msg.sender, tokenId), "Not approved or owner");
        _transfer(from, to, tokenId);
    }

    // --- Auction ---
    function bid() external payable {
        require(block.number < endBlock, "Auction ended");
        require(msg.value > highestBid, "Bid too low");

        uint256 minBid = (highestBid * 105) / 100;
        require(msg.value >= minBid, "Bid increment 5% minimum");

        address oldBidder = highestBidder;
        uint256 oldBid = highestBid;
        highestBidder = msg.sender;
        highestBid = msg.value;

        if (oldBidder != address(0)) {
            (bool ok, ) = payable(oldBidder).call{value: oldBid}("");
            require(ok, "Refund failed");
        }
        emit Bid(msg.sender, msg.value);
    }

    function finalize() external {
        require(block.number >= endBlock, "Auction not ended");
        require(!finalized, "Already finalized");
        finalized = true;

        if (highestBidder != address(0)) {
            _transfer(address(this), highestBidder, ticketId);
            emit Finalized(highestBidder, highestBid);
        } else {
            emit Finalized(address(0), 0);
        }
    }

    function burnForPriority() external {
        require(finalized, "Auction not finalized");
        require(ownerOf(ticketId) == msg.sender, "Not ticket owner");
        _burn(ticketId);
        IQueue(queueContract).consumeSlot(msg.sender);
        emit PriorityConsumed(msg.sender, ticketId);
    }

    function withdraw() external {
        require(block.number >= endBlock, "Auction not ended");
        require(msg.sender == beneficiary, "Only beneficiary");
        uint256 amount = highestBid;
        highestBid = 0;
        (bool ok, ) = payable(beneficiary).call{value: amount}("");
        require(ok, "Withdraw failed");
    }

    // --- Internal ERC-721 helpers ---
    function _exists(uint256 tokenId) internal view returns (bool) {
        return _owners[tokenId] != address(0);
    }

    function _mint(address to, uint256 tokenId) internal {
        require(to != address(0), "Mint to zero address");
        require(!_exists(tokenId), "Token already exists");
        _balances[to] += 1;
        _owners[tokenId] = to;
    }

    function _burn(uint256 tokenId) internal {
        address owner = _owners[tokenId];
        require(owner != address(0), "Burn nonexistent token");
        _balances[owner] -= 1;
        delete _owners[tokenId];
        delete _tokenApprovals[tokenId];
    }

    function _transfer(address from, address to, uint256 tokenId) internal {
        require(_owners[tokenId] == from, "Transfer from incorrect owner");
        require(to != address(0), "Transfer to zero address");
        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;
        delete _tokenApprovals[tokenId];
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
        address owner = ownerOf(tokenId);
        return (spender == owner || spender == getApproved(tokenId));
    }
}