mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			356 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			356 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. index:: auction;blind, auction;open, blind auction, open auction
 | |
| 
 | |
| *************
 | |
| Blind Auction
 | |
| *************
 | |
| 
 | |
| In this section, we will show how easy it is to create a completely blind
 | |
| auction contract on Ethereum.  We will start with an open auction where
 | |
| everyone can see the bids that are made and then extend this contract into a
 | |
| blind auction where it is not possible to see the actual bid until the bidding
 | |
| period ends.
 | |
| 
 | |
| .. _simple_auction:
 | |
| 
 | |
| Simple Open Auction
 | |
| ===================
 | |
| 
 | |
| The general idea of the following simple auction contract is that everyone can
 | |
| send their bids during a bidding period. The bids already include sending money
 | |
| / Ether in order to bind the bidders to their bid. If the highest bid is
 | |
| raised, the previous highest bidder gets their money back.  After the end of
 | |
| the bidding period, the contract has to be called manually for the beneficiary
 | |
| to receive their money - contracts cannot activate themselves.
 | |
| 
 | |
| ::
 | |
| 
 | |
|     // SPDX-License-Identifier: GPL-3.0
 | |
|     pragma solidity ^0.8.4;
 | |
|     contract SimpleAuction {
 | |
|         // Parameters of the auction. Times are either
 | |
|         // absolute unix timestamps (seconds since 1970-01-01)
 | |
|         // or time periods in seconds.
 | |
|         address payable public beneficiary;
 | |
|         uint public auctionEndTime;
 | |
| 
 | |
|         // Current state of the auction.
 | |
|         address public highestBidder;
 | |
|         uint public highestBid;
 | |
| 
 | |
|         // Allowed withdrawals of previous bids
 | |
|         mapping(address => uint) pendingReturns;
 | |
| 
 | |
|         // Set to true at the end, disallows any change.
 | |
|         // By default initialized to `false`.
 | |
|         bool ended;
 | |
| 
 | |
|         // Events that will be emitted on changes.
 | |
|         event HighestBidIncreased(address bidder, uint amount);
 | |
|         event AuctionEnded(address winner, uint amount);
 | |
| 
 | |
|         // Errors that describe failures.
 | |
| 
 | |
|         // The triple-slash comments are so-called natspec
 | |
|         // comments. They will be shown when the user
 | |
|         // is asked to confirm a transaction or
 | |
|         // when an error is displayed.
 | |
| 
 | |
|         /// The auction has already ended.
 | |
|         error AuctionAlreadyEnded();
 | |
|         /// There is already a higher or equal bid.
 | |
|         error BidNotHighEnough(uint highestBid);
 | |
|         /// The auction has not ended yet.
 | |
|         error AuctionNotYetEnded();
 | |
|         /// The function auctionEnd has already been called.
 | |
|         error AuctionEndAlreadyCalled();
 | |
| 
 | |
|         /// Create a simple auction with `_biddingTime`
 | |
|         /// seconds bidding time on behalf of the
 | |
|         /// beneficiary address `_beneficiary`.
 | |
|         constructor(
 | |
|             uint _biddingTime,
 | |
|             address payable _beneficiary
 | |
|         ) {
 | |
|             beneficiary = _beneficiary;
 | |
|             auctionEndTime = block.timestamp + _biddingTime;
 | |
|         }
 | |
| 
 | |
|         /// Bid on the auction with the value sent
 | |
|         /// together with this transaction.
 | |
|         /// The value will only be refunded if the
 | |
|         /// auction is not won.
 | |
|         function bid() public payable {
 | |
|             // No arguments are necessary, all
 | |
|             // information is already part of
 | |
|             // the transaction. The keyword payable
 | |
|             // is required for the function to
 | |
|             // be able to receive Ether.
 | |
| 
 | |
|             // Revert the call if the bidding
 | |
|             // period is over.
 | |
|             if (block.timestamp > auctionEndTime)
 | |
|                 revert AuctionAlreadyEnded();
 | |
| 
 | |
|             // If the bid is not higher, send the
 | |
|             // money back (the revert statement
 | |
|             // will revert all changes in this
 | |
|             // function execution including
 | |
|             // it having received the money).
 | |
|             if (msg.value <= highestBid)
 | |
|                 revert BidNotHighEnough(highestBid);
 | |
| 
 | |
|             if (highestBid != 0) {
 | |
|                 // Sending back the money by simply using
 | |
|                 // highestBidder.send(highestBid) is a security risk
 | |
|                 // because it could execute an untrusted contract.
 | |
|                 // It is always safer to let the recipients
 | |
|                 // withdraw their money themselves.
 | |
|                 pendingReturns[highestBidder] += highestBid;
 | |
|             }
 | |
|             highestBidder = msg.sender;
 | |
|             highestBid = msg.value;
 | |
|             emit HighestBidIncreased(msg.sender, msg.value);
 | |
|         }
 | |
| 
 | |
|         /// Withdraw a bid that was overbid.
 | |
|         function withdraw() public returns (bool) {
 | |
|             uint amount = pendingReturns[msg.sender];
 | |
|             if (amount > 0) {
 | |
|                 // It is important to set this to zero because the recipient
 | |
|                 // can call this function again as part of the receiving call
 | |
|                 // before `send` returns.
 | |
|                 pendingReturns[msg.sender] = 0;
 | |
| 
 | |
|                 if (!payable(msg.sender).send(amount)) {
 | |
|                     // No need to call throw here, just reset the amount owing
 | |
|                     pendingReturns[msg.sender] = amount;
 | |
|                     return false;
 | |
|                 }
 | |
|             }
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         /// End the auction and send the highest bid
 | |
|         /// to the beneficiary.
 | |
|         function auctionEnd() public {
 | |
|             // It is a good guideline to structure functions that interact
 | |
|             // with other contracts (i.e. they call functions or send Ether)
 | |
|             // into three phases:
 | |
|             // 1. checking conditions
 | |
|             // 2. performing actions (potentially changing conditions)
 | |
|             // 3. interacting with other contracts
 | |
|             // If these phases are mixed up, the other contract could call
 | |
|             // back into the current contract and modify the state or cause
 | |
|             // effects (ether payout) to be performed multiple times.
 | |
|             // If functions called internally include interaction with external
 | |
|             // contracts, they also have to be considered interaction with
 | |
|             // external contracts.
 | |
| 
 | |
|             // 1. Conditions
 | |
|             if (block.timestamp < auctionEndTime)
 | |
|                 revert AuctionNotYetEnded();
 | |
|             if (ended)
 | |
|                 revert AuctionEndAlreadyCalled();
 | |
| 
 | |
|             // 2. Effects
 | |
|             ended = true;
 | |
|             emit AuctionEnded(highestBidder, highestBid);
 | |
| 
 | |
|             // 3. Interaction
 | |
|             beneficiary.transfer(highestBid);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| Blind Auction
 | |
| =============
 | |
| 
 | |
| The previous open auction is extended to a blind auction in the following. The
 | |
| advantage of a blind auction is that there is no time pressure towards the end
 | |
| of the bidding period. Creating a blind auction on a transparent computing
 | |
| platform might sound like a contradiction, but cryptography comes to the
 | |
| rescue.
 | |
| 
 | |
| During the **bidding period**, a bidder does not actually send their bid, but
 | |
| only a hashed version of it.  Since it is currently considered practically
 | |
| impossible to find two (sufficiently long) values whose hash values are equal,
 | |
| the bidder commits to the bid by that.  After the end of the bidding period,
 | |
| the bidders have to reveal their bids: They send their values unencrypted and
 | |
| the contract checks that the hash value is the same as the one provided during
 | |
| the bidding period.
 | |
| 
 | |
| Another challenge is how to make the auction **binding and blind** at the same
 | |
| time: The only way to prevent the bidder from just not sending the money after
 | |
| they won the auction is to make them send it together with the bid. Since value
 | |
| transfers cannot be blinded in Ethereum, anyone can see the value.
 | |
| 
 | |
| The following contract solves this problem by accepting any value that is
 | |
| larger than the highest bid. Since this can of course only be checked during
 | |
| the reveal phase, some bids might be **invalid**, and this is on purpose (it
 | |
| even provides an explicit flag to place invalid bids with high value
 | |
| transfers): Bidders can confuse competition by placing several high or low
 | |
| invalid bids.
 | |
| 
 | |
| 
 | |
| ::
 | |
| 
 | |
|     // SPDX-License-Identifier: GPL-3.0
 | |
|     pragma solidity ^0.8.4;
 | |
|     contract BlindAuction {
 | |
|         struct Bid {
 | |
|             bytes32 blindedBid;
 | |
|             uint deposit;
 | |
|         }
 | |
| 
 | |
|         address payable public beneficiary;
 | |
|         uint public biddingEnd;
 | |
|         uint public revealEnd;
 | |
|         bool public ended;
 | |
| 
 | |
|         mapping(address => Bid[]) public bids;
 | |
| 
 | |
|         address public highestBidder;
 | |
|         uint public highestBid;
 | |
| 
 | |
|         // Allowed withdrawals of previous bids
 | |
|         mapping(address => uint) pendingReturns;
 | |
| 
 | |
|         event AuctionEnded(address winner, uint highestBid);
 | |
| 
 | |
|         // Errors that describe failures.
 | |
| 
 | |
|         /// The function has been called too early.
 | |
|         /// Try again at `time`.
 | |
|         error TooEarly(uint time);
 | |
|         /// The function has been called too late.
 | |
|         /// It cannot be called after `time`.
 | |
|         error TooLate(uint time);
 | |
|         /// The function auctionEnd has already been called.
 | |
|         error AuctionEndAlreadyCalled();
 | |
| 
 | |
|         // Modifiers are a convenient way to validate inputs to
 | |
|         // functions. `onlyBefore` is applied to `bid` below:
 | |
|         // The new function body is the modifier's body where
 | |
|         // `_` is replaced by the old function body.
 | |
|         modifier onlyBefore(uint _time) {
 | |
|             if (block.timestamp >= _time) revert TooLate(_time);
 | |
|             _;
 | |
|         }
 | |
|         modifier onlyAfter(uint _time) {
 | |
|             if (block.timestamp <= _time) revert TooEarly(_time);
 | |
|             _;
 | |
|         }
 | |
| 
 | |
|         constructor(
 | |
|             uint _biddingTime,
 | |
|             uint _revealTime,
 | |
|             address payable _beneficiary
 | |
|         ) {
 | |
|             beneficiary = _beneficiary;
 | |
|             biddingEnd = block.timestamp + _biddingTime;
 | |
|             revealEnd = biddingEnd + _revealTime;
 | |
|         }
 | |
| 
 | |
|         /// Place a blinded bid with `_blindedBid` =
 | |
|         /// keccak256(abi.encodePacked(value, fake, 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.
 | |
|         function bid(bytes32 _blindedBid)
 | |
|             public
 | |
|             payable
 | |
|             onlyBefore(biddingEnd)
 | |
|         {
 | |
|             bids[msg.sender].push(Bid({
 | |
|                 blindedBid: _blindedBid,
 | |
|                 deposit: msg.value
 | |
|             }));
 | |
|         }
 | |
| 
 | |
|         /// Reveal your blinded bids. You will get a refund for all
 | |
|         /// correctly blinded invalid bids and for all bids except for
 | |
|         /// the totally highest.
 | |
|         function reveal(
 | |
|             uint[] memory _values,
 | |
|             bool[] memory _fake,
 | |
|             bytes32[] memory _secret
 | |
|         )
 | |
|             public
 | |
|             onlyAfter(biddingEnd)
 | |
|             onlyBefore(revealEnd)
 | |
|         {
 | |
|             uint length = bids[msg.sender].length;
 | |
|             require(_values.length == length);
 | |
|             require(_fake.length == length);
 | |
|             require(_secret.length == length);
 | |
| 
 | |
|             uint refund;
 | |
|             for (uint i = 0; i < length; i++) {
 | |
|                 Bid storage bidToCheck = bids[msg.sender][i];
 | |
|                 (uint value, bool fake, bytes32 secret) =
 | |
|                         (_values[i], _fake[i], _secret[i]);
 | |
|                 if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
 | |
|                     // Bid was not actually revealed.
 | |
|                     // Do not refund deposit.
 | |
|                     continue;
 | |
|                 }
 | |
|                 refund += bidToCheck.deposit;
 | |
|                 if (!fake && bidToCheck.deposit >= value) {
 | |
|                     if (placeBid(msg.sender, value))
 | |
|                         refund -= value;
 | |
|                 }
 | |
|                 // Make it impossible for the sender to re-claim
 | |
|                 // the same deposit.
 | |
|                 bidToCheck.blindedBid = bytes32(0);
 | |
|             }
 | |
|             payable(msg.sender).transfer(refund);
 | |
|         }
 | |
| 
 | |
|         /// Withdraw a bid that was overbid.
 | |
|         function withdraw() public {
 | |
|             uint amount = pendingReturns[msg.sender];
 | |
|             if (amount > 0) {
 | |
|                 // It is important to set this to zero because the recipient
 | |
|                 // can call this function again as part of the receiving call
 | |
|                 // before `transfer` returns (see the remark above about
 | |
|                 // conditions -> effects -> interaction).
 | |
|                 pendingReturns[msg.sender] = 0;
 | |
| 
 | |
|                 payable(msg.sender).transfer(amount);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// End the auction and send the highest bid
 | |
|         /// to the beneficiary.
 | |
|         function auctionEnd()
 | |
|             public
 | |
|             onlyAfter(revealEnd)
 | |
|         {
 | |
|             if (ended) revert AuctionEndAlreadyCalled();
 | |
|             emit AuctionEnded(highestBidder, highestBid);
 | |
|             ended = true;
 | |
|             beneficiary.transfer(highestBid);
 | |
|         }
 | |
| 
 | |
|         // This is an "internal" function which means that it
 | |
|         // can only be called from the contract itself (or from
 | |
|         // derived contracts).
 | |
|         function placeBid(address bidder, uint value) internal
 | |
|                 returns (bool success)
 | |
|         {
 | |
|             if (value <= highestBid) {
 | |
|                 return false;
 | |
|             }
 | |
|             if (highestBidder != address(0)) {
 | |
|                 // Refund the previously highest bidder.
 | |
|                 pendingReturns[highestBidder] += highestBid;
 | |
|             }
 | |
|             highestBid = value;
 | |
|             highestBidder = bidder;
 | |
|             return true;
 | |
|         }
 | |
|     }
 |