🌐

CeloのPrice Oracleの仕組みについて

2023/08/30に公開

はじめに

CeloにはCeloネイティブトークンを使ったアルゴリズム型のステーブルコインcUSDが存在していることをこの記事を読んで知りました。

https://gaiax-blockchain.com/celo

仕組みはLUNAと同じで、1cUSDをmintしたい場合は1ドル分のCeloと交換し、1cUSDをburnすると1ドル分のCeloが返ってくるものです。

LUNAと同じ仕組みであることよりも、Price Oracleの仕組みが気になったので詳しく調べてみました。

要約

  • OracleにはCelo独自のSortedOracles Smart Contractが使われており、トークン毎に価格が保存・更新されている
  • oracleを提出できるアドレスは複数存在しており、各アドレスから提出された価格を全て保存し中央値をoracleとして使っている
  • Celoのガス代が安いので、Chainlinkと比較するとオンチェーンの処理が多く、価格の反映が高頻度に行われる

具体的なコード

下記の順番で具体的なコードを用いながら説明します。

  1. Reporterの登録
  2. Reporterによるprice oracleの提出
  3. priceの取得

Reporterの登録

Oracleを提出できるアドレスはreporterと呼ばれ、SortedOraclesのownerのみがaddOracle()でreporterを追加できます。
codeではreporterはoracleで表現されてます。

https://github.com/celo-org/celo-monorepo/blob/958757d839148e4e792dab93107f3bebb28576d5/packages/protocol/contracts/stability/SortedOracles.sol#L114-L122

また、getOracle()で特定のトークンの価格を提出できるアドレスのリストを取得することができます。

https://github.com/celo-org/celo-monorepo/blob/958757d839148e4e792dab93107f3bebb28576d5/packages/protocol/contracts/stability/SortedOracles.sol#L309-L311

実際に実行してみると、cUSDのoracleには16個のアドレスが登録されています。
しかし、10番目から15番目のアドレスはreportしていないので、実質は11個のアドレスによりoracleが管理されていることになります。


https://celoscan.io/address/0xefB84935239dAcdecF7c5bA76d8dE40b077B7b33#readProxyContract#F2

また、余談にはなりますが、1~8番目のアドレスは$350後半の分のCeloを持っていたのは何か意味がありそうだなと感じました。(もしかしたら単一のエンティティーにより運営されているとか。。。)

Reporterによるprice oracleの提出

価格の提出はreport()関数で行われます。
提出された値の中央値を計算するために、report()を実行する場合は提出する値の一個前の値と一個後の値を提出したアドレスを引数に入れる必要があります。
また、modifierにonlyOraceとあるように、登録されたaddressしかこの関数を叩けないようになっています。

https://github.com/celo-org/celo-monorepo/blob/958757d839148e4e792dab93107f3bebb28576d5/packages/protocol/contracts/stability/SortedOracles.sol#L184-L226

実際の値のアップデートのロジックはinsert()関数で行われています。

https://github.com/celo-org/celo-monorepo/blob/master/packages/protocol/contracts/common/linkedlists/LinkedList.sol#L32-L78

priceの取得

提出されている値はの全てのリストはgetRates()で取得できます。


https://celoscan.io/address/0xefB84935239dAcdecF7c5bA76d8dE40b077B7b33#readProxyContract#F3

addressの配列はreportを提出したアドレス、uint256の配列はprice、uint8は提出したpriceと中央値の関係性を表しています。

https://github.com/celo-org/celo-monorepo/blob/958757d839148e4e792dab93107f3bebb28576d5/packages/protocol/contracts/common/linkedlists/SortedLinkedListWithMedian.sol#L16C8-L16C22

中央値を直接取得する場合はmedianRate()で取得することができます。

疑問

Reporterの登録で説明したように、Reporterの中にはアクティブではないReporterが存在しています。
Reporterの削除するためにremoveOracle()が存在していますが、この関数にはonlyOwnerのmodifierがついています。

Reporterがoracleを提出しない、どのアドレスがreporterなのかわからなくなる、などの問題があるので、この関数はもう少し柔軟性を持って良いかと感じました。

https://github.com/celo-org/celo-monorepo/blob/958757d839148e4e792dab93107f3bebb28576d5/packages/protocol/contracts/stability/SortedOracles.sol#L124-L145

感想

  • Chainlinkとの違いがあって面白かったです
    • Celoはガス代が安いからこそ各reporterがオンチェーンでoracleを提出しているけど、chainlinkはオフチェーンでまとめてoracleを提出している
    • Celoは各reporterが高頻度でreportを提出しているので価格の反映が早いけど、chainlinkはまとめて提出しているので一定時間経たないと更新されない
  • report()を実行する際に、前と後の値をリンクさせる必要があるので、oracle同士の衝突は起こりそうと感じました、実際に失敗しているtxはいくつかありました

参考

Discussion