こんにちは!弐号です。
DeFi運用をしていると、いくら儲かったのか運用中の残高を知りたい場合があります。
運用先のDeFiプラットフォームのウェブサイトにいって確認すればいいのですが、複数のDeFiで運用していたりするとわざわざ全てのプラットフォームを巡回しないといけなくて大変面倒です。
そこでDeFiのスマートコントラクト上のデータを読むことで、DeFiへの預け入れ残高を取得するプログラムを書いてみましょう。
目次
既存のポートフォリオサービス
DeBank や ApeBoard といった既存のポートフォリオサービスは存在するのですが、どれもすべてのDeFiに対応しているわけではないですし、CSVファイルなどでデータをローカルにダウンロードできなかったりと、不便なものが多いです。
そのため弐号は自前でポートフォリオの計算アプリを作っており、これで資産管理を行っています。
現在のところ
- Aave
- Alpaca
- Apricot
- Astar (オンチェーン上の$ASTR)
- Binance
- ビットバンク
- dForce
- Ethereum ($ETH)
- Mango
- Mercurial
- OKX
- Quarry (Saber Lidoのみ)
- Saber
- Tulip
- Venus
- Vires
- Zeta
に対応しており、新しいDeFiを使うたびに追加していっています。
ここでは最も有名なレンディングプロトコルであるAaveの残高を取得するプログラムをTypeScriptで書く方法を見てみます。
特に、スマートコントラクトを用いて動作するDeFiの残高を取得するという性質上、DeFiにデプロイされているスマートコントラクトの内容をよく確認し、適切な関数をコールする必要がありますので、その考え方を中心に解説していきます。
ここではAaveを題材としますが、他のDeFiプロトコルであっても基本的な考え方は同じですので、ぜひご自身の利用されているDeFiでも作れるようになりましょう!
なお、本記事で使うソースコードはすべて以下のGitHubリポジトリにアップロードしてありますので、必要に応じて参照してください。
https://github.com/DeFIRE-jp/aave-balance-fetcher
ドキュメントとソースコードを読む
Aaveの開発者向けドキュメントはこちらにありますので、まずは一通り熟読します。
またスマートコントラクトのソースコードやTypeScriptライブラリがこちらのGitHubリポジトリに掲載されており、ドキュメントに書かれていない情報もたくさんありますので、こちらもざっと眺めておきましょう。
かなり量がありますが、全体像がつかめるようしっかりと読み込みましょう。
残高取得ができるエンドポイントのあたりをつける
一通り眺めると、AToken.solが貸出を行ったトークンと引き換えに発行されるトークンであり、このコントラクトの balanceOf()
関数を読めば貸出残高が取れそうです。
またStableDebtToken.solおよびVariableDebtToken.solが借り入れを行った際に発行される負債トークンとなっており、このコントラクトの balanceOf()
関数を読めば借入残高が取れそうだと分かります。
とすれば、それぞれのトークンに紐付いたこれらAToken、StableDebtTokenおよびVariableDebtTokenのコントラクトアドレスさえ分かってしまえば、そのコントラクトアドレスに対して balanceOf()
関数を呼び出すだけです。
そこで次にレンディングプール上で対応しているトークンの一覧をどうにかして取得できないか?と考えましょう。
トークン一覧を取得するエンドポイントを見つける
ドキュメントをくまなく探すと、Poolコントラクトの getReservesList()
関数が見つかります。
この関数は、レンディングプール上で対応しているトークンのコントラクトアドレスの一覧を返す関数です。
この関数を叩くことで、対応しているトークンのERC20コントラクトのリストを取得することができます。
そしてERC20トークンのコントラクトアドレスがわかれば、シンボル名は symbol()
関数を叩くことで、また小数点以下の桁数に関しては decimals()
関数を叩くことで、それぞれ取得することができます。
しかし、これだけではAToken、VariableDebtTokenやStableDebtTokenのコントラクトアドレスは分かりません。
Aave専用トークンのコントラクトアドレスを返すエンドポイントを探す
そこで次にこれらのコントラクトアドレスを取得するエンドポイントを探します。
さらにドキュメントを探すと、Poolコントラクトの getReserveData()
関数が見つかります。
この関数のコール結果のうち
- aTokenAddress
- variableDebtTokenAddress
- stableDebtTokenAddress
を読めばいいでしょう。
あとはそれぞれのコントラクトの balanceOf()
関数を読めば残高をすべて取得することができます!
ここまでの考察から得られた情報を用いて実際にTypeScriptコードにすればOKです。
https://github.com/DeFIRE-jp/aave-balance-fetcher/blob/master/src/index.ts
DeFiの残高を取得する際には適切なコントラクトのエンドポイントを見つける作業が一番大変となりますが、逆に言えばそれさえ分かってしまえばプログラムは非常に簡単にかけてしまいます。
みなさんもぜひ自身の利用するDeFiの残高を取得するプログラムを作ってみてくださいね!
演習問題
①:高速化
GitHubに掲載しているコードは、正しく残高を取得できるものの、取得するまでに手元の環境で4分近い時間を要してしまいます。
この原因は主に、コントラクトの関数を呼ぶ際のレイテンシーによるものです。
そこで、コントラクトの呼び出し処理をなるべく並列に処理し、実実行時間を減らすようにしてください。
実行時間を数秒程度まで抑えることができれば実用上も問題ないでしょう。
②:他のDeFi
例として、例えばBSCチェーン上のVenusなど他のDeFiプロトコルの残高を取得するプログラムを作成してください。
また、それを高速化し、数秒程度で残高を取得できるようにしましょう。