Solidiity Documentation ver0.5.8を読んだ(3.3.2前半のまとめ)

3.3.2 Blind Auction

blind auctionをイーサリアムのsmart contractで作成するのは簡単。
全員が入札を確認できるopen auctionをから初めて、
auctionが終わるまで実際の入札がわからないblind auctionのコントラクトに拡張していく。

Simple Open Auction
入札時間中は誰でも入札を行える。
入札は入札者を彼らの入札に拘束するためにすでにお金／エーテルを送ることを含む。
一番高い入札が行われると、その以前で一番高かった入札者の掛け金が戻される。
入札期間の終了後、受取人が金銭を受け取るには契約を手動で呼び出す必要があります。
契約を有効にすることはできません。

```SimpleOpenAuction.sol

pragma solidity >=0.4.22 <0.7.0;

contract SimpleAuction {
    
    //オークションのパラメーター
    //Timesはunix timestampsか秒単位の時間
    //受益者のアドレス
    address payable public beneficiary;
    uint public auctionEndTime;
    
    //現在のオークションの状態
    //もっとも高い入札者のアドレス
    address public highestBidder;
    
    //もっとも高い入札の額
    uint public highestBid;
    
    //以前の入札の引き出しを許可する
    //最高額の入札者のアドレスと入札額
    mapping(address => uint) pendingReturns;
    
    //デフォルトの初期値はfalse
    //trueに設定すると、いかなる変更も許可されない。
    bool ended;
    
    //もっとも高額な入札の更新をするイベント
    
    event HighestBidIncreased(address bidder, uint amount);
    
    //オークションを終了させ勝者と入札額を記録するイベント
    event AuctionEnded(address winner, uint amount);
    
    //以下は、3つのスラッシュで認識される、いわゆるnatspecコメントです。
    //ユーザーがトランザクションの確認を求められたときに表示されます。
    ///受取人アドレス `_beneficiary`に代わって
    ///` _biddingTime`秒の入札時間で簡単なオークションを作成する
    //コントラクトデプロイ時に入札時刻を_biddingと受益者を_beneficiaryに渡す
    
    constructor(
        
        uint _biddingTime,
        address payable _beneficiary
        
    ) public {
        beneficiary = _beneficiary;
        auctionEndTime = now + _biddingTime;
    }
    
    //引数は必要ない。
    //全ての情報は既にトランザクションの一部にある。
    //etherを受け取る為にキーワードpayableが必要になる。
    //入札時間が終わるとコールが返される。
    //入札
    function bid() public payable {
        
        
        require(
            now <= auctionEndTime,
            "Auction already ended."
        );
        
        //現在の入札額よりも高い額で入札しなければならない
        require(
            msg.value > highestBid,
            "There already is a higher bid."
        );
        
        //もっとも高い入札額が0だとまずい
        if (highestBid != 0) {
            
            //mapping(address => uint) pendingReturns;
            pendingReturns[highestBidder] += highestBid;
        }
        
        //もっとも高い入札者の更新
        highestBidder = msg.sender;  
        
        //もっとも高い入札額の更新
        highestBid = msg.value;
        
        //もっとも高い入札を更新するイベントを発火
        emit HighestBidIncreased(msg.sender, msg.value);
    }
    
    //入札額を引き出す
    //成功するとtrueが返される
    function withdraw() public returns (bool) {
        
        //mapping(address => uint) pendingReturns;
        uint amount = pendingReturns[msg.sender];
        
        if (amount > 0) {
            
            //
            pendingReturns[msg.sender] = 0;
            
            if (!msg.sender.send(amount)) {
                
                pendingReturns[msg.sender] = amount;
                return false;
            }
        }
        return true;
    }
    
    //オークションを終了させ、もっとも高い入札額をbeneficiaryに送る
    function auctionEnd() public {
        
        //他のコントラクトと相互作用する関数（つまり関数を呼び出すかEtherを送る）
        //を3つのフェーズに構造化することは良いガイドラインです。
        //もしこれらのフェーズが混合されると、他のコントラクトが現在のコントラクト
        //に呼び戻され、状態が修正されたり、
        //結果（etherの支払い）を複数回実行したりすることになる。
        //内部的に呼び出される関数のが外部関数との相互作用を含む場合、
        //それらも外部コントラクトとの相互作用と見なされる必要がある。
        
        //1.条件を確認する
        require(now >= auctionEndTime, "Auction not yet ended.");
        require(!ended, "auctionEnd has already been called.");
        
        //2.結果、オークションを実行する
        ended = true;
        //オークション終了時点でもっとも高額な入札の変数が引数として渡され、
        //イベントを発火させる。
        emit AuctionEnded(highestBidder, highestBid);
        
        //3.他のコントラクトと相互作用する
        beneficiary.transfer(highestBid);
    }    
}
```