フラッシュローンで DEX アービトラージをやってみる — 自動化 —
DeFiプロトコルでフラッシュローンを行えるスマートコントラクトである『OrFeed』を昨年ご紹介しました。OrFeedは取引を行いたい複数のDEXを指定してTriangularアービトラージを指示できるスマートコントラクトです。OrFeedの弱点はトレードのトークンや指定できるDEXが限定されていることです。今回はNode.jsでアービトラージの裁定取引を監視しつつ、取引機会が訪れたときにフラッシュローンのコントラクトへトランザクションを投げるシステムを作成してみました。元ネタはこちらです。YouTuberの方の有料講座のコードベースですがコードは無料でgithubに公開されていました。ここでテストしているトークンはステーブルコインのみですが、自由にトークンを追加することで様々な取引機会を狙えると思います。本記事および公開されているコードは個人的な実験用であり、そのままで動作することおよびその利益については保証しておりませんのでご了承ください。
作るものです。1. から 2. を呼び出すことでアービトラージの機会があったときに秒速でトランザクションを作成して発行します。Node.jsスクリプトはforeverを使って常時動作する状態にしておきます。
- アービトラージ機会を発見するNode.jsスクリプト
- dYdXのSoloMarginを使ったFlashloanコントラクト
フラッシュローン(Flashloan)については説明いたしません。ただ今回はTriangularアービトラージではなく単純なアービトラージを対象としているのでどのような状態をアービトラージの機会として補足しているか書き残しておきます。フラッシュローンはシングルトランザクションで裁定解消できるアービトラージしか対象とできません。裁定解消に時間差のあるような取引は難しいということになります。そこで単純に現物トークンでとある取引所の価格と他のある取引所での価格に乖離のある場合を考えます。
例をあげます。ETHを使ってUSDCの取引を行う場合、取引所Aで安く買えるUSDCを他の取引所Bでは買った価格より高く売れるということが起こり得ます。この時の価格差とレバレッジした資産の額がプロフィットへ影響します。フラッシュローンを活用することで売買資金の調達から複数取引所での売買を1つのトランザクションへ内包できるため最初の取引所Aで安く買えたが取引所Bで売れなかった等というリスクが低減されるメリットもあります。返済ができなければトランザクションがリバートするだけなので。
この単純なアービトラージの場合に2つの取引所があるときには2方向の売買を評価することができます。ETH -> USDC -> ETH と取引する場合は以下の通り。
- KyberSwapでUSDCを買ってUniswapでUSDCを売る
- UniswapでUSDCを買ってKyberSwapでUSDCを売る
いま現在多くのDEXがあるために、すべてのDEXにおけるトークンスワップの価格の組み合わせを網羅的に調査することは難しいと思いました。ただ1inchのようなDEX AggregatorであればそれぞれのDEXで価格差があるかどうかをベストプライスで把握することができます。つまりETHでUSDCを購入する場合のレートとその購入したUSDCでETHを買い戻すベストプライスを1inchは提示してくれます。市場に歪みがあるときには両方向のスワッピングルートは異なることが多く、購入と売却におけるDEXやディストリビューションがばらつきます。ディストリビューションについては過去記事である『1inch.exchangeでトークンスワップをやってみる — EthereumDev Tutorial — 』に書いてあります。今回は1inchを使ってスワップのレート計算をしてみました。
distributionはuint256型の配列で各DEXに対するスワップの比率が格納されています。より正確には各DEXというより各コントラクトのスワップに対する比率となり、例えばETHからDAIへの交換の44%はUniswap V1で56%はUniswap V2で行う、といった良いレートで交換が成立するコントラクトの使用比率を意味します。[1]
アービトラージ機会を発見する
元ネタのコードではKyberSwapとUniswapのレートを監視してその価格差からトランザクションコストを引くことで利益を計算しています。前述のとおりこの記事では1inchでトークンの行きのスワップ(例えば ETH -> USDC)と帰りのスワップ(同様に USDC -> ETH)のレートを計算してアービトラージの機会があるかどうかを判断します。USDT/ETH、USDC/ETH、DAI/ETHを試しに見てみましたがDAI/ETHでは少額ながらアービトラージの機会が発生しているようでした。以下の画像のような結果を出力するNode.jsスクリプトを作成します。
コードの抜粋で説明します。まず fromToken
にETHをセットし toToken
にはUSDCのトークンのアドレスを格納します。 fromTokenDecimals
や toTokenDecimals
はスワップ後のトークンの桁のパディングに使います。USDCはデシマルが6と中途半端なので桁数を変更する必要があるためです。これらのトークンアドレスを使って1inchのスマートコントラクトへレートを照会します。
この箇所はETHから交換先のトークンへのスワップレートを取得する部分です。returnAmountでスワップされた後に受け取るトークン量が返されます。distributionはuint256型の配列で各DEXに対するスワップの比率が格納されています。distributionが0%以上の取引だけをループでコンソール表示しています。returnAmountは戻りのスワップへと渡す予定です。
returnAmountをスワップするトークンとして逆方向のレートを取得します。順方向のreturnAmountはUSDC建てのデシマル6で、逆方向のレートはETH建てのデシマル18で計算されており、これらをドル建てにした上でトランザクションコストを減算することでプロフィットを計算します。
oneSplitRates
というjsonに購入するトークンとそれを売却した際のトークンをドル建て(USDC建て)で格納しました。売却分から購入分とトランザクションコストを減算したものを profit
という変数に入れています。 profit
がプラスのときにデプロイしたフラッシュローンのコントラクトを呼び出してこのスワップを実行します。
dYdXでフラッシュローンを実行する
厳密にはdYdXにフラッシュローンと呼ばれる機能はありません。dYdXではSoloMarginのスマートコントラクトでWithdraw -> Call -> Depositをシングルトランザクションで実行することでフラッシュローンと同等のトランザクションを発行できます。dYdXはETHはなくWETHのサポートなので、今回はWETHを引き出して1inchで取引を行った上でまたdYdXへデポジットするという流れです。[2]
Aaveは比較的易しかったのですが、dYdXのインターフェースやスマートコントラクトを理解するには敷居が高かったです。money-legosというDeFiプロトコルを使用したアプリケーションの開発を助けてくれるライブラリがあります。『money-legosでDeFiと戯れてみる』でも紹介した通りプロトコルのABI (Application Binary Interface)やアドレスを提供してくれていて、公式サイトにはサンプルのコードも用意されています。[3]
今回はmoney-legosを使用しました。EthereumDevで紹介されているKollateralというパッケージも良さそうです。KollateralはdYdXやAaveのフラッシュローンをラップするライブラリでめちゃ簡単にフラッシュローンを実行できるようでした。[4]
スマートコントラクトはSolidityで開発します。コードはmoney-legosのdYdX Flashloan Logic (Solidity)をベースに書いています。その中でもアービトラージのロジックを書いている callFunction
部分だけ抜粋してみます。 _swap
は1inchで行きと帰りのスワップを実行するinternal関数です。元のトークンからdYdXへデポジットする repayAmount
を減算したものを beneficiary
アドレスへ送金して終わりです。
あとは完成したスマートコントラクトをコンパイルしてデプロイするだけです。Remixからもデプロイしたコントラクトが見えることを確認できました。ちなみにメインネットへコントラクトを打ちましたがガスコスト110Gweiぐらいで通しましたww
最後の最後にNode.jsのスクリプトを動かす環境ですがスクリプトのままサーバ上のforeverを使って動かしています。foreverはNode.jsアプリを常駐化してログなども吐いてくれるライブラリです。Herokuを使ったりクラウドのサーバレス化でも対応できますが、アプリ自体が軽量なので今回はこのままで。ただプライベートキーなどの情報をdotenvで読み込んでしまっているのでその点は改良の余地があると思います。
$ npm install -g forever
$ forever --version
v3.0.4$ forever <your file name>.js
$ forever list
foreverでは標準出力は ~/.forever/
配下にログとしてファイル保存されていくのでファイルから出力結果を確認することができます。Out of Gasでトランザクションがリバートされてしまうのですが、アービトラージの機会がときたまあることは確認ができました。
-------------------------------------------------------------
New block received. Block # 11584634
GasLimit: 12445131 and Timestamp: 1609718326
Trading DAI ...
Forward Swap
Uniswap V2: 88%
Uniswap V2 USDC: 12%
Inverse Swap
Uniswap V2: 89%
Uniswap V2 USDC: 11%
1inch Exchange DAI/ETH:
{"buy":"956415.818342323740250171","sell":"959957.4809119636"}
transaction cost: 28.22516885680787
profit: 3513.437400783047
Arbitrage opportunity found!
今回のコードリポジトリは以下です。
まとめ
- 1inch.exchangeはDEX aggregatorの1つで、サポートされる複数のDEXの中から最適なレートと低いスリッページを発見する
- 1inch.exchangeのプラットフォーム上でユーザは効率的に最適なトークンスワップをシングルトランザクションで実現できる
- money-legosはDeFiプロトコルのためのJavascriptライブラリでプロトコルの操作のコストを下げることができる
- Node.jsとSolidityによる開発でアービトラージの機会発見およびフラッシュローンによる裁定を自動化できた