スマートコントラクトにおいてオークションのシステムを理解することはいずれ必須となるのかも知れない。
オークションとは「モノを割り当てたりサービスを入手したりする方法(下記参照文献『入門オークション』p.6)」のことで、特にモノを割り当てるという機能に関して「マーケットデザイン」と呼ばれる概念が鍵となる。それは肝移植マッチングもしくは学校選択マッチングのような、いわば社会制度のデザインにおいて重要となってくる分野だ。
Dappsのようなアプリにおいて重要なのは、自動的な理想分配システムを、人々の内に潜む多様な志向性を結びつけてどのように構築していくかである。
オークションは応札者に対して事前にルールを明確化しておく必要がある。そのような視点においてもブロックチェーン技術との相性は良いだろう。
本記事ではオークションが歴史的にどのように考えられ活用されてきたのかは述べないが(いずれ別のブログ記事でまとめたらココにリンクを貼っておく)、イーサリアムのスマートコントラクトにおいての基本的なオークションの仕組みを記述しておく。
今回は、Solidityによるオークションシステムについて以下のサイトを参考にして構築してみました。
オークションは大きく分けて公開型と封印型の2つに分けられます。公開型とは応札者が他の応札者の入札価格や脱落状況などを共有しながら行う方式で、いわゆる「せり」と呼ばれる形式もこの方式です。上記リンクページの「Simple Open Auction」もどちらかというと公開型に近い設計です。一方、封印型は他の応札者の入札価格は共有されず、またいったん提出した金額の変更も認められません。封筒に金額を書いた紙を入れて提出するようなイメージです。
今回は「Simple Open Auction」に少し手を加えて、複数のオークションを登録できるようにしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
pragma solidity ^0.4.21; contract SimpleAuction { struct auction { uint highestBid; //最高入札額 address highestBidder; //最高入札額者のアドレス address beneficiary; //受益者 uint auctionEnd; //オークション終了時刻 bool ended; //オークションの終了フラグ mapping(address => uint) pendingReturns; //返金者リストとその金額 } mapping(uint => auction) Auctions; uint public auctionID; //期限と入札額受益者のアドレスを設定しオークションを生成する function newAuction(uint _biddingTime,address _beneficiary) public { auctionID += 1 ; Auctions[auctionID].auctionEnd = now + _biddingTime * 1 minutes; //期限 Auctions[auctionID].beneficiary = _beneficiary; //受益者 } //オークションIDを指定して入札を行う function bid(uint _auctionID) public payable { auction storage a = Auctions[_auctionID]; //期限前かどうか require(now <= a.auctionEnd); //送られた金額が現在の最高額よりも高いか require(msg.value > a.highestBid); //指定したオークションにおいてすでに入札がある場合に実行される //最高入札額が更新された時点で返金者リストに以前の最高額を保存する if (a.highestBid != 0) { a.pendingReturns[a.highestBidder] += a.highestBid; } //現時点での最高入札額者のアドレスとその入札額を設定する a.highestBidder = msg.sender; a.highestBid = msg.value; } //引き落とす オークション終了前に引き落とせない? function withdraw(uint _auctionID) public returns (bool) { //オークション番号を指定 auction storage a = Auctions[_auctionID]; //アドレスから入札金額を特定する uint amount = a.pendingReturns[msg.sender]; if (amount > 0) { a.pendingReturns[msg.sender] = 0; if (!msg.sender.send(amount)) { //もしsendが失敗したらもう一度セットする a.pendingReturns[msg.sender] = amount; return false; } } return true; } function auctionEnd(uint _auctionID) public { //オークション番号を指定 auction storage a = Auctions[_auctionID]; require(now >= a.auctionEnd); // auction did not yet end require(!a.ended); // this function has already been called a.ended = true; //コントラクト生成時に設定したオークション受益者に対して最高額を送る。 a.beneficiary.transfer(a.highestBid); } } |
11行目
オークション構造体の中に、最高入札額に至らなかった返金者アドレスとその返金額リストをmappingとして設定しています。mappingの値に更に構造体を設定したい場合は、以下のページを参照すると良いでしょう。
Solidity Structs
21行目
オークションの制限時間を指定する引数としての_biddingTimeに、例えば1を設定するとその時間は1分(minutes)となります。この単位は他にもsecondsやhours、daysなどがあります。以下のページを参照すると良いでしょう。
Solidity Time Units
53行目
アドレス.send(送金額)はtureおよびfalseを返します。アドレス型(Address)のメンバ関数であるsendとtransferの違いについては下記の関連ページをご覧下さい。
関連ページ
参照文献