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)