本文深入分析了Ribbon Finance的结构和代码,重点讨论了其核心组件如Theta Vault、Strike Selection、Opyn和Gnosis Auction,以及用户在使用过程中如何进行资产存储、选项发行及交易等一系列流程。文章从代码层面解析了每个步骤的实现原理,并概述了该协议的运作机制,具有很高的技术深度。
《DeFi 系列:加密期权市场现状及分析》系列的第一篇,主要对加密期权市场进行总体分析。代表性期权协议的逻辑分析在第二篇(链接),对Ribbon Finance 的代码分析在第三篇(链接),对期权协议的局限性及改进点在第四篇(链接 中进行了描述。
[目录]
Ribbon Finance主要由以下组件(Component)组成。
我们将简要审视每个组件的角色,并在此基础上理解Ribbon Finance如何运作。
ThetaVault 是Ribbon Finance的核心功能所在,负责执行自动期权投资策略。在DeFi中的期权投资基于用户存入的资产,执行特定的期权策略。ThetaVault提供一种自动化方式的期权投资策略,使用户即使不持续执行交易(不重复消耗Gas费)也能处理数千名用户的交易。通过这种自动化方式,用户可以将资产设置在ThetaVault中,即使忘记也能获得利息。
StrikeSelection顾名思义是确定期权的行权价。正如之前文章所介绍的,Ribbon FinanceV1采用通过管理者确定行权价的方式,但在V2中已更新为去中心化的StrikeSelection方式。在原V1中,ThetaVault通过Ribbon Finance管理者的讨论决定行权价格。这种方式的限制在于每次决定行权价时管理者都需介入,导致去中心化和扩展性受到限制。V2的StrikeSelection移除了管理者的干预,从而确保了去中心化和扩展性。
Ribbon Finance利用OpynV2的Gamma Protocol(伽马协议)发行期权,并将其转换为所有人都可交易的ERC20类型资产。Opyn是一个去中心化的平台,任何人都可以创建和交易期权的合约。
Gnosis Auction是一个提供可以进行拍卖的平台的协议。Ribbon Finance通过Opyn协议创建的ERC20形式期权,通过Gnosis Auction进行交易。
在审视了Ribbon Finance的组件后,接下来我们将基于实际用户使用Ribbon Finance的用户故事,分析其是如何通过代码组成的。
Ribbon Finance主要分为Ribbon Finance(期权)和硬分叉的Ribbon DAO,本文不涉及治理领域,只讨论与期权相关的业务逻辑。
Vault.sol
VaultLifecycle.sol
RibbonVault.sol
RibbonThetaVault.sol
StrikeSelection.sol
接下来,我们将基于以下用户故事,确认代码中实际是如何运作的。
首先,了解全局通用的合约状态(state)中必不可少的状态信息。
以下是创建一个金库所需的VaultParams
和管理已创建金库状态的 VaultState
结构体。
VaultParams
管理以下信息。
接下来,我们将了解通过Ribbon Finance交易期权的首要步骤——存入过程。
2.4.1. deposit
和 depositETH
通过上述函数存入资产,Ribbon Finance为用户的便利性,接收ETH并将其内部转换为包装(Wrapping)后的WETH,处理时将ETH与ERC20资产区分,提供depositETH
和 deposit
两个函数进行存入。
两个函数除了使用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
函数创建新的头寸。
在存入过程结束后,合约上的资产处于待处理状态,接下来需要运用这些资产发行期权。为了发行期权,需要行权价格(StrikePrice),而解决此过程的是Ribbon Finance的算法StrikeSelection(StrikeSelection)。通过StrikeSelection计算出行权价格后,利用Opyn协议发行oToken
,实现期权发行的过程。处理这一整个期权发行过程的函数是commitAndClose
。
ThetaVault的 commitAndClose
函数如上所示,将数据处理成VaultLifecycle
中的 CloseParams
格式,然后调用VaultLifecycle
的 commitAndClose
。
接下来我们审视VaultLifecycle
中的 commitAndClose
。
commitAndClose
函数的功能如前所述,行权价格的确定和期权的发行。首先处理发行期权所需的信息,如到期日、行权价格、看跌期权或看涨期权、资产种类、宝塔使用的资产种类等。在计算行权价格时,利用 StrikeSelection
进行计算。除了德尔塔(delta)之外,行权价格是通过合约的自动化算法计算得出的。
通过以上处理的数据发行Opyn协议的 oToken
。发行的过程通过内部实现的getOrDeployOtoken
完成,该函数如果内部已有分发的 oToken
,将避免重新发行而返回已发行的代币。
通过commitAndClose
流程,基于存入过程中用户存入的待处理状态资产,期权发行完成后,Ribbon Finance的经理将会调用rollToNextOption
推进下一轮。在这个过程中,金库的股份也会被铸造出来。只有当rollToNextOption
的流程进行后,用户才能确认金库的股份。
通过上述过程发行的期权将使用Gnosis Auction交易。这个过程是一般用户也可以通过Gnosis Auction网站完成的,而Ribbon Finance则实现了自动化函数。
首先通过getAuctionSettlementPrice
函数获取注册的拍卖价格。如果拍卖尚未注册,内部将返回0,从而不进行期权销售。接下来执行sellToBuyers
函数,此函数将向多个排队的购买者销售期权。但此函数由于实际进行期权销售,不能由任何人执行,只有金库才能进行销售。因此,金库才能以适当价格销售期权,收取溢价,从而获得高收益。那么,上述 sellOptionsToQueue
函数为何会被实现为任何人均可调用的形式,实则在实际使用的ThetaVault中,实施如下,而是通过库的方式实现,只有Ribbon Finance的管理员(在代码中表示为Keeper)才能通过该函数销售期权。
在Ribbon Finance中,提取过程大致分为标准提取(Standard Withdraw)和即时提取(Instant Withdraw)。
标准提取过程是如字面意思在一轮结束后,周五UTC时间上午10点之后可以进行的过程。而即时提取过程是在存入资产中途进行的,即在周中(mid-week)进行提取。
2.6.1. 标准提取过程(Standard Withdraw)
标准提取过程按照如下顺序进行。
initiateWithdraw
函数将需要提取的 Withdrawal
对象放入队列中。initiateWithdraw
可以在轮次进行中被调用。completeWithdraw
时逐步生效。completeWithdraw
函数成功提取资产,销毁股份。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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!