First verified Ethereum Smart Contract written in Vyper on etherscan

First verified Ethereum smart contract written in Vyper is now live on etherscan.
Here is the code for the curious ones.
# Blind Auction # Adapted to Vyper from [Solidity by Example](https://github.com/ethereum/solidity/blob/develop/docs/solidity-by-example.rst#blind-auction-1)
struct Bid:
blindedBid: bytes32
deposit: wei_value
# Note: because Vyper does not allow for dynamic arrays, we have limited the
# number of bids that can be placed by one address to 128 in this example
MAX_BIDS: constant(int128) = 128
# Event for logging that auction has ended
AuctionEnded: event({_highestBidder: address, _highestBid: wei_value})
# Auction parameters
beneficiary: public(address)
biddingEnd: public(timestamp)
revealEnd: public(timestamp)
# Set to true at the end of auction, disallowing any new bids
ended: public(bool)
# Final auction state
highestBid: public(wei_value)
highestBidder: public(address)
# State of the bids
bids: map(address, Bid[128])
bidCounts: map(address, int128)
# Allowed withdrawals of previous bids
pendingReturns: map(address, wei_value)
# Create a blinded auction with `_biddingTime` seconds bidding time and
# `_revealTime` seconds reveal time on behalf of the beneficiary address
# `_beneficiary`.
@public
def __init__(_beneficiary: address, _biddingTime: timedelta, _revealTime: timedelta):
self.beneficiary = _beneficiary
self.biddingEnd = block.timestamp + _biddingTime
self.revealEnd = self.biddingEnd + _revealTime
# Place a blinded bid with:
#
# _blindedBid = sha3(concat(
# convert(value, bytes32),
# convert(fake, bytes32),
# secret)
# )
#
# The sent ether is only refunded if the bid is correctly revealed in the
# revealing phase. The bid is valid if the ether sent together with the bid is
# at least "value" and "fake" is not true. Setting "fake" to true and sending
# not the exact amount are ways to hide the real bid but still make the
# required deposit. The same address can place multiple bids.
@public
@payable
def bid(_blindedBid: bytes32):
# Check if bidding period is still open
assert block.timestamp < self.biddingEnd
# Check that payer hasn't already placed maximum number of bids
numBids: int128 = self.bidCounts[msg.sender]
assert numBids < MAX_BIDS
# Add bid to mapping of all bids
self.bids[msg.sender][numBids] = Bid({
blindedBid: _blindedBid,
deposit: msg.value
})
self.bidCounts[msg.sender] += 1
# Returns a boolean value, `True` if bid placed successfully, `False` otherwise.
@private
def placeBid(bidder: address, value: wei_value) -> bool:
# If bid is less than highest bid, bid fails
if (value <= self.highestBid):
return False
# Refund the previously highest bidder
if (self.highestBidder != ZERO_ADDRESS):
self.pendingReturns[self.highestBidder] += self.highestBid
# Place bid successfully and update auction state
self.highestBid = value
self.highestBidder = bidder
return True
# Reveal your blinded bids. You will get a refund for all correctly blinded
# invalid bids and for all bids except for the totally highest.
@public
def reveal(_numBids: int128, _values: wei_value[128], _fakes: bool[128], _secrets: bytes32[128]):
# Check that bidding period is over
assert block.timestamp > self.biddingEnd
# Check that reveal end has not passed
assert block.timestamp < self.revealEnd
# Check that number of bids being revealed matches log for sender
assert _numBids == self.bidCounts[msg.sender]
# Calculate refund for sender
refund: wei_value
for i in range(MAX_BIDS):
# Note that loop may break sooner than 128 iterations if i >= _numBids
if (i >= _numBids):
break
# Get bid to check
bidToCheck: Bid = (self.bids[msg.sender])[i]
# Check against encoded packet
value: wei_value = _values[i]
fake: bool = _fakes[i]
secret: bytes32 = _secrets[i]
blindedBid: bytes32 = sha3(concat(
convert(value, bytes32),
convert(fake, bytes32),
secret
))
# Bid was not actually revealed
# Do not refund deposit
if (blindedBid != bidToCheck.blindedBid):
assert 1 == 0
continue
# Add deposit to refund if bid was indeed revealed
refund += bidToCheck.deposit
if (not fake and bidToCheck.deposit >= value):
if (self.placeBid(msg.sender, value)):
refund -= value
# Make it impossible for the sender to re-claim the same deposit
zeroBytes32: bytes32
bidToCheck.blindedBid = zeroBytes32
# Send refund if non-zero
if (refund != 0):
send(msg.sender, refund)
# Withdraw a bid that was overbid.
@public
def withdraw():
# Check that there is an allowed pending return.
pendingAmount: wei_value = self.pendingReturns[msg.sender]
if (pendingAmount > 0):
# If so, set pending returns to zero to prevent recipient from calling
# this function again as part of the receiving call before `transfer`
# returns (see the remark above about conditions -> effects ->
# interaction).
self.pendingReturns[msg.sender] = 0
# Then send return
send(msg.sender, pendingAmount)
# End the auction and send the highest bid to the beneficiary.
@public
def auctionEnd():
# Check that reveal end has passed
assert block.timestamp > self.revealEnd
# Check that auction has not already been marked as ended
assert not self.ended
# Log auction ending and set flag
log.AuctionEnded(self.highestBidder, self.highestBid)
self.ended = True
# Transfer funds to beneficiary
send(self.beneficiary, self.highestBid)
