読了時間:10分
概要
多くの場合、信頼関係のない2者間で取引を行うには、信頼できる仲介者が必要となりますが、スマートコントラクトを利用すれば、こうした仲介者を完全に不要にすることができます。このガイドでは、オークションのプロセス全体を信頼不要にする「ダッチオークション」のスマートコントラクトの作成方法を学びます。
前提条件
- Quicknodeアカウント(こちらから無料で作成できます)
- MetaMaskウォレット
- セポリア・テストネット上のETH(こちらから入手可能です)
- ERC721(NFT)スマートコントラクトの作成に関する知識
スマートコントラクトを利用したオークション
オークションとは、入札というプロセスを通じて商品を公の場で販売する場であり、通常、最高額の入札をした人が落札し、その商品の所有権を得ることになります。これがオークションの基本的な仕組みですが、他にもいくつかのルールがあります。
オークションには、次のような種類があります:
-
イングリッシュ・オークション(公開昇値式オークション):これは、最高額の入札者が落札者となる、誰もが知っている伝統的なオークションです。
-
オランダ式オークション(オープン・ディセンディング・プライス・オークション):このタイプのオークションは、主に花、食品、衣類などの生鮮品に用いられます。開始価格が設定され、時間の経過とともに価格が一定の割合で下がっていきます。現在の価格以上で入札した入札者が落札者となります。また、あらかじめ設定された時間が経過した時点で、最後に入札を行った入札者が落札者となります。
-
ファーストプライス・シールド・ビッド方式(ブラインドオークション):この方式では、すべての入札者が互いの入札額を知らないまま、封筒に入れて入札額を提出し、その後、競売人が封筒を開封し、最高額の入札者が落札する。
-
セカンドプライス・密封入札オークション(ヴィックリー・オークション):これはファーストプライス・密封入札オークションと同じです。唯一の違いは、落札者が2番目に高い入札額であるという点です。
どのような形式のオークションにおいても、仲介者とは、オークションの進行や落札者の決定を担当する競売人であり、その業務に対して、通常は総売却価格の一定割合を手数料として徴収します。スマートコントラクトを利用すれば、競売人を介在させる必要がなくなり、イーサリアムブロックチェーン上で処理を行うことで、プロセス全体を自動化し、信頼不要かつ安全なものにすることができます。
オランダ式オークションとは何ですか?
オランダ式オークション(オープン・ディセンディング・オークションとも呼ばれる)とは、売り手がまず開始価格、開催期間、および割引率を設定するオークションの一種です。時間が経過するにつれて、あらかじめ設定された期間が終了するまで、商品の価格は下がり続けます。例えば、手に入れたいとても良いバッグがあるものの、予算を超えてしまうとします。 その場合、時間が経つにつれてバッグの価格は安くなっていきます。最初は10%割引、次に30%、さらに50%と割引率が上がり、最終的に購入できるほど安くなるのです。これがオランダ式オークションの仕組みです。これをイーサリアム上で実現するには、ERC721コントラクトとオランダ式オークション用スマートコントラクトの両方を作成し、デプロイする必要があります。
Quicknodeのイーサリアムエンドポイントを設定する
Remix IDEとMetaMaskウォレットを使用して、イーサリアムのSepoliaテストネットにコントラクトをデプロイします。MetaMaskのデフォルトのSepoliaネットワークを使用することも可能ですが、MetaMaskはイーサリアム開発者の大多数が利用している非常に人気のあるツールであるため、ネットワークが混雑することがあります。これを回避するために、こちらで無料のQuicknodeアカウントを作成し、イーサリアムSepoliaのエンドポイントを作成した上で、MetaMaskにQuicknodeをカスタムプロバイダーとして追加します。

MetaMaskにカスタムネットワークを追加する
さて、なぜMetaMaskでカスタムネットワークを設定したいのか、不思議に思われるかもしれません。ネットワーク一覧にあるデフォルトのSepoliaテストネットを使ってもいいのですが、レート制限を受けず、最大8倍の速度で利用できるため、ここではカスタムネットワークを使用します。
MetaMaskウォレットを開き、右上の省略記号をクリックします。「設定」→「ネットワーク」→「ネットワークを追加」の順にクリックします。新しいページが開いたら、MetaMask拡張機能ページの下部にある「手動でネットワークを追加」ボタンをクリックします。
以下の項目を入力するよう求められます:
- ネットワーク名:Sepolia Quicknode
- 新しい RPC URL:
お客様のQuickNode HTTPプロバイダーのURL - チェーンID:11155111
- 通貨記号:ETH
- ブロックエクスプローラーのURL(任意):https://sepolia.etherscan.io/
Quicknode マルチチェーン・ファウセット
スマートコントラクトのデプロイや操作にかかる費用を支払うために、テスト用のETHを用意する必要があります。
Quicknode Multi-Chain Faucetにアクセスし、ウォレットを接続するか、ウォレットアドレスを貼り付けてください。イーサリアムチェーンとセポリアネットワークを選択してから、資金の受け取りをリクエストする必要があります。

なお、ファウセットを利用するには、イーサリアムメインネット上に少なくとも0.001 ETHが必要となります。
ウォレットへの入金完了後、デプロイのセクションに進むことができます。
オランダ式オークション契約の作成と展開
それでは、Remix.IDEに移動し、2つの新しいSolidityファイルを作成してください。1つは「dutchAuction.sol」、もう1つは「NFT.sol」という名前です。まず、オランダ式オークションのコントラクトで使用するERC-721トークンであるNFTを作成し、デプロイする必要があります。
NFT.solファイルを開き、以下のコードを入力してください:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC721, Ownable {
constructor(address initialOwner)
ERC721("MyToken", "MTK")
Ownable(initialOwner)
{}
function safeMint(address to, uint256 tokenId) public onlyOwner {
_safeMint(to, tokenId);
}
}
上記のコードは、OpenZeppelinの「Contracts Wizard」から取得したものです。このツールには、開発者が利用できる事前設定済みのコードテンプレートが用意されています。
このコードでは OpenZeppelin ライブラリを利用しており、具体的には ERC721 そして 所有可能 モジュール。ERC721モジュールは、NFTの作成および管理に必要な基本機能を提供し、Ownableモジュールは、コントラクトの特定の機能に、コントラクトの所有者のみがアクセスできるようにします。このコントラクトは、名前を指定して初期化されます MyToken そして、記号 MTK. このコントラクトのコンストラクタは、initialOwnerというアドレスパラメータを受け取り、このアドレスを最初の所有者として設定します。このコントラクトの中核となる機能である、 safeMint この関数により、所有者は安全に新しいトークンを鋳造することができます。この関数は、 onlyOwner 修飾子。所有者が新しいトークンを鋳造することを決定すると、内部の _safeMint ERC721モジュールの関数が呼び出され、そこで実際のミント処理が行われます。
Remix.IDEで、まず(「Solidity Compiler」タブで)コントラクトをコンパイルし、次に(「Deploy & Run Transactions」タブで)デプロイして、コントラクトをデプロイしてみてください。このコントラクトはコンストラクタを使用しているため、 最初の所有者 フィールド。デプロイが完了すると、 safeMint スマートコントラクト上の関数で、次のセクションで使用するNFTを鋳造します。鋳造されたNFTのコントラクトアドレスは、デプロイする際に必要となるため、必ず保存しておいてください。 オランダ式オークション 契約。
次に、dutchAuction.solファイルに、以下のコードを貼り付けてください:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC721 {
function transferFrom(
address _from,
address _to,
uint _nftId
) external;
}
contract dutchAuction {
uint private constant DURATION = 7 days;
IERC721 public immutable nft;
uint public immutable nftId;
address payable public immutable seller;
uint public immutable startingPrice;
uint public immutable discountRate;
uint public immutable startAt;
uint public immutable expiresAt;
constructor(
uint _startingPrice,
uint _discountRate,
address _nft,
uint _nftId
) {
seller = payable(msg.sender);
startingPrice = _startingPrice;
discountRate = _discountRate;
startAt = block.timestamp;
expiresAt = block.timestamp + DURATION;
require(_startingPrice >= _discountRate * DURATION, "Starting price is too low");
nft = IERC721(_nft);
nftId = _nftId;
}
function getPrice() public view returns (uint) {
uint timeElapsed = block.timestamp - startAt;
uint discount = discountRate * timeElapsed;
return startingPrice - discount;
}
function buy() external payable {
require(block.timestamp < expiresAt, "This auction has ended");
uint price = getPrice();
require(msg.value >= price, "The amount of ETH sent is less than the price of token");
nft.transferFrom(seller, msg.sender, nftId);
uint refund = msg.value - price;
if (refund > 0) {
payable(msg.sender).transfer(refund);
}
selfdestruct(seller);
}
}
上記の契約に関する説明:
主な変数:
期間: オークションの期間を表す定数で、7日に設定されています。nft: オークションにかけられているERC721トークンを表す不変の変数。nftId: オークションにかけられるERC721トークンの固有ID。売り手: ERC721トークンの最初の所有者。契約のデプロイ元として初期化される。開始価格: オークション開始時のトークンの初期価格。割引率: トークンの価格が時間の経過とともに下落する割合。startAt: オークションが開始される時刻のタイムスタンプ。expiresAt: オークション終了時刻のタイムスタンプ。開始時刻に「DURATION」を加算して算出される。
主な機能:
1. コンストラクタ
この関数は、売り手や開始価格などの主要な変数を初期化することで、コントラクトの初期状態を設定します。さらに、開始価格が割引率や期間に比べて低すぎないかどうかも確認します。
2. getPrice
経過時間と割引率に基づいて、トークンの現在の価格を計算するパブリックなビュー関数です。使用される計算式は、開始価格から時間の経過に伴う累積割引額を差し引いたものです。
3. 購入する
この外部支払機能により、ユーザーはトークンを購入することができます。この機能には、以下の事項を確認するためのチェック機能が含まれています:
- オークションはまだ終了していません。
- 送金額は、現在のトークン価格以上となります。
- 購入が正常に完了すると、transferFrom メソッドを使用して ERC721 トークンが購入者に譲渡されます。送金されたイーサのうち余剰分は購入者に返金され、契約は終了し、その残高は販売者に送金されます。
コントラクトをコンパイルしてください。コンパイル中にエラーが発生した場合は、正しいSolidityコンパイラのバージョンが選択されていることを確認してください。

次に、「デプロイ」タブに移動し、「ENVIRONMENT」オプションで「Injected Web3」を選択します。また、その下に「Sepolia (11155111) network」と表示されていることを確認してください。表示されていない場合は、先ほど追加した「Quicknode Sepolia」ノードを選択し、ドロップダウンメニューの「CONTRACT」オプションから「dutchAuction」を選択します。その後、「デプロイ」ボタンの横にある小さな矢印をクリックし、以下の詳細情報を入力してください。
- NFTの開始価格はgwei単位で、ここでは10,000,000 gweiです。
- 契約がデプロイされてから7日間、または価格がゼロより大きくなるまでのいずれか早い時点まで、NFTの価格が毎秒低下する割引率。ここでは1とする。
- 先ほどデプロイしたNFTコントラクトのアドレスです。
- 当社のNFTのID。

次に、「トランザクションボタン」をクリックし、MetaMaskからトランザクションを承認してください。トランザクションが承認されると、当社のオランダ式オークション契約がデプロイされます。
オークションの実施
オランダ式オークションのコントラクトがデプロイされたので、実際に動作を確認してみましょう。まず、NFTを転送できるようにするために、dutchAuctionコントラクトを承認する必要があります。デプロイ済みのNFTコントラクトに移動し、approve関数を展開して、最初のフィールドにdutchAuctionコントラクトのアドレスと、販売するトークンのNFT IDを貼り付けます。

それでは、MetaMaskで別のアカウントに切り替え、デプロイ済みのdutchAuctionコントラクトを展開し、以下の手順を実行してください:
- 「getPrice」ボタンをクリックして、NFTの現在の価格を確認してください。
- その値をコピーし、画面を上にスクロールして、コントラクトのデプロイ時にコントラクト名を選択した場所のすぐ上にある値入力欄に貼り付けてください。
- 次に、画面を下にスクロールして、デプロイ済みのdutchAuctionコントラクトに戻り、「buy」をクリックしてください。



取引が完了すると、NFTの所有権が移転され、オランダ式オークションが終了します。
また、契約は現在破棄されています。出力は一切返されません:

結論
ここまで読み進めた皆さん、おめでとうございます。あなたはSolidityのエキスパートへの道を歩み始めています。このガイドでは、スマートコントラクトを用いたオークションについて学び、ダッチオークションを実行するためのSolidityでのスマートコントラクトの作成とデプロイについて解説しました。
イーサリアムに関する記事やガイドをもっと読みたい方は、ぜひニュースレターにご登録ください。ご意見やご感想がございましたら、Twitterからお気軽にお寄せください。また、Discordコミュニティサーバーでは、これまで出会った中で最もクールな開発者たちが集まっていますので、いつでもお気軽にチャットで交流してください :)
皆様からのフィードバックを心よりお待ちしております!❤️
ご意見や新しいトピックに関するご要望などがありましたら、ぜひお知らせください。皆様からのご連絡を心よりお待ちしております。
