Cointime

扫码下载App
iOS & Android

慢雾:Compound Finance V2 安全审计手册

项目方

随着 DeFi 生态系统的迅速发展,Compound Finance V2 作为该领域的先驱者之一,凭借其创新的借贷模式吸引了大量用户。然而,任何复杂的分布式应用都面临着潜在的安全威胁,尤其是涉及到价值数百万甚至上亿美金的资金流动时。因此,对 Compound Finance V2 及其分叉项目进行全面且细致的安全审计显得尤为重要。本手册旨在为开发者、安全研究员以及 DeFi 爱好者提供一份详尽的安全审计指南,帮助大家更有效地识别和防范潜在的风险。

1. 项目背景概述

Compound Finance V2 是一个基于以太坊区块链构建的开放式借贷平台,允许用户存入各种 ERC-20 底层代币并从中赚取利息,同时也允许以支付利息的形式借用市场中的代币。通过引入“利率市场”的概念,它实现了去中心化的资金池管理和自动化的利率调整机制。

2. 项目架构分析

Compound Finance V2 的核心架构组件包括:

  • Comptroller:控制整个系统逻辑,如利率计算、账户状态维护等。
  • cToken:实现 ERC-20 标准的自定义代币,代表用户在系统中的权益。
  • InterestRateModel:计算存款和借款利率的模型。
  • PriceOracle: 提供资产价格的预言机。
  • Governance:负责社区治理相关的功能。

2.1 Comptroller

Comptroller 合约是 Compound Finance V2 的中枢神经系统,它负责协调各个 cToken 实例的行为。主要职责有:

  • 管理市场列表,确定哪些市场是活跃的。
  • 执行跨市场操作的各类检查,如用户的头寸健康度检查等。
  • 设置和更新全局参数,如借款限额、抵押因子、清算阈值等。

2.2 cToken

每个支持的 ERC-20 代币都有一个对应的 cToken 实例(即 CErc20 / CEther 合约),用于处理该代币所有与项目的交互操作。每个 cToken 除了实现了基本的代币转账功能外,还添加了一些特定于 Compound 的功能,如借贷、累积利息和分配奖励。所以我们可以将 cToken 看作是用户在 Compound 上存入资产的凭证和用户进行借贷操作的入口。

当用户将底层的资产代币存入合约后,即可铸造对应的 cToken 代币,cToken 与标的资产的兑换比例按照如下公式计算:

注意:borrows 表示借款额,cash 表示资金池余额,reserves 表示储备金。借款利率由使用率决定,存款利率由借款利率决定。

用户一般通过与不同的 cToken 合约交互来在不同的市场中进行代币的借贷操作:

2.3 InterestRateModel

InterestRateModel 合约定义了计算利率的方法。不同的市场可能会使用不同类型的利率模型,以适应各自的风险偏好和流动性需求。

Compound V2 的市场中使用的利率模型主要有两种,一种是直线型,一种是拐点型。

直线型模型的借款利率计算公式如下:

资金使用率的计算公式如下:

存款利率则随着借款利率线性变化:

使用率逐渐升高则意味着资金池里的钱在逐渐减少,当达到一定峰值时可能会导致用户无法正常存款和借款。为尽量避免这种情况,Compound 推出了第二种利率模型 —— 拐点型。

拐点型的借款利率计算公式如下:

当使用率达到一定的峰值时,会瞬间大幅提高借款利率和存款利率,激励用户多存款少借款,以此将使用率控制在合适的范围,这个峰值也被称为拐点(一般是利用率达到 80% 时)。

2.4 PriceOracle

PriceOracle 合约负责获取外部市场价格信息,并将其转换为系统内部使用的数值,这对于准确计算用户的头寸价值至关重要。

2.5 治理机制与激励模型

Compound 引入了一种独特的治理机制,允许持有治理代币(COMP) 的用户参与重要决策的投票,如更改某些参数或添加新的资产类型。通过发行治理代币(COMP),Compound 激励用户积极参与平台活动,并为贡献者提供奖励。详细内容可参考 Compound 官方文档和代码仓库。(https://docs.compound.finance/v2/; https://github.com/compound-finance/compound-protocol)

3. 交互流程

接下来,我们通过简单示例来说明用户在 Compound Finance V2 上进行交互的大致过程:

3.1 存款和赎回流程

如果用户 Alice 需要将 1 个 WBTC 存入 Compound,那么他将调用 cWBTC 合约的 mint 函数来进行存款。该合约继承了 cToken 合约,会先通过 mintInternal 函数内部调用 accrueInterest 函数来更新借款和存款利率,之后调用 mintFresh 进行具体的铸造操作。

mintFresh 函数会外部调用 Comptroller 合约的 mintAllowed 函数来检查当前市场是否允许存款,然后将用户的 1 个 WBTC 通过 doTransferIn 函数转入合约,再根据当时最新的兑换率为用户铸造相应数量的 cToken 代币(假设当前最新的兑换率是 0.1,那么 Alice 将收到 10 个 cWBTC 代币)。

如果 Alice 未来决定赎回存款,她可以通过调用 redeem 函数将 cWBTC 兑换回 WBTC,兑换率可能已经改变(假设为 0.15),这意味着 Alice 能够赎回 1.5 个 WBTC,其中 0.5 个 WBTC 为利息收入。

3.2 借款和还款流程

Alice 首先需要调用 Comptroller 合约的 enterMarkets 函数将她的 cWBTC 设置为可作为抵押品的状态,之后才可以进行借款。

假设 Alice 选择借出 70 个 USDC,由于 WBTC 的抵押因子为 0.75,Alice 最多可以借出相当于 75% 的 WBTC 价值资产,所以这不会超过她的最大借款额度。

注意:为了避免被清算的风险,Alice 应该保留一定的缓冲空间而不是完全用尽她的借款额度。

Alice 调用 cUSDC 合约的 borrow 函数,其会先通过 borrowInternal 函数内部调用 accrueInterest 函数来更新借款和存款利率,之后调用 borrowFresh 进行具体的借款操作。

在通过 Comptroller 合约的 borrowAllowed 函数进行用户的头寸价值检查后,先进行借款数据的记账,之后通过 doTransferOut 函数将代币转出给用户。

若 Alice 需要还款,可以通过调用 cUSDC 合约的 repayBorrow 函数自行还款,或者让其他人调用 repayBorrowBehalf 函数来代还款。

3.3 清算流程

如果 WBTC 的价格大幅下跌,使得 Alice 的抵押品价值低于其借款额度的 75%,则 Alice 的贷款头寸将处于被清算状态。

外部清算人(例如 Bob)可以调用 cUSDC 合约中的清算函数 liquidateBorrow 来帮助 Alice 清偿部分债务。其会先通过 liquidateBorrowInternal 函数同时更新 cUSDC 与还款用的抵押品 cToken 的利率,之后调用 liquidateBorrowFresh 进行具体的清算操作。

在通过 Comptroller 合约的 liquidateBorrowAllowed 函数进行是否允许清算的检查后,会先调用 repayBorrowFresh 函数将 USDC 转入合约进行还款,并更新被清算人的借款数据。接着调用 Comptroller 合约的 liquidateCalculateSeizeTokens 函数根据清算的价值来计算 Bob 可以拿到 Alice 相应价值的抵押品数量,最后通过指定抵押品市场的 cToken 合约(例如 cWBTC)的 seize 函数来为 Bob 和 Alice 转移 cToken。

打开此链接可查看上图的高清版,点击阅读原文也可直接跳转:https://www.figma.com/board/POkJlvKlWWc7jSccYMddet/Compound-V2?node-id=0-1&node-type=canvas。

Bob 为 Alice 清偿部分贷款(例如 20 USDC),并因此获得 Alice 相应价值的抵押品(如 WBTC),同时 Bob 还能额外获得一笔清算激励(假设为 5%)。最终结果是 Bob 收到了价值 21 个 USDC 的 WBTC(20 USDC 的贷款 + 1 USDC 的清算激励)。

4. 安全漏洞 Checklist

4.1 空市场导致的舍入漏洞

如果 cToken 是一个空市场的情况(即没有用户在市场中进行借贷),由于 exchangeRateStoredInternal 函数中 exchangeRate 的值依赖于合约对应的底层资产代币的数量,所以可以通过向 cToken 合约转入大量的底层资产代币来操纵 cToken 的价格。

因此,可以用少量的 cToken 借出大量的其他代币,之后再调用 cToken 的 redeemUnderlying 函数来提取底层资产代币。在计算赎回时,需要扣除的 cToken 数量会由于除法的向下舍入导致结果远少于预期(几乎只有一半)。

假设此时持有的 cToken 的数量是 2(同时也是总的 totalSupply),而 exchangeRate 在经过操控后被拉高为 25,015,031,908,500,000,000,000,000,000,需要赎回的底层资产代币数量为 50,030,063,815。那么预期应该扣除的 cToken 数量应该为:

而实际计算出来的 cToken 数量却为:

因此,最后只需要清算极少数的 cToken 就可以获得从其他市场中借出的大量资产代币。

可以参考由于该漏洞导致的 Compound 分叉项目 Hundred Finance 被黑的交易:https://optimistic.etherscan.io/tx/0x6e9ebcdebbabda04fa9f2e3bc21ea8b2e4fb4bf4f4670cb8483e2f0b2604f451

审计要点:在审计时,需要关注兑换率的计算方式是否容易被操控以及舍入的方式是否恰当,同时可以建议项目团队在新的市场创建后立刻铸造小额的 cToken,以防止市场为空进而被操控。

4.2 ERC677 / ERC777 代币导致的重入漏洞

ERC677 / ERC777 是 ERC20 合约的一个扩展,兼容 ERC20 代币的协议标准。这些代币允许在转账过程中,如果接收地址是合约则会触发接收地址的回调函数(如 transferAndCall 或 tokensReceived)。

在旧版本的 Compound Finance V2 代码中,当用户在 cToken 市场中进行借款时,会先将被借的代币转出,之后再进行借款数据的记账。

假如用户借出的代币是带有回调功能的 ERC677 / ERC777 代币的话,那么可以构造接收代币的恶意合约来通过回调函数重入 borrow 函数中进行再次借款,由于上一次借款时用户的借款数据还未被记账,所以此时可以成功通过账户的健康系数检查来再次借出代币。

可以参考由于该漏洞导致的 Compound 分叉项目 Hundred Finance 被黑的交易: https://blockscout.com/xdai/mainnet/tx/0x534b84f657883ddc1b66a314e8b392feb35024afdec61dfe8e7c510cfac1a098

审计要点:最新版本的 Compound V2 代码中已经修复了借款逻辑,改为先记录借款的数据再转出被借的代币。在审计中,需要关注借贷功能的相关代码是否符合 CEI(Checks-Effects-Interactions) 规范,并且需要考虑具有回调功能的代币造成的影响。

4.3 不恰当的预言机机制导致的价格操控风险

由于 Compound Finance 采用超额抵押贷款的模式,用户能借出的代币数量取决于抵押品的价值是否足够。

因此,如果项目在计算抵押品价值时所采用的预言机的喂价机制容易被操控,则很容易借出超预期的代币。

举个例子,在 Compound 分叉项目 Lodestar Finance 被黑的事件中,预言机获取抵押品 plvGLP 代币价格的方式是先将 plvGLP 合约中 plsGLP 代币的数量(totalAssets) 除以 plvGLP 的总供应量(totalSupply) 计算出兑换率,再将兑换率乘上 GLP 代币的价格计算出 plvGLP 代币的价格。

而 plvGLP 有一个捐赠的功能,允许用户捐赠 sGLP 为 plvGLP 代币合约铸造相应的 plsGLP 代币。

所以攻击者可以先利用闪电贷在 Lodestar Finance 市场中创建大量 plvGLP 抵押品头寸,之后在 GMX 上利用闪电贷大量铸造 sGLP,再通过 donate 函数为 plvGLP 合约铸造 plsGLP 代币以增加 totalAssets 的值。随着总资产的增加,plvGLP 的汇率会变大,导致 plvGLP 代币的价格瞬时急速上涨,从而可以在市场上借出超出预期的其他代币。

可以参考 Lodestar Finance 被黑的交易:https://arbiscan.io/tx/0xc523c6307b025ebd9aef155ba792d1ba18d5d83f97c7a846f267d3d9a3004e8c

此外还需注意的是,Compound Finance 或其分叉项目也会采用链下预言机例如 ChainLink 或者 CoinBase 来获取抵押品的价格。如果遇到市场剧烈波动的情况,可能会导致链下价格与链上出现价差而危害项目的资金安全。

例如 LUNA 代币的价格由于市场原因而急速暴跌,而 Compound Finance 的分叉协议 Venus Protocol 和 Blizz Finance 都使用 Chainlink 预言机作为喂价来源来计算抵押品的价值,其中对 LUNA 代币的最低价格(minAnswer) 进行了硬编码,其值为 0.10 美元。

当 LUNA 代币的价格跌破 0.1 美元时(例如 0.001 美元),任何人都可以按市场价格购买大量 LUNA,并将其作为抵押品(价值 0.10 美元)从平台借出其他资产。

审计要点:在审计时,需要关注计算抵押品价值时采用的预言机喂价机制是否容易被外部操控,可以建议项目方采用多种价格来源进行综合评估,以规避单一价格来源造成的风险。

4.4 多入口点代币导致的汇率操控风险

在 Compound 的代码中有一个名为 sweepToken 的函数,其作用是为了让不小心将代币转入到合约的用户能够取出这些代币。旧版本的代码如下,这个函数有一个重要的安全检查:传入的 token 参数不能是合约的底层资产代币。

然而,假如某个 cToken 市场的底层资产代币存在多个入口点合约(通过多个合约地址能访问同一底层余额,外部交互影响所有入口点的余额,这是一种早期的类似代理的模式),攻击者则可以调用 sweepToken 函数通过传入与 underlying 不同的入口点合约,将合约中的底层资产代币转出。

下面以 TUSD 为例,其拥有两个入口点合约,辅助入口点合约 0x8dd5fbce 会将任何的调用(例如 transfer 或者 balanceOf)转发到主合约,这意味着与其中任何一个合约的交互会影响两个合约中的余额数据(即两个不同的合约共用相同的余额数据)。

此时假设市场中设置的底层代币地址是 TUSD 的主合约地址,那么我们可以在调用 sweepToken 函数时将辅助入口点合约地址 0x8dd5fbce 作为传入的 token 参数,则可以成功通过检查 address(token) != underlying,之后合约会将其中全部的底层资产代币 TUSD 转移到管理员地址。

而 TUSD / cTUSD 的兑换率会受到 cTUSD 合约中底层资产代币 TUSD 数量的影响,当 TUSD 被全部转移到管理员地址后,TUSD / cTUSD 的汇率会瞬间暴降。此时攻击者可以以极低的兑换率去清算其他用户或者在借款之后偿还少于预期的代币数量来获利。

值得一提的是,Compound V2 的最新版本代码中对 sweepToken 函数添加了权限验证,保证只能由管理者角色来调用该合约,并且已经移除了所有存在多入口点代币的市场。

审计要点:在审计时,对于转移合约内代币的功能,需要考虑到多入口点代币存在的场景对项目造成的影响,可以建议项目方不采用多入口点代币或者验证代币转移前后合约中的底层资产代币数量是否会有变化,并对相关的功能做好权限的检查。

4.5 新旧版本合约代码的兼容性问题

如果在 Compound Finance V2 分叉项目中,某个核心合约的代码分叉的是新版本的 Compound Finance V2 代码,而与其交互的某个其他合约采用的却是旧的代码版本,那么可能会出现兼容性的问题。

例如旧版本的 cToken 使用的 InterestRateModel 合约中获取借款利率的函数 getBorrowRate 的返回值是两个 uint 类型的值,而在新版本的 InterestRateModel 函数中,getBorrowRate 函数只会返回一个 uint 类型的值。

但是在 Compound Finance V2 分叉项目 Percent Finance 中,项目方使用的是旧版本的 cToken 合约代码,而 InterestRateModel 合约却是采用的新的版本,这就导致了 cToken 中的 accrueInterest 函数调用 getBorrowRate 函数时会失败。而 accrueInterest 函数在提现和借贷中都有使用到,最终使得提现和借贷功能均无法正常进行,合约中的资金被彻底锁住。

审计要点:在审计时,需要关注更新的代码中的合约接口、状态变量、函数签名和事件的变更是否会破坏现有系统的正常运行,确保所有合约代码版本更新的一致性或者保证更新后的代码能够兼容旧版本的代码。

4.6 多链部署导致的硬编码问题

在 Compound Finance V2 的代码中,常量 blocksPerYear 代表每年产出区块的预估数量,其值在利率模型合约中被硬编码为 2102400 ,这是因为以太坊的平均出块时间为 15 秒。

然而不同链的区块时间不一定相同,同样全年产出的大致区块数量也不一定是相同的。如果某个 Compound 的分叉项目在其他链上部署,但是却没有根据不同链的情况修改硬编码的值,那么可能会造成利率最后计算的结果超出预期。这是因为 blocksPerYear 的值会影响到 baseRatePerBlock 和 multiplierPerBlock 的值,而 baseRatePerBlock 和 multiplierPerBlock 最终会影响到借款利率。

例如 BSC 链的出块时间是 3 秒,那么全年预估的出块数量(blocksPerYear) 应该为 10512000。如果在部署前没有修改 blocksPerYear 的值,那么会导致最后计算出来的借款利率比预期高出五倍。

审计要点:在审计时,要关注项目合约中硬编码的常量或变量在不同链的特性下是否会造成非预期的结果,建议项目方根据不同链的情况来正确地修改其值。

其他

除了上面提到的这些主要关注的问题,Compound V2 的分叉项目通常会根据项目团队的设计来修改部分业务逻辑,例如添加与外部第三方协议进行交互的代码。这需要在审计时根据其具体的业务逻辑和设计需求去评估是否会对 Compound Finance V2 本身的核心借贷模型以及项目造成影响。

写在最后

希望这份 Compound Finance V2 及其 Fork 项目的安全审计手册能帮助大家在审计时更好地理解和评估此类复杂系统的安全性,随着技术的迭代更新,本手册也会随之更新和完善。

[1] https://github.com/YAcademy-Residents/defi-fork-bugs

[2] https://medium.com/chainsecurity/trueusd-compound-vulnerability-bc5b696d29e2

[3] https://github.com/code-423n4/2023-05-venus-findings/issues/559

[4] https://learnblockchain.cn/article/2593

[5] https://github.com/compound-finance/compound-protocol

作者 | 九九

编辑 | Liz

评论

所有评论

推荐阅读

  • 美参议院批准凯文·沃什出任美联储主席

    5月14日,美国参议院以54票对45票通过确认沃什出任美联储主席。参议院此前已于12日批准沃什担任美联储理事,为期14年。随着13日主席任命获得通过,沃什将在完成白宫相关签署程序后正式履职,接替任期将于本周五(5月15日)结束的现任主席鲍威尔。不过,鲍威尔预计仍将继续留任美联储理事。此次投票是历史上党派分歧最严重的一次:仅有一位民主党人——宾夕法尼亚州参议员约翰·费特曼——与共和党多数派一同投了赞成票。

  • 英伟达股价续创记录新高 总市值达到5.5万亿美元

    5月13日电,美股盘初,英伟达股价上涨2.85%,报227.080美元/股,续创记录新高,总市值报5.50万亿美元。

  • BTC跌破80000美元

    行情显示,BTC跌破80000美元,现报79998.07美元,24小时跌幅达到1.06%,行情波动较大,请做好风险控制。

  • 美国总统特朗普将抵达北京

    5月13日傍晚,美国总统特朗普乘专机将抵达北京。

  • OG Agent 全球启动大会(深圳站)圆满落幕:智算时代正式开启

    2026年5月13日,由 Ju.com 与 Nivex 联合主办的 AI 意图驱动引擎 「智能交易意图觉醒: OG Agent 全球启动大会」于今日在在杭州、重庆、深圳三城同时圆满闭幕。本次盛会汇聚全球区块链精英与生态领主,共同见证 Web 4.0 时代 AI 交易的里程碑时刻。

  • OG Agent 全球启动大会举行圆桌论坛,就《Web 4.0 时代:AI 意图交易的基于、挑战与未来方向》主题展开讨论

    2026年5月13日,由 Ju.com 与 Nivex 联合主办的 AI 意图驱动引擎 「智能交易意图觉醒: OG Agent 全球启动大会」于今日在在杭州、重庆、深圳三城同时拉开帷幕,此次三城联动,标志着 OG Agent 所蕴含的巨大 AI 动能深受用户青睐。活动期间,区块链商业博主 Michael、香港国际投资总会执行董事 Annetta、Mblock 投研基金创始人 Louis、大湾区上市联盟主席 Alex 就《Web 4.0 时代:AI 意图交易的基于、挑战与未来方向》主题展开讨论。

  • 先进的 Web4.0 智能意图 AI 大模型 OG Agent 正式完成全球发布和生态启动

    2026年5月13日,由 Ju.com 与 Nivex 联合主办的 AI 意图驱动引擎 「智能交易意图觉醒: OG Agent 全球启动大会」于今日在在杭州、重庆、深圳三城同时拉开帷幕,此次三城联动,标志着 OG Agent 所蕴含的巨大 AI 动能深受用户青睐。包括 Ju.com CEOSammi、OG Agent 国际生态大使 Becky、OG Agent 韩国市场领袖 Jacob、世界人工智能组织首席科学家郑晓军等参会嘉宾,以及8 位重要嘉宾,和上千名参与者共同见证了OG Agent 的全球发布和生态启动。

  • 美国SEC:纽交所代币化证券提案已正式生效

    5月12日美国SEC官网发布文件,详细说明了纽交所之前提交的代币化证券的规则修订提案,并表示纽交所代币化证券提案已自动正式生效。2026年5月1日纽交所提交本次规则变更申请,并于5月12日由美国SEC发布公告。根据美国SEC的规定,只要拟议规则变更的不会显著影响投资者保护或公共利益,不会对竞争造成任何显著负担,则其在提交之日起30天后(或SEC指定的更短时间)自动生效。

  • 世界人工智能组织首席科学家郑晓军:普通人在 AI 时代,最重要的就是抓住 AI 机遇,获得收益

    2026年5月13日,由 Ju.com 与 Nivex 联合主办的 AI 意图驱动引擎 「智能交易意图觉醒: OG Agent 全球启动大会」于今日在在杭州、重庆、深圳三城同时拉开帷幕,此次三城联动,标志着 OG Agent 所蕴含的巨大 AI 动能深受用户青睐。活动期间,世界人工智能组织首席科学家郑晓军发表《Global AI DAO》主题演讲,郑晓军表示,普通人在 AI 时代,最重要的就是抓住 AI 机遇,获得收益。目前总结有几种方式可以获得财富:1.投资有代表性的非上市公司指数基金,往往会有 10-30 倍的潜在收益,但往往受限于参与资金和账号门槛;2.让 AI 帮你补齐自身的短板缺陷,比如复制知名网红去做商业化带货,这需要用户自己掌握基本的 AI 技术和代码;3.通过 AI 去做量化交易,就和 OG Agent 一样。当前 AI Trade 系统,基本上能够实现完全自动化交易,这将成为未来的主流。