Latency-Bonded Slot Auction
latency-bonded-slot-auction · auction-allocation · 2652 B runtime · funds-movement
scores
- usefulness: 70
- safety: 50
- liveness: 45
- authenticity: 85
- extensibility: 65
- bytecodeDiscipline: 90
- practical: 63
source
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract LatencyBondedSlotAuction {
address payable public immutable owner;
struct Slot {
address payable bidder;
uint256 delay;
uint256 stake;
bool allocated;
bool confirmed;
uint256 allocatedAt;
}
mapping(uint256 => Slot) public slots;
event Bid(uint256 indexed slotId, address indexed bidder, uint256 delay, uint256 stake);
event Allocated(uint256 indexed slotId, address indexed bidder, uint256 delay);
event Confirmed(uint256 indexed slotId, address indexed bidder);
event Slashed(uint256 indexed slotId, address indexed bidder, uint256 stake);
modifier onlyOwner() {
require(msg.sender == owner, "only owner");
_;
}
constructor() {
owner = payable(msg.sender);
}
// Place or replace a bid for a slot with a lower delay.
// Sends msg.value as new stake; refunds previous bidder's stake if replaced.
function bid(uint256 slotId, uint256 delay) external payable {
require(msg.value > 0, "stake required");
require(delay > 0, "delay must be positive");
Slot storage s = slots[slotId];
require(!s.allocated, "slot already allocated");
require(s.bidder == address(0) || delay < s.delay, "must offer a lower delay");
address payable prevBidder = s.bidder;
uint256 prevStake = s.stake;
s.bidder = payable(msg.sender);
s.delay = delay;
s.stake = msg.value;
if (prevBidder != address(0)) {
(bool ok, ) = prevBidder.call{value: prevStake}("");
require(ok, "refund failed");
}
emit Bid(slotId, msg.sender, delay, msg.value);
}
// Lock the current bidder as the winner of the slot.
function allocate(uint256 slotId) external onlyOwner {
Slot storage s = slots[slotId];
require(s.bidder != address(0), "no bidder");
require(!s.allocated, "already allocated");
s.allocated = true;
s.allocatedAt = block.timestamp;
emit Allocated(slotId, s.bidder, s.delay);
}
// Winner confirms within the promised delay and gets their stake back.
function confirm(uint256 slotId) external {
Slot storage s = slots[slotId];
require(s.allocated, "not allocated");
require(!s.confirmed, "already confirmed");
require(msg.sender == s.bidder, "not the winner");
require(block.timestamp <= s.allocatedAt + s.delay, "confirmation too late");
s.confirmed = true;
uint256 stake = s.stake;
s.stake = 0;
(bool ok, ) = msg.sender.call{value: stake}("");
require(ok, "transfer failed");
emit Confirmed(slotId, msg.sender);
}
// Anyone can slash the winner's stake after the promised delay has passed.
function slash(uint256 slotId) external {
Slot storage s = slots[slotId];
require(s.allocated, "not allocated");
require(!s.confirmed, "already confirmed");
require(block.timestamp > s.allocatedAt + s.delay, "too early to slash");
address oldBidder = s.bidder;
uint256 stake = s.stake;
delete s.bidder;
s.delay = 0;
s.stake = 0;
s.allocated = false;
s.confirmed = false;
s.allocatedAt = 0;
(bool ok, ) = owner.call{value: stake}("");
require(ok, "transfer failed");
emit Slashed(slotId, oldBidder, stake);
}
}