本文深入解析了DeFi中重要的借贷协议Compound V3的智能合约架构及其使用方法。文章分为几个部分,详细介绍了如何借贷USDC、提供抵押品以及清算不足抵押的贷款,并通过代码分析了Compound V3的核心合约Comet及其相关合约的结构。另外,还讨论了参数更新的治理机制,强调了Immutable变量的设计优势。
Compound 是 DeFi 领域最重要的借贷协议之一,几乎启发了多个区块链上的每个借贷协议的设计。本文将解释 V3 的智能合约架构。
由于 Compound 是一个借贷协议,我们假设读者熟悉 DeFi 中利率的工作原理 并了解 清算和抵押 在 DeFi 贷款中的背景。
与 Compound V2 不同,每个 Compound 实例仅仅借出一种资产。也就是说,你只能从 USDC 市场借用 USDC,从 ETH 市场借用 ETH - 你无法借用其他任何东西。为了借用这些资产,你需要根据市场规定的抵押比例提供抵押品(该比例由治理设定)。
在 Compound V3 中,可借资产被称为 base asset。由于 USDC 是最受欢迎的借贷资产,在我们的示例中将使用 USDC 作为基础资产。处理借贷核心逻辑的智能合约在他们的文献和代码库中称为 Comet。目前 Compound 在以太坊主网以及多个 L2 上实例化:Polygon、Base 和 Arbitrum。可用的 Compound V3 市场列表在此。
本文概述了如何使用这个智能合约以及 Solidity 文件的架构。
在 Compound V3 中可以执行三项主要操作:
出借基础资产
提供抵押并借用基础资产
清算不足抵押的贷款。
以下视频展示了在 Polygon 网络上出借 USDC 的操作(USDC on Polygon 市场链接)。
https://img.learnblockchain.cn/2025/02/26/file.mp4
参与该协议的用户会获得 COMP 代币奖励。上面的账户迄今为止已经累积了 0.0004 COMP 奖励,如粉红色箭头所示。奖励尚未被领取。此外,它迄今为止已经累积了超过 6 美分的利息(4,602.0649 – 4,602.00 = 0.0649)。
绿色框与黄色框中价格的不匹配是因为 USDC 并不总是正好 \$1.00。下图展示了 Chainlink 最近的 USDC / USD 的预言机价格,读者可以看到它并不总是一美元。
以下视频展示了借用的过程。这次该账户提供了 0.06 ETH (\$110)作为抵押并借用了 \$100 的 USDC。这是在 BASE L2 借贷市场 上进行的。
https://img.learnblockchain.cn/2025/02/26/file.mp4
检查浏览器钱包后,该账户现在拥有 100 Base USDC。USDCbC 是桥接到 Base 的 USDC。
以下视频展示了用户还清 USDC(在利息显著累积之前)并提取 ETH 抵押品的过程。
在上面的截图中,请注意净供应年利率(出借利率)高于净借款年利率(黄色圆圈)。这通常是不可能的,因为出借人无法赚取比借款人支付的更多。Compound 正在将 COMP 奖励的价值纳入计算。具体来说,借款人目前支付 6.71% 的利息(上方红色圆圈),但在 COMP 中获得 2.58% 的利息(上方蓝色圆圈)。6.71 – 2.58 = 4.13%,这就是净借款年利率。
同样,目前出借人从借出 USDC 中收益 6.47%(下方红色圆圈),但也从 COMP 奖励的价值中再获得 4.63%(下方蓝色圆圈)。这两者之和是 11.10%。
由于借款人支付 6.71% 的 USDC 利息,而出借人收取 6.47% 的利息,0.24% 的 USDC 利息流入协议。
COMP 奖励利率根据治理投票而变化。
Compound V3 有 4,304 行 Solidity 代码,不包括注释或空白空间。
以下是 Compound V3 的 Github 仓库 的截图。
以下图示总结了构成 Compound V3 的已部署智能合约。这只是一种高层次的概述,后面将提供更详细的内容。注意颜色编码与上面的高亮相匹配。具体而言,主要的借贷合约(Comet)是绿色的,与部署新 Comet 实例相关的合约是蓝色的,而奖励合约是粉色的。
大多数用户将通过 comet proxy(在 Github 中未显示)或通过奖励合约与 Compound V3 互动,以便作为出借人或借款人获得 COMP。所有用户面向的逻辑都在 comet 合约中,comet proxy 将其功能委派给这个合约。
配置和工厂合约是在治理投票升级时使用的,以部署新的 comet 实例。
带有星号 (*) 的智能合约有多个祖先合约。在下一部分中,我们将展示 Comet 的继承链。
如果加密经济条件变化,治理可以改变利率模型,清算因子也可以。Compound 将所有这些信息存储在不可变变量中,而不是存储中。要更改这些值,必须部署一个新的 Comet 实例,并将代理更新为新的实现合约。
这可能看起来是一个奇怪的设计选择,但它有几个优点:
我们将在稍后检查参数变化的生命周期。
Comet 的祖先在下图中展示。我们将在本节中对每个祖先合约提供高层次的概述。
Comet 数学简单地包含了一些将无符号整数转换为较低位值无符号整数并在可能导致溢出时回滚的函数。例如,如果我们从 uint256 转换为 uint104,但 uint256 的值大于 uint104 可存储的范围,则将回滚。你将在整个代码库中看到 safe64
函数分散,其中这些函数希望能够容易地理解,无需进一步解释。此文件较小,因此我们在此展示了整个内容:
Comet 使用的每个存储变量都定义在 CometStorage.sol 中,而没有其他地方。也就是说,Comet 继承链中除了 CometStorage 之外的其他合约都没有存储变量。
CometCore.sol 定义了 Compound 如何跟踪和解释出借人和借款人利息的累计。它还定义了一些全局常量。当我们讨论本金价值和现值时,将深入探讨此文件。
顾名思义,CometMainInterface.sol 只是一个接口文件,唯一的特别之处在于它被定义为一个抽象合约。由于接口是自明的,我们不讨论此内容。
Comet.sol 是该协议的明星合约。它提供了所有用户可见的出借、借贷、还款和清算贷款的公共函数。
为了避免达到 24kb 的部署限制,Comet 使用 fallback-extension pattern 将多个额外功能卸载到 CometExt 中。例如,name()
函数不在 Comet.sol 中,因此 无法在 Etherscan 上看到。
然而,如果我们通过 Foundry 调用该函数,我们可以看到这些合约的行为就像它存在一样。
发生的事情是,该函数调用命中回退函数并将调用委派给 CometExt,后者其中有一个名为 name()
的函数。
重要的是,CometExt 必须尊重 Comet 的存储布局并 扩展它,而不冲突。 这可以通过让 CometExt 模仿 Comet 的继承结构来实现。
请记住,继承只是一种语义描述——合同部署时,整个继承链变成一个合约。已部署的合约无法从另一已部署合约继承。
此处显示了一幅更详细的图,展示了 Comet 与 CometExt 之间的关系。
处理非利息奖励发放的合约仅有一个,如粉色框所示。
出借人和借款人因参与生态系统而获得 COMP 代币奖励。Comet 合约跟踪参与情况,但不处理奖励的发放。奖励合约读取 Comet 的状态并发放 COMP 代币。奖励发放的速率是放置在奖励合约中的参数,由治理设定。
如前所述,Comet 通过不可变变量进行参数化。更改这些参数需要更新代理,以指向由配置合约部署的新实现。
在实践中,更改利率曲线是非常少见的。主网合约仅经历过三次利率曲线更新:
<https://compound.finance/governance/proposals/162>(2023年5月30日)
<https://compound.finance/governance/proposals/168>(2023年7月17日)
<https://compound.finance/governance/proposals/201>(2023年12月11日)
利率并不是唯一可以更改的参数 - 其他值得注意的参数包括更改清算比例,甚至允许的抵押资产。好奇的读者可以浏览治理提案。
以下 GIF 展示了配置智能合约的结构以及治理如何部署带有更新参数的新 Comet 实例。
CometConfiguration.sol 定义参数化 Comet 整个行为的结构。CometStorage 则简单存储这些结构。
如果你已经阅读了我们关于 DeFi 利率工作原理以及 DeFi 抵押与清算的文章,那么结构中的变量名称也大多是自解释的。ConfiguratorStorage 继承了这些定义,并将结构存储在映射中。
这些存储变量不属于 Comet。Configurator 合约使用这些存储的配置来部署 Comet 实例。
下面我们展示 CometConfiguration 和 ConfiguratorStorage 的代码快照。
这是部署新 Comet 实例的更可取的方法,而不是在 calldata 中提供一个极大的结构。
当部署带有更新的 Comet 实例时,我们只需更新特定的存储变量,保持其他变量不变。例如,如果我们更改抵押 wBTC 的清算阈值,仅该属性的存储变量在配置器中受到影响。
Configurator.sol 合约继承 ConfigurationStorage,并提供仅限治理的设置函数。Configurator.sol 文件 相当大,因此我们不展示整个内容。然而,仅通过查看其中定义的事件,我们就可以很清楚地了解该合约的作用——更新结构中参数化新 Comet 实例部署的个别字段。
Configurator.sol 还持有 deploy()
函数以部署新的 Comet 实例。
上面代码中的 CometFactory
定义在 CometFactory.sol 中,并且文件相当小,因此我们展示了整个文件。函数 “clone” 有些误导,因为它并没有创建 proxy clones —— 它创建了一个新的实例。
我们参考早前的动画将之前的讨论结合在一起。
以治理 提案 162 为例。我们看到它调用了一些用于更新参数的设置函数,然后在最后一步(第 8 步)部署了一个新的 Comet 实例。
以下是所有文件之间的关系以及它们如何相互作用的总结。接口文件已被忽略。
请查看我们的 区块链培训营 以了解更多信息。
原文发布时间:2024年1月3日
- 原文链接: rareskills.io/post/compo...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!