Last Moment Auction

last-moment-auction · auction-allocation · 1944 B runtime · funds-movement

scores

source

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract LastMomentAuction {
    uint32 public openBlock;
    uint32 public closeBlock;
    uint96 public totalPool;

    struct BlockInfo {
        uint32 bidCount;
        uint96 bidSum;
    }

    struct BidderInfo {
        uint32 blockNum;
        bool claimed;
    }

    mapping(uint32 => BlockInfo) private blockInfos;
    mapping(address => BidderInfo) private bidders;

    event Bid(address indexed bidder, uint32 indexed blockNum, uint96 amount);
    event Claim(address indexed bidder, uint96 amount);

    constructor(uint32 open, uint32 close) {
        require(close > open + 1, "invalid window");
        openBlock = open;
        closeBlock = close;
    }

    function bid() external payable {
        uint32 blockNum = uint32(block.number);
        require(blockNum >= openBlock && blockNum < closeBlock, "not in window");
        require(msg.value > 0, "zero bid");
        require(bidders[msg.sender].blockNum == 0, "already bid");
        require(msg.value <= type(uint96).max, "value too high");

        bidders[msg.sender].blockNum = blockNum;
        blockInfos[blockNum].bidCount++;
        blockInfos[blockNum].bidSum += uint96(msg.value);
        totalPool += uint96(msg.value);

        emit Bid(msg.sender, blockNum, uint96(msg.value));
    }

    function claim() external {
        require(block.number >= closeBlock, "not closed");

        BidderInfo memory info = bidders[msg.sender];
        require(info.blockNum == closeBlock - 1, "not winner");
        require(!info.claimed, "claimed");

        BlockInfo memory win = blockInfos[info.blockNum];
        require(win.bidCount > 0, "no winners");

        uint96 share = totalPool / win.bidCount;
        require(share > 0, "no share");

        bidders[msg.sender].claimed = true;
        (bool success, ) = payable(msg.sender).call{value: share}("");
        require(success, "transfer failed");

        emit Claim(msg.sender, share);
    }
}