パンケーキとベーカリースワップで洋風アービトラージを楽しむ会
皆さんお高い雲丹や寿司はもうあきましたね??幸運なことにパンケーキやベーカリーの方はお値段がお安くなっております。Ethereum上のDeFiアービトラージはなかなか難しく失意の日々を送っておりました。特にガス代の高さが半端ではなくトランザクションのrevert、それに伴うEthのロストが発生していたので早い段階で止めています。前回の記事でBinance Smart Chain(以下BSC)でもフラッシュローンを行えることが判明しました。ので、今回はガス代の安いBSCでパンケーキアービトラージャー職人になれないか検討してみます。アービトラージですので他方の交換所(DEX)が必要ですがベーカリースワップを選択してみました。
※BSC上の古参のDEXとしてはBinanceのページにも記載があるとおりバーガースワップなども存在します [1]
Disclaimer
This article is not either an investment advice or a recommendation or solicitation to buy or sell any investment and should not be used in the evaluation of the merits of making any investment decision. It should not be relied upon for accounting, legal or tax advice or investment recommendations. The contents reflected herein are subject to change without being updated.
目次です。
- フラッシュローンの選択
- Flash Swapsとは
- アービトラージ監視スクリプト
- フラッシュスワップコントラクト
フラッシュローンの選択
Multi-Chain Lend(MCL)はBinance Smart Chain(BSC)上のAave V1フォークでフラッシュローンをサポートしています。前回の記事で確認したとおり現在は流動性の問題があり、それほど多くのアービトラージ原資を得ることができないので今回は初物ではありますがFlash Swapsにチャレンジしてみます。こちらでも記載しましたが、dYdXのSoloProtocol、Aave V1/V2のフラッシュローン以外にUniswapに付属しているFlash Swapsという機能でもフラッシュローンと同等のことが行えます。
パンケーキスワップのドキュメントには明記されていないのですが、コードベースからこの機能がパンケーキに実装されていることを確認できたのでパンケーキでフラッシュスワップしてベーカリースワップで戻した資産をパンケーキに返済する形でフラッシュローンを使ったアービトラージを実現してみようと思います。パンケーキやベーカリースワップはUniswapフォークなので基本的には同じ動作をするはずです。[2]
Flash Swapsを使ったアービトラージの場合にはdYdXやAaveのプロトコルから原資を借りるのではなく、流動性プールから直接トークンを使用することができます。したがってシンプルなケースとして以下のようなアービトラージの例が考えらます。
- BNBをパンケーキで借りる(Flashloan BNB at PancakeSwap)
- BNBを売りBUSDを買う(Sell BNB for BUSD at BakerySwap)
- BUSDを売りBNBを買う(Sell BUSD for BNB at PancakeSwap)
- BNBをパンケーキで返す(Reimburse BNB at PancakeSwap)
さらにFlash SwapsではdYdXやAaveのレンディングとは異なり、同じトークンで返さなくてもよいという利点があります。借り入れた元のトークン額に時価で相当する保有トークンで返済を行えるとのこと。その場合はこのようなアービトラージの例が考えられます。BNBを借りてもBTCBであったりUSDCでパンケーキスワップへ返済できます。
- BNBをパンケーキで借りる(Flashloan BNB at PancakeSwap)
- BNBを売りBUSDを買う(Sell BNB for BUSD at BakerySwap)
- BUSDを売りBTCBを買う(Sell BUSD for BTCB at PancakeSwap)
- BNBに相当するBTCBをパンケーキで返す(Reimburse equivalent BTCB for BNB at PancakeSwap)
Flash Swapsとは
AaveやdYdXのフラッシュローンがレンディングプールにある流動性を借り入れる形式であることに対して、UniswapのFlash Swapsは仕組みが若干異なります。まずFlash Swapsは流動性プールにおけるスワップの1つの形態で、Uniswapの通常のトークン交換そのものです。
違いは流動性ペアに対してユーザが片側のトークンを送り、コントラクトがそれを受領する前に交換先のトークンを受け取ることができる点です。例えば100 WBNBを3000 BUSDに交換できるとすると、3000 BUSDが msg.sender
へ先に送付され100 WBNBの着金は先送りされます。
※先送りといっても同じトランザクション内で着金される必要がある
Flash Swapsは使い方にくせがあるので説明します。基本的にはPairに実装されている swap
関数を呼ぶだけです。WBNBとBUSDの例でいうと amount0Out
にWBNBの数量を amount1Out
にBUSDの数量を渡します。BUSDはスワップ先なので0となります。addressはトークンの送付先ですが、スマートコントラクトのアドレスを設定します。 swap
の呼び出し元コントラクトでアービトラージの処理をするのであれば address(this)
で良いでしょう。
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data);
最も重要な点は bytes calldata data
です。 data.length
が0の場合は通常のスワップと見なされます。つまり交換元のトークンが先にペアコントラクトに着金している必要があると見なされます。Flash Swapsを行いたい場合は bytes calldata data
を空にせずパディングします。以下の例では bytes('not empty')
としてパディングすることでこのスワップはFlash Swapsであることをお知らせしています。
Flash Swapsとしてスワップが行われると交換先のトークンを先出しし、プロトコルのコールバック関数を呼びます。対象のコールバック関数内で交換元のトークンをペアコントラクトへ返すことでスワップ成立です。これらの処理をアトミックな単一のトランザクションへ内包することでフラッシュローンのような処理を行うことができます。AaveやdYdXのようにレンディングプールから原資を借りてまたプールへ返却する仕組みではなく、スワップの変形であるということですね。
コールバック関数の例のスニペットです。交換した後のトークンはペアコントラクトへ戻す必要がありますが、Flash Swapsは通常のスワップと同様に0.3%のスワップ手数料がかかります。パンケーキスワップのコールバック関数名は pancakeCall
です UniswapV2Call
としても動作しないので気を付けてください。UniswapのリポジトリにFlash Swapsの使用例が公開されています。[6]
アービトラージ監視スクリプト
パンケーキはNode.jsのSDKがあったのですがベーカリーのSDKは見当たりませんでした。SDKを使わずにweb3.jsだけで今回はやってみます。このスクリプトはブロックが生成されるたびにDEXからスワップした場合の価格情報を取得してアービトラージの機会を監視します。利益の生まれそうなアービトラージのチャンスが発生したら、想定されるトランザクションコストを考慮してフラッシュスワップのコントラクトへトランザクションを投げます。利益が生まれる場合にはトランザクション送信者へプロフィットがペイバックされ、フラッシュスワップが失敗した場合にはトランザクションはリバートされます。
以下のようなNode.jsのパッケージを使いました。ベーカリーはSDKがないということで今回はJavascript SDKを使わずスクリプトを書いてみましたがこちらの方が分かりやすいような気がしてきました。たぶんSolidityのインターフェースみてJSから呼べる人はその方が直感的に分かりやすいという罠ですね。Fetcher、Trade、Routeとかのエンティティの使い方を調べてたときは時間が溶けました。全フォークプロトコルのSDKパッケージがUniswapのフォークなのでこれは厄介ですね、、、
$ truffle init
$ npm init -y
$ npm install web3
$ npm install dotenv
$ npm install bignumber.js
$ npm install @truffle/hdwallet-provider
パンケーキとベーカリーでアービトラージの機会が発生するかを監視するスクリプトはこのようになりました。foreverを使って走らせておきます。foreverはNode.jsアプリを常駐化してログなども吐いてくれるライブラリです。出力は適宜修正して加工しやすいようにしておきます。以下の画像のとおりWBNB -> BUSD -> WBNBではプロフィットの機会がなさそうですね。通貨ペアの追加、ローン額を変更して色々なパターンを確認する予定です。
$ npm install -g forever
$ forever --version
v3.0.4
$ forever <your file name>.js // forever test.js
$ forever list
フラッシュスワップコントラクト
フラッシュスワップの特性からコールバックは流動性ペアの片側が0のときに有効ということにします。パンケーキ上でWBNB -> BUSDとスワップしたい場合には、WBNBを0としてBUSD側を先出ししてもらい、BUSDをベーカリースワップでWBNBにスワップ、そのスワップしたWBNBをパンケーキの流動性ペアへ戻すことでパンケーキ上のスワップを成立させます。
コールバック関数内ではまず amountRequired
としてパンケーキへ戻すWBNBをgetAmountsIn
で計算します。次にベーカリースワップで先出ししてもらったBUSDをWBNBへ amountReceived
としてスワップします。この amountReceived
と amountRequired
の差額がプロフィットです。 amountRequired
は流動性ペアへと戻すことでこのトランザクションが成功します。
サンプルですがFlashswap.solは以下のようになりました。
Truffle consoleからなぜかデプロイできなかったのでコントラクトはRemixからBSCへデプロイしました(前回と同じやり方でできます)。あとはアービトラージの機会がありそうなペアを見つけてNode.jsのスクリプトからコントラクトを発火するだけですね。詳細はリポジトリを参照くださいませ。
まとめ
- パンケーキスワップやベーカリースワップはUniswapのフォークなのでFlash Swapsが存在する
- Flash Swapsは通常のトークンスワップの変形で交換先トークンを先に受領して使用することができる
- パンケーキとベーカリーでは流動性に差があるのでFlash Swapsはパンケーキ側で行った方がよい
- 簡単なアービトラージ監視スクリプトとフラッシュスワップのコントラクトがデプロイできた