Blockhash Lottery Slot Auction
blockhash-lottery-slot-auction · auction-allocation · 2957 B runtime · funds-movement
scores
- usefulness: 70
- safety: 55
- liveness: 35
- authenticity: 65
- extensibility: 40
- bytecodeDiscipline: 75
- practical: 59
source
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract BlockhashAuction {
uint256 public immutable START_BLOCK;
uint256 public immutable END_BLOCK;
uint256 public constant MAX_BIDS = 64;
struct Bid {
address payable bidder;
uint96 amount;
}
Bid[] private _bids;
mapping(address => uint256) private _bidIndex;
mapping(address => bool) public claimed;
bool public settled;
address payable public winner;
uint256 public pot;
event BidSubmitted(address indexed bidder, uint256 amount);
event AuctionSettled(address indexed winner, uint256 pot);
event WinningsClaimed(address indexed winner, uint256 amount);
event Refunded(address indexed bidder, uint256 amount);
constructor(uint256 duration) {
require(duration > 0, "duration 0");
START_BLOCK = block.number;
END_BLOCK = block.number + duration;
}
function bid() external payable {
require(block.number >= START_BLOCK && block.number < END_BLOCK, "not open");
require(msg.value > 0, "zero bid");
require(_bids.length < MAX_BIDS, "max bids");
require(_bidIndex[msg.sender] == 0, "already bid");
_bidIndex[msg.sender] = _bids.length + 1;
_bids.push(Bid(payable(msg.sender), uint96(msg.value)));
emit BidSubmitted(msg.sender, msg.value);
}
function settle() external {
require(block.number > END_BLOCK, "not ended");
require(!settled, "settled");
require(_bids.length > 0, "no bids");
bytes32 bhash = blockhash(END_BLOCK);
require(bhash != bytes32(0), "blockhash expired");
uint256 minHash = type(uint256).max;
uint256 winnerIndex;
uint256 total;
for (uint256 i = 0; i < _bids.length; i++) {
uint256 h = uint256(keccak256(abi.encodePacked(_bids[i].bidder, _bids[i].amount, bhash)));
total += _bids[i].amount;
if (h < minHash) {
minHash = h;
winnerIndex = i;
}
}
winner = _bids[winnerIndex].bidder;
pot = total;
settled = true;
emit AuctionSettled(winner, pot);
}
function claimWinnings() external {
require(settled, "not settled");
require(msg.sender == winner, "not winner");
require(!claimed[msg.sender], "claimed");
claimed[msg.sender] = true;
uint256 amount = pot;
emit WinningsClaimed(msg.sender, amount);
_transfer(winner, amount);
}
function claimRefund() external {
require(settled, "not settled");
require(msg.sender != winner, "winner");
uint256 idx = _bidIndex[msg.sender];
require(idx != 0, "no bid");
require(!claimed[msg.sender], "claimed");
claimed[msg.sender] = true;
uint256 amount = _bids[idx - 1].amount;
emit Refunded(msg.sender, amount);
_transfer(payable(msg.sender), amount);
}
function _transfer(address payable to, uint256 amount) private {
(bool ok, ) = to.call{value: amount}("");
require(ok, "transfer failed");
}
}