DeFi 系列 3. Robbin Finance 代码分析

  • Decipher
  • 发布于 2022-08-28 23:52
  • 阅读 18

本文深入分析了Ribbon Finance的结构和代码,重点讨论了其核心组件如Theta Vault、Strike Selection、Opyn和Gnosis Auction,以及用户在使用过程中如何进行资产存储、选项发行及交易等一系列流程。文章从代码层面解析了每个步骤的实现原理,并概述了该协议的运作机制,具有很高的技术深度。

《DeFi 系列:加密期权市场现状及分析》系列的第一篇,主要对加密期权市场进行总体分析。代表性期权协议的逻辑分析在第二篇(链接),对Ribbon Finance 的代码分析在第三篇(链接),对期权协议的局限性及改进点在第四篇(链接 中进行了描述。

[目录]

  1. Ribbon Finance组件
  2. 用户故事与操作分析
  3. 总结

1. Ribbon Finance组件

Ribbon Finance主要由以下组件(Component)组成。

  • ThetaVault
  • StrikeSelection(行权价选择)
  • Opyn
  • Gnosis Auction(拍卖)

我们将简要审视每个组件的角色,并在此基础上理解Ribbon Finance如何运作。

1.1. ThetaVault

ThetaVault 是Ribbon Finance的核心功能所在,负责执行自动期权投资策略。在DeFi中的期权投资基于用户存入的资产,执行特定的期权策略。ThetaVault提供一种自动化方式的期权投资策略,使用户即使不持续执行交易(不重复消耗Gas费)也能处理数千名用户的交易。通过这种自动化方式,用户可以将资产设置在ThetaVault中,即使忘记也能获得利息。

1.2. StrikeSelection

StrikeSelection顾名思义是确定期权的行权价。正如之前文章所介绍的,Ribbon FinanceV1采用通过管理者确定行权价的方式,但在V2中已更新为去中心化的StrikeSelection方式。在原V1中,ThetaVault通过Ribbon Finance管理者的讨论决定行权价格。这种方式的限制在于每次决定行权价时管理者都需介入,导致去中心化和扩展性受到限制。V2的StrikeSelection移除了管理者的干预,从而确保了去中心化和扩展性。

1.3. Opyn

Ribbon Finance利用OpynV2的Gamma Protocol(伽马协议)发行期权,并将其转换为所有人都可交易的ERC20类型资产。Opyn是一个去中心化的平台,任何人都可以创建和交易期权的合约。

1.4. Gnosis Auction

Gnosis Auction是一个提供可以进行拍卖的平台的协议。Ribbon Finance通过Opyn协议创建的ERC20形式期权,通过Gnosis Auction进行交易。

2. 按用户故事与操作分析

在审视了Ribbon Finance的组件后,接下来我们将基于实际用户使用Ribbon Finance的用户故事,分析其是如何通过代码组成的。

2.1. 合约角色

Ribbon Finance主要分为Ribbon Finance(期权)和硬分叉的Ribbon DAO,本文不涉及治理领域,只讨论与期权相关的业务逻辑。

Vault.sol

  • 包含所有Vault类型公用的数据结构的合约

VaultLifecycle.sol

  • 处理每周的金库逻辑的合约(与时间相关)

RibbonVault.sol

  • 包含RibbonThetaVault和RibbonDeltaVault之间共享的逻辑的合约

RibbonThetaVault.sol

  • 利用Opyn生成短期期权头寸的合约

StrikeSelection.sol

  • 根据传入参数计算合适行权价格的合约

2.2. 用户故事

接下来,我们将基于以下用户故事,确认代码中实际是如何运作的。

  1. 用户将100ETH存入ThetaVault(ThetaVault)。
  2. 在星期五UTC时间上午10点,基于ThetaVault的资产铸造oToken(表示期权的Opyn协议基于ERC20的资产)。此时,用户存入的100ETH将被Opyn协议锁定一周。
  3. ThetaVault在获得100个oToken后将其投入拍卖。
  • 任何人都可以参与拍卖并对oToken进行出价。→ 期权的溢价以ETH支付。
  • 当拍卖结束时,ThetaVault将根据溢价收取1ETH。
  • 未售出的期权会被销毁,提取Opyn协议中的一个oToken可获得1单位的抵押资产。

2.3. 合约状态(Vault.sol)

首先,了解全局通用的合约状态(state)中必不可少的状态信息。

以下是创建一个金库所需的VaultParams 和管理已创建金库状态的 VaultState 结构体。

2.3.1. VaultParams

VaultParams 管理以下信息。

  • isPut:销售的期权类型(如果为true,则为看跌期权;如果为false,则为看涨期权)
  • decimals:金库的十进制单位
  • asset:金库使用的资产种类
  • underlying:金库销售的实际资产(例如,如果为ETH看涨期权,则ETH为underlyingAsset)
  • minimumSupply:金库必须管理的最低供应量
  • cap:金库上限

2.3.2. VaultState

  • round:表示周数的信息
  • lockedAmount:为销售期权锁定的数量
  • lastLockedAmount:上一个周期锁定的数量→用于计算绩效费用
  • totalPending:目前存在的资产数量
  • queuedWithdrawShares:上一个周期为提取而排队的数量

2.3.3. DepositReceipt

  • round:表示周数的信息
  • amount:存入数量的信息
  • unredeemedShares:存入后未提取的股份

2.3.4. Withdrawal

  • round:表示周数的信息
  • shares:想要提取的股份

2.4. 存入

接下来,我们将了解通过Ribbon Finance交易期权的首要步骤——存入过程。

2.4.1. deposit depositETH

通过上述函数存入资产,Ribbon Finance为用户的便利性,接收ETH并将其内部转换为包装(Wrapping)后的WETH,处理时将ETH与ERC20资产区分,提供depositETHdeposit 两个函数进行存入。

两个函数除了使用msg.value 和经过WETH转换之外,内部都会调用_depositFor 函数,处理过程相同。

2.4.2. _depositFor

此函数的任务是为存入用户发行金库股份(Vault Share)。金库股份由2.3.3中介绍的 DepositReceipt 组成。

它接受数量和将成为金库股份持有者的账户(表示为creditor)作为参数。首先通过全局金库的状态管理器VaultState 获取当前执行该函数的轮次信息。如果当前轮次的最大值设定上限(cap)加上进入的数量(totalWithDepositedAmount)大于的话,会导致函数执行失败。同时,如果总数量未达到最低供给量(minimumSupply),也会执行失败。记录该用户的股份数之前,会发出Deposit 事件。

在发出事件后,通过将数量(depositAmount)与未赎回股份(unredeemedShare)进行计算,记录之前提到的 DepositReceipt 。根据本轮次以及股份价格、十进制,调用getSharesFromReceipt 函数计算未赎回股份。接下来处理用户在本轮中的存入以便进行加权计算,并最终计算出depositAmount。

最后通过计算得出的值构建depositReceipt 。在最后,将此次存入的增加数量记录至金库的全局状态VaultState中的 totalPending ,以管理当轮期间已存入资产的待处理状态。待处理状态的资产最终将由Ribbon Finance的管理员(Manager)通过执行rollToNextOption函数创建新的头寸。

2.5. 行权价确定及期权发行

在存入过程结束后,合约上的资产处于待处理状态,接下来需要运用这些资产发行期权。为了发行期权,需要行权价格(StrikePrice),而解决此过程的是Ribbon Finance的算法StrikeSelection(StrikeSelection)。通过StrikeSelection计算出行权价格后,利用Opyn协议发行oToken,实现期权发行的过程。处理这一整个期权发行过程的函数是commitAndClose

ThetaVault的 commitAndClose 函数如上所示,将数据处理成VaultLifecycle中的 CloseParams 格式,然后调用VaultLifecyclecommitAndClose

接下来我们审视VaultLifecycle中的 commitAndClose

commitAndClose 函数的功能如前所述,行权价格的确定和期权的发行。首先处理发行期权所需的信息,如到期日、行权价格、看跌期权或看涨期权、资产种类、宝塔使用的资产种类等。在计算行权价格时,利用 StrikeSelection 进行计算。除了德尔塔(delta)之外,行权价格是通过合约的自动化算法计算得出的。

通过以上处理的数据发行Opyn协议的 oToken。发行的过程通过内部实现的getOrDeployOtoken完成,该函数如果内部已有分发的 oToken,将避免重新发行而返回已发行的代币。

通过commitAndClose流程,基于存入过程中用户存入的待处理状态资产,期权发行完成后,Ribbon Finance的经理将会调用rollToNextOption推进下一轮。在这个过程中,金库的股份也会被铸造出来。只有当rollToNextOption的流程进行后,用户才能确认金库的股份。

2.6. 期权交易

通过上述过程发行的期权将使用Gnosis Auction交易。这个过程是一般用户也可以通过Gnosis Auction网站完成的,而Ribbon Finance则实现了自动化函数。

首先通过getAuctionSettlementPrice函数获取注册的拍卖价格。如果拍卖尚未注册,内部将返回0,从而不进行期权销售。接下来执行sellToBuyers函数,此函数将向多个排队的购买者销售期权。但此函数由于实际进行期权销售,不能由任何人执行,只有金库才能进行销售。因此,金库才能以适当价格销售期权,收取溢价,从而获得高收益。那么,上述 sellOptionsToQueue 函数为何会被实现为任何人均可调用的形式,实则在实际使用的ThetaVault中,实施如下,而是通过库的方式实现,只有Ribbon Finance的管理员(在代码中表示为Keeper)才能通过该函数销售期权。

2.6. 提取,期权到期后的处理

在Ribbon Finance中,提取过程大致分为标准提取(Standard Withdraw)和即时提取(Instant Withdraw)。

标准提取过程是如字面意思在一轮结束后,周五UTC时间上午10点之后可以进行的过程。而即时提取过程是在存入资产中途进行的,即在周中(mid-week)进行提取。

2.6.1. 标准提取过程(Standard Withdraw)

标准提取过程按照如下顺序进行。

  1. 通过 initiateWithdraw 函数将需要提取的 Withdrawal 对象放入队列中。
  2. 此时函数 initiateWithdraw 可以在轮次进行中被调用。
  3. 如果此函数调用次数等于队列长度,每次将堆叠到一个队列中,在稍后调用 completeWithdraw 时逐步生效。
  4. 之后通过 completeWithdraw 函数成功提取资产,销毁股份。
  5. 此时 completeWithdraw 函数只能在星期五上午10点UTC调用。

我们先来查看initiateWithdraw函数。

ThetaVault的 initiateWithdraw 函数内部调用 _initiateWithdraw 函数,并将想提取的股份添加到队列中。

接着经过检查提取的资产为0或不为0的步骤,获取当前轮次的轮次信息。之后处理真正用于提取的 Withdrawal 对象。数据未填入之前,通过 InitiateWithdraw 事件发出提取调用者、欲提取股份、当前轮次的信息。

接下来如果已经存在欲被提取的股份,并且是同一轮次,则应加以计算;如果不是同一轮次,则进行欲提取股份是否存在的检查,同时更新轮次信息。最终更新欲提取的股份,并将其发送给用户。

在星期五UTC时间上午10点后,一轮结束,用户将能够调用以下 completeWithdraw 函数。

此函数内部调用 _completeWithdraw 函数,并更新之前一轮的提取数量。

_completeWithdraw 函数中再次获取上一个initiateWithdraw中使用的对象。需要更新股份和轮次,因此将其存入局部变量,并检查几个条件。首先检查欲提取股份是否大于0,以确认以正确经过initiateWithdraw流程。然后判断当前时点是否经过轮次,只有结束轮次的Withdrawal才能被执行。

在检查完所有条件后,初始化用户的 Withdrawal 对象,并减少全局变量相应的数值。

接着根据欲提取股份、每股价格、十进制计算真正的提取资产数量。一旦计算完成,提取的股份(ERC20 类型)将被销毁,最后将可提取的资产数量发送给用户,函数至此结束。

2.6.2. 即时提取过程(Instant Withdraw)

即时提取过程与标准提取过程相较,允许的时间是在轮次未结束的周中进行提取。例如在周三存入A资产的用户仅可在周三至周五的时段内进行即时提取。

即时提取过程通过上述 withdrawInstantly 函数进行,程序相较于标准提取过程更为简洁。

从存入时创建的 depositReceipt 中获取数据,检查调用时点的轮次是否与存款时一致。这使得仅可在周中执行。之后进行检查防止提取的资产大于存入资产。基本检查结束后,减少存入资产中的提取资产的数量,并在全局变量中同期减少被管理的待处理状态数量。最终将提取者、提取数量及轮次的信息通过 InstantWithdraw 事件一并发出,将可提取数量发送给用户,函数结束。

总结

本文分析了构成Ribbon Finance的要素——ThetaVault、StrikeSelection、Opyn、Gnosis Auction,并从代码层面审视了存入资产、发行期权、交易期权以及提取期权的过程。在下一篇文章中,将通过分析此类期权DeFi协议的局限性与改进点,分享对期权DeFi协议未来发展方向的思考。

参考文献

  • 原文链接: medium.com/decipher-medi...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Decipher
Decipher
https://medium.com/decipher-media