「Uniswap」プロトコルを使ってEtherとERC20トークンの交換アプリを作ろう

「Uniswap」プロトコルを使ってEtherとERC20トークンの交換アプリを作ろう

分散型取引のためのプロトコル「Uniswap(ユニスワップ)」を使うと、非常に分散的でありながら優れたユーザー体験を実現できます。この記事では、EtherとERC20トークンを交換する簡単なアプリ開発をとおしてUniswapを理解し、その実装ノウハウを学びます。

はじめに

ブロックチェーンは、誰もが使えて誰も支配しないパブリックな台帳を歴史上初めて実現しました。しかし、実際には多くの中央集権的な交換所がハッシュパワーとプライベートキーを支配しているのが現状です。中央集権的な交換所は法定通貨と暗号通貨を交換するのには不可欠なのですが、その存在は当初のビットコインの哲学とは反しています。ブロックチェーンは、会社や政府などの機関から、個人へと力を移行させる技術のはずでした。

 

どうすれば本来の哲学に戻り、個人に力を移せるのか。そのために分散型なプロトコルの1つとして取引所の開発がすでに進められています。そのなかで私が注目しているのが「Uniswap」という交換プロトコルです。分散型取引所は実装によってどの程度分散的であるかが異なりますが、私がUniswapに注目しているのは、非常に分散的でありながらユーザー体験が優れているからです。

 

既存の仕組みを代替するには、ユーザーが使いたいと思う、よりよいサービスを創るしかないでしょう。これはプログラマーであればコードを書き、実際にモノを創ることを意味します。この記事でやろうとしているのは、まさにそれです。具体的には、EtherとERC20トークンを交換する簡単なアプリを作ります。

 

この記事を読み終えるころには、Uniswapプロトコルを使ってDappやウォレットのなかでEtherとERC20トークンを交換する機能を追加できるようになります。

 

まずはプロトコルの仕組みを簡単に説明してから、アプリを作っていきます。

Uniswapの簡単な説明

Uniswapは、EtherとERC20トークンのための分散的な交換プロトコルです。交換のプロセスはすべてブロックチェーン上で行われ、間に誰も介入しません。第三者が介入しない代わりに、それぞれのERC20トークンごとに別々のスマートコントラクトがあります。これらの交換コントラクトは、Etherと特定のERC20トークンを備えています。トレーダーはこの貯蓄を使っていつでも交換することができます。Etherをコントラクトに送れば特定のERC20トークンを得ることができ、逆に特定のERC20トークンを送ればEtherを得ることができます。

 

この貯蓄はリクイディティ・プロバイダーによって貯められ、彼らは見返りとして手数料を交換が行われるごとに取ります。「リクイディティ」というのは流動性のことで、EtherとERC20トークンの流動性を提供するので「リクイディティ・プロバイダー」と呼ばれます。ERC20トークン同士を交換することも可能です。

 

交換するときの価格は、Constant Product Market Maker Modelという価格設定の式によって自動で決められます。交換コントラクト内のEtherの量とERC20トークンの量が常に一定になるように価格が計算されます。この式はERC20トークン同士の交換でも同様に適応され、交換コントラクト内のERC20トークンAの量とERC20トークンBの量が常に一定になるように価格が計算されます。

 

例えば、DaiというERC20トークンの交換コントラクトに10ETH1000DAIが入っているとします。ここで定数は 10 x 100010000となります。次にこのコントラクトに1ETH送ると、コントラクトは11ETH持つことになります。Etherの量とDaiの量をかけた値は常に10000で一定でなければいけません。つまりDaiは10000 ÷ 11 ≒ 909DAIでなければならないということです。逆にいえば、コントラクトは1000DAIを持っている必要はないので、1000 – 909 = 91DAIがトレーダーに渡されることになります。

 

この式は別名 x * y = k 式とも呼ばれ、xとyの値によって以下のような曲線を描きます。


Uniswapがすばらしいのは、貯蓄システムと自動計算式によって売り手と買い手をマッチするコストを減らしたことにあります。このおかげでトレーダーは簡単かつ迅速に交換できます。

セットアップ

では早速アプリを作るためのセットアップに進みましょう! これから作るアプリは、EtherとDaiを交換できる簡単なNodeJSのアプリです。

 

GitHubでレポジトリを作ったので、先に軽く目を通しておくことをお勧めします。地図を持っていると道を進みやすいのと同じで、最終的にどういうコードになるのかわかった方が、コーディングが楽になります。私が使っているOSはmacOS High Sierraです。

 

プライベートキーとアドレスの取得
vanity-ethからプライベートキーとアドレスを取ってきてください。Generateボタンを押すと一番下に生成されたプライベートキーとアドレスが出てきます。安全ではないので、テスト目的以外で使わないようにしましょう。

 

RinkebyテストネットのEtherを手に入れる
RinkebyのEtherを持っていない人は、faucetから取ってきてください。以下のようにツイートして、

このツイートのリンクをフォームに貼り付けるともらえます。時間がかかる場合は友達にお願いするか、もしくは@taisuke_mino_JPにツイートしていただければ少しは予備があるかもしれません。

 

残高はRinkeby Etherscanでアドレスを検索して確認することができます。0.1ETHあれば十分です。

 

Npmのプロジェクトを作る

※npmとnodejsが入っていない方はこちらからダウンロードできます。

 

gitをinitializeする

 

web3のパッケージをインストールする

web3はEthereumのJavaScript APIと呼ばれています。テストネットを含めたEthereumのネットワークへのインターフェースです。JavaScriptのアプリからEthereumのノードとやりとりしたり、ブロックチェーン上にデプロイされているスマートコントラクトとやりとりすることもできます。

 

Infuraのエンドポイントを取得
EthereumのネットワークとやりとりするためのプロバイダーとしてInfuraを使います。Infuraに登録して新しいプロジェクトを作ってからRinkebyネットワークのエンドポイントを取得してください。URLは以下のようなフォーマットになります。

 

ethereumjs-txパッケージをインストールする

ethereumjs-txは、トランザクションオブジェクトを生成したり、サインするのに使います。

EtherからDaiへの交換

ではここからはコードを書いていきます。

 

ファイル作成
EthToDaiRinkeby.mjsというファイルを作成します。mjsは、ES6の文法で書くための拡張子です。

 

パッケージのインポート

 

交換コントラクトのアドレスとabiを宣言
Rinkebyテストネット上のDaiの交換コントラクトアドレスとabiを宣言します。こちらのリンクから引っ張ってきています。この定数は、コントラクトとやりとりする上で必要になります。

 

交換コントラクトアドレス:

*Etherscanでも見ることができます。

 

交換コントラクトabi:

 

アドレスとプライベートキーの定数宣言

 

Web3のセットアップ
Infuraをweb3プロバイダーとして使うためにセットアップします。

 

コントラクトをインスタンス化
web3のContractメソッドにアドレスとabiを渡して、コントラクトをインスタンス化します。

これでDai交換コントラクトの関数を呼んでEtherとDaiを交換する準備ができました。

 

ETH_SOLD定数宣言
Daiと交換したいEtherの量をもつ定数を宣言します。

 

ethToTokenSwapInput関数のabiをエンコード
EtherをDaiと交換するのに、ethToTokenSwapInput関数を使います。

min_tokensdeadlineを引数にとります。min_tokensでは購入するDaiの最低額を指定します。deadlineではトランザクションの期限を指定します。つまり期限以内に実行されなかったトランザクションは失敗することになります。

 

これらの引数はフロントランニングを抑止するためのものです。仮に1ETHを交換する時の価格が200DAIだとしましょう。199DAI以下しかもらえない場合にはトランザクションが失敗するように指定することで、フロントランナーが価格を操作したとしても大損することはありません。また期限を設定することで、フロントランナーが利益を得るためにトランザクションを持ち続けることを抑制できます。フロントランニングについてはこちらの投稿が詳しいです。

 

min_tokens引数に渡すための定数を宣言します。

値が低すぎるとフロントランナーに簡単に利益を取られてしまい、逆に高すぎるとトランザクションが失敗しやすいので、バランスを考える必要があります。

 

deadline引数に渡す別の定数を宣言します。以下の値は、UTC時間2019年10月1日午前12:00のUnixタイムスタンプです。すでに以下の日時を過ぎてしまっている場合は、こちらのオンラインのコンバーターを使ってください。

次にethToTokenSwapInput関数に2つの値を渡し、abiをエンコードします。

 

sendSignedTx関数の宣言
トランザクションオブジェクトにサインして、それをEthereumネットワークにブロードキャストする関数を宣言します。

 

トランザクションオブジェクトを作成し、sendSignedTx関数を実行

exchangeEncodedABIETH_SOLDをそれぞれdatavalueプロパティに渡しています。

 

ファイルを走らせる

 

0xbbc617c97323301d7376683a0ae58db012b418f27963e54866eb92bec4839e10のようなトランザクションハッシュを受け取ったら、Etherscanで詳細を見ることができます。以下は私のトランザクションの例です。

https://rinkeby.etherscan.io/tx/0xbbc617c97323301d7376683a0ae58db012b418f27963e54866eb92bec4839e10

トランザクションが成功していれば、「Erc20 Token Txns」というタブメニューから自分のアドレスにDaiが入っていることが確認できるはずです。


これでEtherとDaiを交換できました!

DaiからEtherへの交換

次にDaiからEtherに交換します。今回は少しトリッキーで、2つのトランザクションが必要になります。最初にUniswapのDai交換コントラクトに自分のアカウントに代わってDaiを送る許可を与えます。次にtokenToEthSwapInput関数を呼んでDaiをEtherと交換します。

1. UniswapのDai交換コントラクトに許可を与える

別のapproveDaiExchangeRinkeby.mjsというファイルを作ります。今回は前のコードと重複している部分は省きます。最終的なコードはこちらを参考にしてください。

 

トークンコントラクトのアドレスとabiを宣言
RinkebyテストネットでのDaiのトークンアドレスとabiを宣言します。

 

トークンコントラクトアドレス:

*Etherscanでも確認できます。

 

トークンコントラクトabi:

*ERC20トークンのabiはDai以外のトークンでもすべて同じです。

 

daiExchangeAddress定数の宣言

このアドレスにDaiを自分の代わりに送金する許可を与えます。

 

Daiトークンコントラクトのインスタンス化

 

approve関数に渡す定数の宣言

daiExchangeAddressに1DAIを送る許可を与えます。

 

approve関数のabiのエンコード

 

sendSignedTx関数の宣言
トランザクションオブジェクトにサインし、それをEthereunネットワークにブロードキャストする関数を作成

 

トランザクションオブジェクトを作成し、sendSignedTx関数を実行

ファイルを走らせる

 

0x1d16e0ccb3947ecbd34e16f9ae9378a17b16e11c701f1d0f44e5262d0e8f018aのようなトランザクションハッシュを受け取ったら、etherscanで詳細を見ることができます。以下は私のトランザクションの例です。

https://rinkeby.etherscan.io/tx/0x1d16e0ccb3947ecbd34e16f9ae9378a17b16e11c701f1d0f44e5262d0e8f018a

2. DaiをEtherに交換する

次に実際にDaiをEtherに交換する部分を実装します。今回も前のコードと重複している部分は省きます。DaiToEthRinkeby.mjsというファイルを作り、tokenToEthSwapInput関数を呼びます。

この関数は引数を3つとります。tokens_sold は交換するERC20トークンの量です。min_ethは交換するEtherの最低額です。deadlineはトランザクションの期限です。

tokenToEthSwapInput関数に3つの定数を引数として渡し、abiをエンコードします。

このtokenToEthEncodedABIをトランザクションオブジェクトのdataプロパティに渡します。
ファイルを走らせます。

 

0x6011a9f50b80dba7a2cb3f9dbf327f0f4c28ec6a4205c2b4002b3a008aa480f4のようなトランザクションハッシュを受け取ったら、etherscanでその詳細を確認できます。以下は私のトランザクションの例です。

https://rinkeby.etherscan.io/tx/0x6011a9f50b80dba7a2cb3f9dbf327f0f4c28ec6a4205c2b4002b3a008aa480f4

以下のようにDaiが自分のアドレスから引かれ

Etherが追加されていることがわかります。

 


 

Dai以外の他のERC20トークンを交換したい場合は、トークンのアドレスを変えるだけで実装できます。

 

この記事がまだ存在していない新たなサービスを作るための一助になれば幸いです。😎

 

Profile

解説:三野泰佑
クリプトエンジニア
2013年頃からブロックチェーンに関わり、スマートコントラクトのベストプラクティスを閲覧できるサイトやSolidityの監査をするサービスを開発。現在はICOVOのエンジニアとして開発や執筆をしながら、ステーブルコインに特化したモバイルウォレットも開発している。