dYdX
dYdXとは
分散デリバティブ市場のための規格。
マージン取引(空売り・レバレッジ買い)
オプション取引(アセットホルダーが特定の価格あるいは特定の日時においてそのアセットを売買できる権利を売る)
などができるDEX。ここではマージン取引のみ解説。
現時点では、マージン取引のみ実装されている。
Dharma的な0x型のハイブリッドDEX(オフチェーンオーダーブック・オンチェーンセトルメント)で、オフチェーンでローンオファーのマッチングし、まるっと0xでマージン取引ができるようになること。
空売り:売りポジションを得るためにトークンを借りる必要
コインの価格値下がりに対して利益を得ることができる
レバレッジロング(買い):より多くの買いポジションを得るためにトークンを借りる必要
通常ロングよりも価格値上がりに対して大きな利益を得ることができる
このようなマージン取引をする場合は、トレーダーがポジションを開くためのトークンを借りてくる必要がある。”本来持っていないはずの"借りてきたトークンをトレードに出すことで空売りやレバレッジロングが可能になる。
なので、マージン取引のためのloanオーダーはDharmaのようなオフチェーンでマッチングし、そのオーダーを用いて0xで信用取引を行う。
概要としては、トークンを貸したい人(利子を得られる)がオーダーを出し、マージン取引をしたいトレーダーがそのオーダーをピックアップし、0xで信用取引を行う。
おそらくUI的には、例えばrader relayがdYdXのプロトコルを採用して、rader上のトレード画面でマージン取引が行え、その他、別途dYdX専用の利子を得るためのloanオファーフォームがあるようなイメージ。
登場するトークンの定義
owed token:Lenderがトレーダーに対して貸し付けるトークン
held token:トレーダーがowed tokenとトレードするペアのトークン
例えば、空売りであればheld tokenに対してowed tokenの価格が下がることで投資家は利益をあげられる。レバレッジロングであればheld tokenに対してowed tokenの価格が上がることで投資家は(ノンレバレッジなロングよりも)多くの利益を得ることができる。
当然、vice versaで投資家は損失を被ることもある。(厳密には、利子分以上の利益を得られないと損失になる)owed tokenの貸し手(Lender)は利子を得ることができる。
ICOはしていない。6月にテストネットデプロイ、オープンソース。
仕組みの概要(マージン取引)
マージントークン
オープニングポジション
クロージングポジション
マージンコール(追証)
マージントークンの生成からクロージングの全体的な流れ。
dYdXのリレイヤーにLenderはloan offerを提示しておく。トレーダーはそのオファーをテイクし、Marginコントラクトにトランザクションを送信することで、0xのExchangeWrapperコントラクトも呼び出され、マージン取引のポジションが開く。
オープニングポジションに伴い、マージントークン(short or long)も生成されトレーダーが保持することになる。
トレーダーはマージントークンをBurnし、いつでもポジションを閉じることが可能。Lenderもマージンコールをしてポジションをクローズすることが可能。
さらに、トレーダーはダッチオークションによるクロージングも任意で可能。
https://gyazo.com/b9942e6afa9d609a218ac53b02b090f8
マージントークン
Marginトークンとはポジションをオープニングすることで生成するdYdXにおけるトークン。
それぞれのマージントークンはショート、レバレッジロングポジションに基づくERC20規格であり二次流通可能。つまり、以下のことも可能。
マージントークンの所有者をユーザーだけでなくスマートコントラクトにする
マージントークン建てのマージンポジション
エンドユーザー的にはERC20のマージントークンを売買するだけでショートしたりロングしたりできるのでシンプル。
それぞれのマージントークンコントラクトには以下のような状態を保持
所有者
利子率
期限切れtimestamp
ポジションにおいて1owed tokenに対するロックされているheld token量
positionId
マージントークンはショート、レバレッジロングのそれぞれの価格決定式に基づいて価格算出されトレードされる。その変数としては、利子率、コラテラル量、建てられているクリプトアセット。
I = 利子率
Q = C/P = (ポジションにおけるheld tokenのコラテラル量) / (Lenderに貸し出されているowed tokenの量)
Pb = Po = owed tokenの価格
https://gyazo.com/75b291538ff7e9d78edcd5742cd6fbee
Pb = Ph = held tokenの価格
https://gyazo.com/934ef960ba0b5253ef9b35b8bad5203b
直感的には、ショートトークンは原資産の価格Pbにネガティブに相関し、レバレッジトークンはより高い比率でポジティブに相関する。
ポジションのオープニング
LenderがオフチェーンでLoan Offeringを提示し、それをトレーダーがテイクするトランザクションをMarginコントラクトに送信することでマージン取引におけるポジションがオープンする。
トレーダーがLoan Offeringをピックアップし、dYdX Marginコントラクトにトランザクションを送信することでMarginポジションが開く。ローン保障のためにHeld Tokenあるいはowed tokenをコラテラルに。(owed tokenをコラテラルにすることもできるけど、結局コラテラル分もheld tokenにトレードされるので意味ない?)
ポジションオープニングの具体的なプロセス
トレーダーがMarginコントラクトのopenPositon()関数を実行するトランザクションを送信する。
ローンオファーのメッセージのインプットデータや署名が検証される
MarginコントラクトがProxyコントラクトを呼び出し、オファーされた量のデポジットをtraderからコントラクトに送金。held tokenであればそのまま保持するのでVaultコントラクトへ。owed tokenであればトレードするのでExchangeWrapperコントラクトへ。
MarginコントラクトがExchangeWrapperコントラクトを呼び出し、リクエストされた量のowed tokenをLenderからExchangeWrapperに送金。
Marginコントラクトはそのローンオファーのうちどのくらいの金額が使われたかを記録する。(例えば、5ETH loan offerされたうちの3ETHはすでにfillされている的な)
MarginコントラクトはExchangeWrapperを呼び出し、owedトークンからheldトークンへのトレードを実行する。この取引において、ExchageWapperコントラクトはTakerであり、owedトークンの買い手がMaker。
MarginコントラクトがProxyコントラクトを呼び出し、トレードしたheld tokenをExchangeWrapperからVaultコントラクトへ送金。このheld tokenはポジションが続いている限りずっとロックされる。
ポジションに関するユニークな識別子などの詳細な情報はコントラクトに保持され、クロージングなどに使用される。
オフチェーンのリレイヤーで管理されるloan offeringのデータ
https://gyazo.com/83e63748420458d08a548744cbb35913
ポジションのクロージング
トレーダーは「Lenderから借りたOwed token量+利子」以上を返金することでポジションをクローズすることができる。これによりMarginトークンはBurnされる。
具体的なポジションクロージングのプロセスは以下の通り。
トレーダーはMarginコントラクトのclosePositon()関数を実行するトランザクションを送信する
この時点でLenderが貸しているOwed Tokenの利子を加味した総量が計算される
MarginコントラクトがExchangeWrapperコントラクトを呼び出し、held tokenからowed tokenへのトレードが行われる。トレード後、Vaultコントラクトが①貸し出しているowed token+利子と②そのポジションのデポジット量+取引利益(損失)が保持される。(②はowed tokenでもheld tokenでもOK。owed token払いの場合は全てのheld tokenがトレードされ、held token払いの場合は①の分だけheld tokenはトレードされ、残りのheld tokenは②に充てられる。)
MarginコントラクトはProxyコントラクトを呼び出し、①貸し出した量のowed token+利子をExchangeWrapperコントラクトからLenderに送金する。
Marginコントラクトはトレーダーに対して②デポジット+取引利益(held token or owed token)を送金する。
Marginコントラクトはそのポジションのvalueがゼロになったことを確認し、ストレージからポジションを取り除く。
マージンコール(追証)
Lenderかauthorizedサードパーティ(owner)がMarginコントラクトのmarginCall関数を実行することで、マージンコールをすることができる。実行後、返金までにloan offeringで指定したcallTimeLimitの猶予が与えられ。時間内にowed tokenを返金する必要がある。あるいは、held Tokenのデポジットを追加してもOK。
もし時間内にトレーダーがクローズあるいはデポジットの追加ができなかったら、ポジションにデポジットされているheld tokenを全て得ることができる。(セカンドレイヤーコントラクトでは、マージンコールは一定条件下のみに制限されることが理想?(例えばオラクルによる価格参照など))
held tokenに対してowed tokenの価格が上がると、そのポジションにロックされているheld tokenでowed tokenを買い戻せなくなりそうな閾値で、Lenderはマージンコールをしないと損失を被る(貸し出しているowed tokenをコラテラル分で保証できなくなる)ので、価格を常にウォッチしておく必要がある。
サードパーティはリレイヤーあるいはウォッチサービス提供主体が想定される。つまり、サードパーティは中央集権的なオラクル。
そして、トレーダーも同様に常にオンラインでcall time limit内に、クロージングトランザクションを送信できないと、価格変動に伴いデポジットが没収されるリスクがある。
そのため、ダッチオークションによる自動的なクロージングコントラクトを提供することで常にオンラインであることを防ぐことも可能。(ダッチオークションクロージングを使うかどうかはトレーダーの任意)(TODO:どういうプロセスでダッチオークション自体を開始するのか?)
ポジションにロックしているheld tokenをcall time limit間でダッチオークションで売り出す。ポジションのowed token量で買えるheld token量を徐々に増やしていく。
リスク
トレーダーにとってのリスクは、十分なデポジットがあるにもかかわらずLenderにコールされてしまうこと。解決策としては、dYdXのベースプロトコルとは別に、Lenderに対するレピュテーションシステムを導入し、良い評価のLenderのオファーがトレーダーに選ばれるようにする。
あるいは、信頼できるサードパーティ(リレイヤーオラクル?)が手動でマージンコールをするようにする。
将来的には、価格の分散オラクルも。
Lenderにとってのリスクは、(owed tokenのロックによる価格変動リスクを除くと)held tokenに対するowed Tokenの価格が急激に上がりすぎて、マージンコールが完了する前に(call time limitの猶予がある)デポジットされているheld tokenではowed tokenを十分にバイバックできなくなってしまうこと。
このようなリスクに対処するためにLenderは、十分高いデポジット、十分短いcall time limitをloan offerで設定する必要性がある。
コントラクトアーキテクチャ
プロトコルの分散ガバナンスを考慮した2ndレイヤーコントラクト構造。無駄にガバナンストークンなどを加えていない。
Baseコントラクトはプロトコルのメインとなる部分で、オーナー権限でのupgradabilityのみ。
セカンドレイヤーコントラクトは、自由にupgrade、拡張することができる。
Baseプロトコル
Margin.sol:エントリーポイント。ポジションの開閉、マージンコールなどのロジック
Proxy.sol:ユーザーの代わりにERC20トークンの送金を行う。(allowance必要)
Vault.sol:全てのトークンをエスクローするためのコントラクト
https://gyazo.com/da2a0a9198717ec8d691800b7c2a07a0
Secondレイヤー
ZeroExEchangeWrapper.sol:ユーザーがトークンを保持せずに0xのオーダーを使ってポジションの開閉をするためのラッパーコントラクト
ERC20Short.sol:複数のパーティがひとつのショートポジションの所有権を比例的に保持できる
ERC20Long.sol:ERC20Shortと同じだがHeldTokenベース。
ERC721Position.sol:ポジションをNFTとして定義し他のユーザーにtranferできる
ERC721MarginLoan.sol:LenderのローンをNFTとして定義し他のユーザーにtransferできる
DutchAuctionCloser.sol:ダッチオークションクロージング
SharedLoan.sol:複数人でローンをシェアするためのコントラクト
エントリーポイントとなるMarginコントラクトの関数から特定のコントラクトの関数がcallされる。もし、Marginコントラクトに全てのロジックを定義するとdeployトランザクションがblock gas limitを超えてしまう。
code:OpenPositionImpl.sol
library OpenPositionImpl {
function openPositionImpl(
MarginState.State storage state,
bool depositInHeldToken,
bytes signature,
bytes orderData
)
public
returns (bytes32)
{
// 欲しいtxデータ構造にparse
BorrowShared.Tx memory transaction = parseOpenTx(
addresses,
values256,
values32,
depositInHeldToken,
signature
);
// このtxのpositionIdがすでに存在していないかチェック
require(
!MarginCommon.positionHasExisted(state, transaction.positionId),
"OpenPositionImpl#openPositionImpl: positionId already exists"
);
// owedTokenを借りて、DEXで売り、heldTokenを
doBorrowAndSell(state, transaction, orderData);
//
recordPositionOpened(
transaction
);
doStoreNewPosition(
state,
transaction
);
return transaction.positionId;
}
function doBorrowAndSell(
MarginState.State storage state,
BorrowShared.Tx memory transaction,
bytes orderData
)
private
{
// loan offering要素に関する検証。
// アドレスが0じゃない、十分なトークン量、expireしていないかとか
BorrowShared.validateTxPreSell(state, transaction);
// トレーダーデポジットがHeldTokenかOwedTokenかに応じてvaultコントラクトに送金
if (transaction.depositInHeldToken) {
BorrowShared.doDepositHeldToken(state, transaction);
} else {
BorrowShared.doDepositOwedToken(state, transaction);
}
transaction.heldTokenFromSell = BorrowShared.doSell(
state,
transaction,
orderData,
MathHelpers.maxUint256()
);
BorrowShared.doPostSell(state, transaction);
}
}