Time-Slice Renting Auction

time-slice-renting-auction · auction-allocation · 2654 B runtime · funds-movement

scores

source

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

contract TimeSliceRentingAuction {
    struct Slot {
        uint256 startBlock;
        uint256 endBlock;
        address feeReceiver;
        address winner;
        uint256 highestBid;
        bool resolved;
    }

    uint256 public nextSlotId;
    mapping(uint256 => Slot) public slots;
    mapping(uint256 => mapping(address => uint256)) public bids;

    event SlotCreated(uint256 indexed slotId, uint256 startBlock, uint256 endBlock, address feeReceiver);
    event Bid(uint256 indexed slotId, address indexed bidder, uint256 amount, uint256 totalBid);
    event Resolved(uint256 indexed slotId, address winner, uint256 amount);
    event Withdraw(uint256 indexed slotId, address indexed bidder, uint256 amount);
    event Use(uint256 indexed slotId, address indexed user, uint256 blockNumber);

    function createSlot(uint256 startBlock, uint256 endBlock, address feeReceiver) external returns (uint256 slotId) {
        require(endBlock >= startBlock, "end<start");
        require(startBlock > block.number + 5, "start too soon");
        require(feeReceiver != address(0), "feeReceiver=0");
        slotId = nextSlotId++;
        slots[slotId] = Slot(startBlock, endBlock, feeReceiver, address(0), 0, false);
        emit SlotCreated(slotId, startBlock, endBlock, feeReceiver);
    }

    function bid(uint256 slotId) external payable {
        Slot storage slot = slots[slotId];
        require(block.number <= auctionEnd(slot), "auction ended");
        require(msg.value > 0, "zero bid");
        uint256 newTotal = bids[slotId][msg.sender] + msg.value;
        bids[slotId][msg.sender] = newTotal;
        if (newTotal > slot.highestBid) {
            slot.highestBid = newTotal;
            slot.winner = msg.sender;
        }
        emit Bid(slotId, msg.sender, msg.value, newTotal);
    }

    function resolve(uint256 slotId) external {
        Slot storage slot = slots[slotId];
        require(!slot.resolved, "already resolved");
        require(block.number > auctionEnd(slot), "auction still open");
        slot.resolved = true;
        uint256 amount = slot.highestBid;
        emit Resolved(slotId, slot.winner, amount);
        if (amount > 0) {
            payable(slot.feeReceiver).transfer(amount);
        }
    }

    function withdraw(uint256 slotId) external {
        Slot storage slot = slots[slotId];
        require(slot.resolved || block.number > auctionEnd(slot), "auction active");
        require(msg.sender != slot.winner, "winner locked");
        uint256 amount = bids[slotId][msg.sender];
        require(amount > 0, "no bid");
        bids[slotId][msg.sender] = 0;
        emit Withdraw(slotId, msg.sender, amount);
        payable(msg.sender).transfer(amount);
    }

    function use(uint256 slotId) external {
        Slot storage slot = slots[slotId];
        require(slot.resolved, "not resolved");
        require(msg.sender == slot.winner, "not winner");
        require(block.number >= slot.startBlock && block.number <= slot.endBlock, "time slot not active");
        emit Use(slotId, msg.sender, block.number);
    }

    function auctionEnd(Slot storage slot) internal view returns (uint256) {
        return slot.startBlock - 5;
    }
}