前言
关于 DeFi 借贷产品之 Compound,前面我们已经讲了很多。从概述篇开始,讲述了 DeFi 和借贷的一些现状,并介绍了 Compound 的核心概念、利率模型、整体架构等。合约篇则从智能合约出发,深入讲解了核心业务的实现细节,包括利率模型的实现原理和 cToken 合约等。Subgraph篇主要还是对 Subgraph 技术的一种入门级讲解,毕竟很多人都还不懂什么是 Subgraph,有什么作用,如何开发 Subgraph。清算篇就专注于介绍 Compound 的清算机制以及清算服务的设计了,我分别提供了三个版本的清算服务设计以供参考。
现在,进入本系列的最后一篇,延伸篇,主要给大家讲讲一些安全建议、优化思路,以及基于 Compound 的扩展产品思路等。
操纵预言机攻击
操控预言机所依赖的信息源进行短时间的价格操纵以达成误导链上价格是典型的预言机攻击,其本质是对预言机进行操控,造成内外价格差并利用闪电贷等新型金融工具从中套利。
Compound 曾在 2020 年 11 月 26 日遭受过价格预言机操纵攻击,导致价值高达八千多万美元的加密资产被系统强制清算。
因为 Compound 使用的是自己设计研发的预言机系统 Open Price Feed,且使用的价格数据只依赖 Coinbase 这个中心化的交易所来提供。而当日下午,Coinbase 交易所稳定币 DAI 价格出现剧烈波动,一度暴涨超 30% 至 1.34 美元,后又快速回落。这一剧烈波动,就让很多借贷了 DAI 的用户资产触发了清算。
事实上,除了 Compound,Harvest Finance、Value DeFi、Cheese Bank、Origin Protocol 等都先后遭到类似预言机的攻击,MakerDAO 和 Aave 历史上也都因此发生过大规模清算。
针对此类攻击,安全建议如下:
- 接入 Chainlink 等完善的第三方价格预言机对所有代币价格提供实时价格数据
- 在智能合约内部建立细致的价格监控程序,对任何可能产生巨大波动的交易进行管制或者阻止
目前第三方价格预言机主要有:Chainlink、NEST、Band、DOS、Tellor、MakerDao 等。其中,龙头当属 Chainlink 了。Chainlink 使用链上聚合模式,节点将数据提交到链上合约,在合约内进行数据聚合,获得最终数据。
另外,随着 DEX 的发展,直接使用 DEX 的链上价格做为预言机也是一个很有潜力的方向,这需要 DEX 具有足够的深度,能够抵抗价格操纵,或者对价格进行加权等操作,来规避价格操纵攻击。这块目前应用最广泛的就是 Uniswap TWAP(Time-Weighted Average Price),即时间加权平均价格,可通过查看官方文档了解其机制:
话说,前两天发现 Compound 的预言机系统 Open Price Feed,数据提供者已经从 Coinbase 换为了 Chainlink。另外,也结合了 Uniswap TWAP(time-weighted average price),用来校验 Chainlink 的喂价是否在 Uniswap TWAP 的可接受边界内。这样,将大幅提高其预言机的安全性。
如果还需要再进一步提高安全性,可以选择多个预言机进行加权平均计算,推荐 3 家第三方预言机 + 3 家 DEX 组合,比如选择 Chainlink、NEST、Band、Uniswap、SushiSwap、Bancor,每家可设置不同的权重值,计算时,获取到六家价格后,去掉最高价和最低价,剩下的再进行加权平均计算作为最后的实际价格。另外,在同个区块内,也无需每次交易都计算出实时价格,只要计算出一个合理价格后,整个区块内的交易都可直接使用该价格,这样,利用闪电贷操纵价格的手段也造不成威胁了。
重入攻击
由于智能合约可以调用外部合约或者发送以太币,这些操作需要合约提交外部的调用,所以这些合约外部的调用就可以被攻击者利用造成攻击劫持,使得被攻击合约在任意位置重新执行,绕过原代码中的限制条件,从而发生重入攻击。
简单的来说,发生重入攻击漏洞的条件有 2 个:
- 调用了外部的合约且该合约是不安全的
- 外部合约的函数调用早于状态变量的修改
举个简单的案例,合约如下:
构造如下攻击合约:
攻击流程如下:
针对这种场景,一般建议使用 “检查-生效-交互”(Checks-Effects-Interactions) 模式,即将“生效”部分的代码放在“交互”(转账)前面,这样重入调用时“检查”结果就防止了其继续执行。
更好的方案则是建议使用重入锁,其实就是定义一个防止重入的修饰器,代码如下:
modifier nonReentrant() { require(_notEntered, "re-entered"); _notEntered = false; _; _notEntered = true; }
将该修饰器添加到需要防止重入的函数即可。Compound 就是使用重入锁来防止重入攻击的,仔细查看 Compound 的 CToken 合约就会发现好多函数都添加了 nonReentrant 的修饰。
部署合约化
Compound 中每增加一个借贷市场(即资金池)的时候,都需要部署几个合约:
- JumpRateModelV2:利率模型合约
- CErc20Delegate:cToken逻辑合约
- CErc20Delegator:cToken代理合约
部署这几个合约时,有些参数其实是通用的,而且将这几个合约关联起来的逻辑也是一样的,既然如此,那干脆就将部署都整合到一起,可以添加一个工厂合约来做这些工作。以下是一个简单版本的实现:
这样子,再部署一个新的 cToken 合约时就方便很多了,而且也会大幅降低出错的概率。
部署完之后,通常需要对合约再检查各种参数,确保没有问题后,接着就可以调用 Comptroller 合约(其实是通过 Unitroller 合约)的 _supportMarket(cToken) 函数将其添加到市场列表了。
产品延伸
在概述篇有说到,其实目前的 DeFi 借贷市场,最主要的需求是为了满足交易活动。比如,我手上持有 ETH,而且我看好 ETH 在接下来的时间内还会上涨,那我可以抵押我的 ETH,借出 USDT,用来买更多 ETH,而且买回来的 ETH 还可以再抵押进去,再借出一些 USDT,再买更多 ETH。本质上,这种需求其实就是为了增加杠杆,也因此,开始有不少 DeFi 产品直接提供杠杆交易、杠杆挖矿等服务,如 dYdX、Lever、 Alpha、Alpaca 等。
dYdX 主打就是杠杆交易,具体业务上提供了借贷、现货交易、保证金交易、合约交易等。不过,采用的交易模式是链下撮合链上结算的订单簿模式(Orderbook),而非现在主流的自动做市商模式(AMM)。
Lever 则是基于 AMM 模式的杠杆交易平台,今年 4 月刚完成 60 万美元种子轮融资,5 月份完成了 IDO 和 ITO,是一个刚崛起的新星。目前其合约代码也已经发布了 v2 版本,相比 v1 版本 还不到两个月,可见团队还是非常高效的。
Alpha 的主要产品则是 Alpha Homora,是首款杠杆流动性挖矿产品。所谓杠杆挖矿,其实就是借贷+杠杆+流动性挖矿的结合体。简单说,是用本金作为抵押,借币放大本金,再去流动性挖矿,让小资金也能获得理想的挖矿收益。
Alpaca(羊驼)其实就是 Alpha Homora 的仿盘,基于 BSC 的。
其实,以上不管哪种产品,要实现杠杆,都需要基于借贷。上面这四款产品,我还没去研究它们的具体业务逻辑和具体实现,但是,对于产品层面上如何设计杠杆交易和杠杆挖矿,我可以聊聊自己的一些想法。
杠杆交易
目前,借贷市场的强需求之一就是为了杠杆交易。但是,将 A 资产抵押到 Compound 再借出 B 资产,之后再到 DEX 平台(如 Uniswap)用 B 资产兑换成 A 资产,又重复投入 Compound,如此反复操作,对用户来说,是非常繁琐的,而且中间多次操作会消耗较多手续费。因此,直接提供杠杆交易服务的产品很受欢迎。
从用户需求层面来说,用户想要的就是做多或做空某种资产。比如,选定 ETH/USDT 交易对,开多的时候,就需要借入 USDT 并兑换成 ETH;开空的时候,则借入 ETH 来兑换 USDT。
当然,用户想要开仓,就需要先抵押资产作为保证金,这样才可借入资产。该逻辑其实和 Compound 的借贷逻辑一样,不同点在于,Compound 属于超额抵押借贷,存入价值 100 美刀的资产,最多只可借出 75 美刀的资产;但杠杆交易则可以借出多倍的资产,比如开 3 倍杠杆时,100 美刀的抵押资产,可以借出 300 美刀的资产来开仓。
这时,大部分人会想到的一个问题就是:抵押 100 美刀,借出 300 美刀,那多出的 200 美刀从哪来呢?这个问题的本质其实是:资金池里的资产从哪来?
Compound 的资金池只有一种流入途径,那就是用户的存款。但杠杆交易的资金池,其实有两种流入途径,一是用户的存款,二是开仓后兑换所得的资产。第二种途径很重要,这是维持资金池流动性很关键的一点。 比如,当用户抵押了 100 美刀的 ETH,开仓时借出了 300 美刀的 USDT,兑换回来了 300 美刀的 ETH,这 300 美刀的 ETH 又流回到资金池里去,因此,资金池的总价值其实没有变化,只是同价值的 A 资产变成了 B 资产。如果兑换后的资产不流回资金池,那资金池很容易就会失衡,导致资金池的流动性不足,甚至枯竭。
多空平衡的情况下,资金池的流动性也会保持平衡。但是,如果出现极端情况,比如,多方远超于空方的时候,就要考虑是否会出现某一资金池枯竭的风险。多方抵押了 ETH,借出了 USDT,兑换回 ETH,随着多方的力量越来越强,那么 ETH 的资金池会越来越多,但 USDT 的资金池则越来越少,那 USDT 的资金池是否就会面临枯竭的风险呢?这种问题,其实在 Compound 中也同样存在,而 Compound 的机制中,降低这种风险的关键点在于利率模型,即资金使用率超过拐点后的利率会非常高,借款利率高涨就会使得用户借款的意愿减低,而存款利率高涨就会促进用户多存款,从而可以让资金池的供需关系又回到一个平衡的位置。
对用户来说,杠杆交易的风险,和中心化的一样,就是存在爆仓的风险。抵押资产的价格变化、借款资产的价格变化、兑换资产的价格变化,以及借款利息的不断增长,都有可能导致爆仓。
借出的资产+利息称为债务,兑换回来的资产称为头寸,那对于每个持仓单,盈亏的计算公式如下:
盈亏 = 头寸价值 - 债务价值
盈亏为正时,表示盈利,那就不会有爆仓的风险。但是,盈亏为负时,就表示亏损了,一旦亏损率(亏损/保证金价值)超过某个阈值时(比如 80%),就需要强制平仓了,即是爆仓,也称为清算。为了避免爆仓,用户可以通过还款降低债务,或追加保证金。
如果用户自己想要止盈或止损了,就可以自己手动平仓了,平仓的逻辑也比较简单,将头寸资产再兑换回所借资产,接着还掉债务。如果兑换回来的资产不足以还债的话,那可能需要用部分抵押资产来兑换成所借资产并还债。
杠杆挖矿
自从 2020 年 6月 Compound 开启流动性挖矿(Yield Farming)以来,这一产业就迅速风靡,很多玩家都当起「农民」,通过为各种 DeFi 产品提供流动性来获得收益,收益一般为治理代币或交易费,或两者都有。比如,在 Compound 存款和借款都能挖到其治理代币 COMP;在 Uniswap 投入流动性得到 LP Token 可挖矿手续费;在 Uniswap 质押 LP Token 能挖到 UNI 代币和手续费。LP Token 本质上就类似于 cToken,不同交易对池子有不同的 LP Token。
杠杆挖矿则是将用户本金加杠杆,达到放大本金的效果,从而为用户提高挖矿的效率。目前大部分杠杆挖矿产品主要都是投入到如 Uniswap、SushiSwap、PancakeSwap 等 DEX 平台进行挖矿。
以 Alpha Homora 为例,矿池可分为两大类,一类称为 liquidity providing pools,另一类称为 yield farming pools。 liquidity providing pools 只是投入流动性到 DEX 平台得到 LP Token,从而赚取 DEX 平台的手续费和 Alpha 代币;而 yield farming pools 则会将 LP Token 质押到 DEX 平台,从而,除了赚取 DEX 平台的手续费和 Alpha 代币,还能赚到 DEX 的平台代币。两者的主要区别就在于是否将 LP Token 进行质押。
而用户要参与杠杆挖矿,首先选定要参与的一个池子,比如选定 Uniswap 的 UNI/ETH 池子。之后,用户需要投入本金,本金可以是 UNI、ETH 或这个交易对的 LP Token,可以投入这三种 Token 中的一种或多种。接着,用户就可以选定杠杆倍数和想要借的币种和数量,本金和杠杆倍数决定了最多可以借多少,借款币种可以是 UNI 和 ETH 中的一种或两种,一般只借一种。最后,用户再次确定各种数据,没问题就可以提交开仓了。开仓时,智能合约就会根据 Uniswap 上该池子的兑换率自动将部分资产兑换成另一种,让两种资产的数量能满足添加流动性的匹配要求,然后就投入到 Uniswap UNI/ETH 的资金池里,得到该池子的 LP Token。如果用户选择的是 liquidity providing pool,那流程到此就算完成了;如果用户选择的是 yield farming pool,那还会自动将 LP Token 质押到 Uniswap。
其他杠杆挖矿产品的逻辑也是大同小异,较大的不同在于其他杠杆挖矿产品大多只有 yield farming pools,并没有 liquidity providing pools。
那么,杠杆挖矿的风险又如何呢?
首先,在没杠杆的情况下,用户往 AMM 模式的 DEX 提供流动性时,参与挖矿的两个币种一旦出现汇率变化,就会产生无常损失(Impermanent loss,也称“非永久性损失”),价格波动越大,无常损失越大;而加了杠杆之后,放大了本金,也同样放大了无常损失。通常,无常损失需要靠手续费分成和质押 LP Token 的收益所得来填补。所以,用户平仓时,如果已经赚取的收益不能覆盖无常损失,那用户其实就是亏损的。当然,这还没算上借贷的利息。
另外,用户进行杠杆挖矿,还存在被清算的风险。借贷资产+利息就是债务,LP Token 的价值就是头寸,当债务比率(债务/头寸)达到清算线时,就会进行清算。为了避免清算,用户可以通过还款降低债务,或追加 LP Token 增加头寸。
杠杆挖矿最大的风险应该是资金池枯竭的风险,因为用户投入的本金和借贷的资产全都流入到 DEX 平台了,只有等到平仓后资产才会重新流回资金池。那么,如果没有足够的存款支撑,就难以维系下去。因此,需要大力吸引用户来存款,利率会远比 Compound、Aave 等借贷平台高得多,且通常采用三级利率模型,才可以较好地均衡资金池的资金使用率。比如,Alpaca(羊驼)的利率模型如下:
- 资金使用率 0% - 50% 时,利率 = 0.4 × 资金使用率
- 资金使用率 50% - 90% 时,利率 = 20%(恒定)
- 资金使用率 90% - 100% 时,利率 = 13 × 资金使用率 - 11.5
整合
那么,将超额抵押借贷、杠杆交易、杠杆挖矿,三者都整合到一起,这样的一款平台产品,又将如何呢?
首先,三者共享资金池,可大大提高资金使用效率,这点是毋庸置疑的,毕竟资金进出的应用场景变多了,如下图:
而且,这完全是可设计成具有扩展性的,即还可以为资金池再添加其他应用场景,比如闪电贷、期权合约等。
利率模型方面,使用 Compound 的利率模型是不太合适的,我觉得类似 Alpaca 的三级利率模型会比较合适。
另外,杠杆交易和杠杆挖矿,其实都涉及到了币种的兑换,那就可以做成一个统一的兑换模块,该模块可对接 Uniswap、SushiSwap、Bancor 等,可获取最优价格进行兑换。这其实也称为聚合交易。
从技术层面来说,核心功能其实可以拆分为多个相互独立的模块:资金池存借、杠杆交易、杠杆挖矿、聚合交易、价格预言机。资金池存借其实就是现有的 Compound 模块,其他模块各成体系,且模块之间可以通过接口的方式实现交互。
至于具体的设计,以后有机会再聊。
总结
至此,整个 Compound 系列就暂告一段落,不过并不代表以后就不再继续深入聊该话题了。我还有后续的计划,可以敬请期待。