Compound V3(Comet)架构详解

理解 V3 单一可借基础资产、隔离市场、不可变参数与 Comet/Configurator/Factory 合约体系

6 分钟阅读
Compound V3(Comet)架构详解

Compound V3(Comet)架构详解

目录


一、与 Compound V2 的根本区别

Compound V3(代号 Comet)彻底改变了借贷模型:

  • 每个市场只有一种可借资产,叫基础资产(base asset)。USDC 市场只能借 USDC,ETH 市场只能借 ETH——想借不同资产要去不同的 Comet 部署;
  • 抵押品不能赚取利息,它们纯粹用来担保借款;
  • 这造就了隔离市场(isolated markets):风险被隔离在各自市场内,一个抵押品出问题不会污染整个协议。

对比 V2:V2 里你可以从一个市场借多种资产,抵押品也能赚息(生成 cToken)。V3 牺牲了灵活性换取风险隔离和 Gas 效率

二、核心架构组件

2.1 Comet 主合约

Comet 是”Compound V3 的主借贷合约”。系统用代理模式:用户和代理交互,代理 delegatecall 到真正的实现。代理地址永久不变,治理可以把实现升级为新的 Comet 实例。

所有面向用户的逻辑都在 Comet 合约里,Comet 代理把功能委托给它。

2.2 继承链

合约职责
CometMath.sol安全类型转换工具(如 safe64,溢出则 revert)
CometStorage.sol集中定义所有存储变量;继承链里其他合约都不定义存储
CometCore.sol处理借贷双方的利息累积追踪
CometMainInterface.sol以抽象合约形式定义接口
Comet.sol实现借、贷、还款、清算等公开函数

把所有存储集中在 CometStorage 一个合约里是关键设计——保证升级时存储布局可控(呼应 Module 6 的存储冲突知识)。

2.3 CometExt 扩展模式(绕过 24KB 限制)

以太坊合约字节码有 24KB 上限。Comet 逻辑太多装不下,于是用 fallback + 扩展 模式:

name() 这样的函数不在 Comet 里,而在 CometExt 里。调用时命中 Comet 的 fallback,再 delegatecall 到 CometExt。CometExt 保持和 Comet 相同的继承结构,以保证存储布局兼容(否则 delegatecall 会读错插槽——Module 6 第 4 篇的核心教训)。

三、参数管理:全部用 immutable

Compound V3 一个不寻常的设计:所有配置参数都是 immutable 变量,不是可变存储。改参数 = 部署一个全新 Comet 实例 + 把代理指向它。

理由很有说服力:

  • immutable 比存储变量省 Gas 得多(读取不花 SLOAD);
  • 核心合约不必塞满 setter 函数,更简洁安全;
  • 实际上主网的利率曲线只改过三次(2023 年 5 月、7 月、12 月)——改动稀少,重新部署完全可接受。

这是典型的”低频更新、高频读取”场景下选 immutable 的工程决策。

3.1 配置合约族

合约职责
CometConfiguration.sol定义参数化 Comet 行为的结构体(利率曲线、清算阈值、抵押品规格、清算因子)
ConfiguratorStorage.sol用 mapping 存这些配置结构体,存在 Comet 之外
Configurator.sol继承 ConfiguratorStorage,提供仅治理可调的 setter
CometFactory.sol逻辑极少,主要是 clone() 函数部署新 Comet 实例(虽叫 clone,但部署的是完整实例,不是 EIP-1167 代理)

四、用户交互:供应与借贷

出借基础资产:用户存入基础资产(如 USDC)赚取利息,还累积 COMP 奖励。“Net Supply APR” = 出借利率 + COMP 奖励价值。例:出借 USDC 赚 6.47% 利息 + 4.63% COMP 价值 = 共 11.10%

抵押借款:用户存入抵押品(ETH、wBTC),借出基础资产。抵押品必须满足治理设定的抵押率。例:存 0.06 ETH(值 $110)借出 $100 USDC。“Net Borrow APR” = 付出利息 − COMP 奖励。例:付 6.71% 利息,赚 2.58% COMP,净 4.13%

利差:借贷双方利率不同。差额(如 “0.24% 的 USDC 利息流向协议”)补偿协议的运营成本,进入储备金。

五、利率与利息机制概览

  • 利率按每秒计(不是每区块),详见下一篇《Interest Per Second》;
  • 区分本金价值(Principal Value)现值(Present Value),用利率指数转换,详见《利率指数》篇;
  • CometCore 负责追踪和解释借贷双方的利息累积。

六、奖励系统

RewardsClaimer(架构图里的粉色块)负责分发 COMP。Comet 只追踪参与度,不直接发奖励。Rewards 合约读取 Comet 的状态,按治理设定的参数分发 COMP,速率可通过治理提案调整。

七、治理与参数更新流程

  1. 治理提案并投票修改参数(利率曲线、清算率、允许的抵押品);
  2. Configurator 的 setter 更新 ConfiguratorStorage 里的存储;
  3. Configurator 调自己的 deploy(),经 CometFactory;
  4. 部署一个编码了新配置的新 Comet 实例;
  5. 治理执行交易,把代理的实现指针更新到新实例。

真实提案 #162 就是这样:先调 Configurator 的 setter 更新参数,最后一步部署新 Comet 实例。

八、总结

  • V3 = 单一可借基础资产 + 抵押品不计息 + 隔离市场,牺牲灵活换风险隔离;
  • Comet(代理 + 实现)是核心,存储集中在 CometStorage,逻辑超 24KB 用 CometExt fallback 扩展;
  • 参数全用 immutable,改参数靠”重新部署 Comet + 切代理”;
  • Configurator/Factory 负责生产新 Comet 实例;
  • 奖励由独立 Rewards 合约读 Comet 状态分发。

九、动手练习项目:迷你隔离借贷市场 MiniComet

项目目标

实现一个高度简化但保留 V3 核心设计的隔离借贷市场:单一基础资产、抵押品不计息、参数 immutable、代理可升级。部署到 Sepolia。

合约要求

1. MiniCometStorage.sol:集中定义所有存储(userPrincipal、userCollateral、totalSupplyBase、totalBorrowBase 等),其他合约只继承不另加存储。

2. MiniComet.sol(实现,继承 Storage)

  • 构造函数注入 immutable 参数:baseTokencollateralTokenborrowCollateralFactor(如 0.8e18)、liquidateCollateralFactor
  • supply(uint amount):存入基础资产(出借);
  • withdraw(uint amount):取出基础资产或借出(若取超过自己存的,变成借款,需有足够抵押);
  • supplyCollateral(uint amount) / withdrawCollateral(uint amount):抵押品进出,抵押品不计息
  • isBorrowCollateralized(address) view:抵押是否足够;
  • 简化利息:先用固定利率,下一篇练习再接每秒利率。

3. MiniCometProxy.sol:ERC-1967 风格代理(复用 Module 6 成果),实现可升级。

测试要求(Foundry)

  1. test_SupplyEarnsBase:供应基础资产后余额记录正确;
  2. test_BorrowAgainstCollateral:存抵押品后可借基础资产,借款记为负 principal;
  3. test_CannotBorrowWithoutCollateral:无足够抵押借款 revert;
  4. test_CollateralEarnsNoInterest:时间推进后抵押品数量不变(不计息);
  5. test_ImmutableParams:参数确为 immutable(无 setter,读取不消耗 SLOAD——可对比 Gas);
  6. test_ProxyUpgrade:升级实现后存储(用户头寸)保留;
  7. test_IsolatedMarket:只能借 baseToken,尝试借 collateralToken 无此功能。

Sepolia 部署与验证步骤

  1. 部署两个测试代币(USDC 当 base、WETH 当 collateral)、MiniComet 实现、Proxy;
  2. supply 一些 base,另一个账户 supplyCollateral 后 borrow;
  3. 在 Etherscan 上读各账户 principal、抵押状态;
  4. 部署新实现,通过代理升级,确认头寸不丢。

进阶挑战(可选)

  • 把逻辑拆成 MiniComet + MiniCometExt,用 fallback 委托,亲手实现 24KB 绕过模式并验证存储布局一致;
  • 写注释回答:为什么 V3 选”重新部署改参数”而不是加 setter?这在 Gas 和安全上各有什么取舍?

💬 评论