十六、从源码讲解compound v2 源码总复盘 + 一条完整交易 walkthrough

  • tomenengr
  • 发布于 2026-05-06 09:21
  • 阅读 74

源码总复盘 + 一条完整交易 walkthrough

这讲不再介绍新模块,而是把前面所有东西串起来。我们用一个故事走完整个 Compound v2 生命周期:

Alice 存 ETH
Alice 把 cETH 设为抵押
Alice 借 DAI
市场计息
Alice 部分还款
ETH 价格下跌
Bob 清算 Alice
Bob 拿到 cETH
Bob redeem cETH

目标是:你看到一笔真实交易时,能知道它会落到哪些函数、改哪些状态、问哪些模块。


第十六讲:完整交易 Walkthrough

简介

本文用一条完整交易链路复盘 Compound v2:Alice 存入 ETH、进入抵押市场、借出 DAI、经历计息和部分还款,随后因 ETH 价格下跌被 Bob 清算,最后 Bob 赎回拿到的 cETH。读完可以把 mintenterMarketsborrowrepayBorrowliquidateBorrowseizeredeem 串成一条完整执行路径,并理解每一步对应的状态变化、风控检查和价值换算。

1. 初始角色

我们设定三个市场:

cETH:
  ETH 供应 / 抵押市场

cDAI:
  DAI 借款市场

Comptroller:
  全局风控中心

角色:

Alice:
  存 ETH,借 DAI

Bob:
  清算人

Comptroller:
  判断 Alice 能不能借、能不能被清算

Oracle:
  提供 ETH / DAI 价格

初始价格:

ETH price = 2000 USD
DAI price = 1 USD

cETH collateralFactor = 75%
liquidationIncentive = 1.08
closeFactor = 50%

2. 第一步:Alice 存入 ETH

Alice 调用:

cETH.mint{value: 10 ether}()

也就是供应 10 ETH。

源码路径

CEther.mint()
  -> mintInternal(msg.value)
    -> accrueInterest()
    -> mintFresh(Alice, 10 ETH)

mintFresh 做什么?

1. Comptroller.mintAllowed(cETH, Alice, 10 ETH)
2. 检查 cETH 市场 fresh
3. 计算 exchangeRate
4. 接收 ETH
5. 计算 mintTokens
6. totalSupply 增加
7. accountTokens[Alice] 增加
8. emit Mint
9. emit Transfer(address(0), Alice, mintTokens)

假设:

cETH exchangeRate = 0.02 ETH / cETH

Alice 存 10 ETH,得到:

mintTokens = 10 / 0.02 = 500 cETH

操作后:

Alice accountTokens[cETH] = 500 cETH
cETH cash 增加 10 ETH
cETH totalSupply 增加 500 cETH

注意,这一步 Alice 只是供应了 ETH,还不一定能用它借款。


3. 第二步:Alice enterMarkets

Alice 想用 cETH 作为抵押品,所以调用:

Comptroller.enterMarkets([cETH])

源码路径

Comptroller.enterMarkets([cETH])
  -> addToMarketInternal(cETH, Alice)

addToMarketInternal 做什么?

1. 检查 cETH market isListed
2. 检查 Alice 是否已经进入该市场
3. markets[cETH].accountMembership[Alice] = true
4. accountAssets[Alice].push(cETH)
5. emit MarketEntered(cETH, Alice)

操作后:

markets[cETH].accountMembership[Alice] = true
accountAssets[Alice] = [cETH]

这一步非常关键。

从现在开始,Comptroller 计算 Alice 账户健康度时,会把她的 cETH 算作抵押品。


4. 第三步:Alice 借 DAI

Alice 调用:

cDAI.borrow(10000e18)

也就是借 10,000 DAI。

源码路径

CErc20Delegator(cDAI)
  -> delegatecall CErc20Delegate.borrow
    -> borrowInternal(10000 DAI)
      -> accrueInterest()
      -> borrowFresh(Alice, 10000 DAI)

这里如果是代理版本,用户实际调用的是 cDAI Delegator,逻辑在 Delegate 中执行。


borrowFresh 主流程

1. Comptroller.borrowAllowed(cDAI, Alice, 10000 DAI)
2. 检查 cDAI 市场 fresh
3. 检查 cDAI cash >= 10000 DAI
4. 计算 Alice 当前 DAI 债务
5. accountBorrowsNew = oldDebt + 10000
6. totalBorrowsNew = totalBorrows + 10000
7. 更新 Alice BorrowSnapshot
8. 更新 totalBorrows
9. doTransferOut(Alice, 10000 DAI)
10. emit Borrow

其中最关键的是:

Comptroller.borrowAllowed

5. borrowAllowed 怎么判断?

Comptroller 会调用:

getHypotheticalAccountLiquidityInternal(
  Alice,
  cDAI,
  redeemTokens = 0,
  borrowAmount = 10000 DAI
)

意思是:

如果 Alice 现在新增 10,000 DAI 债务,
账户是否仍然健康?

Alice 抵押:

500 cETH
exchangeRate = 0.02 ETH / cETH

underlying ETH = 500 * 0.02 = 10 ETH

ETH 价格:

10 ETH * 2000 USD = 20,000 USD

抵押因子:

20,000 * 75% = 15,000 USD

新增借款:

10,000 DAI * 1 USD = 10,000 USD

所以:

liquidity = 15,000 - 10,000 = 5,000 USD
shortfall = 0

Alice 还能借,允许。


6. borrow 后状态变化

借款成功后:

cDAI cash:
  减少 10,000 DAI

cDAI totalBorrows:
  增加 10,000 DAI

Alice accountBorrows[cDAI]:
  principal = 10,000 DAI
  interestIndex = 当前 cDAI borrowIndex

Alice 钱包:
  收到 10,000 DAI

注意,Alice 的 cETH 余额没变:

Alice 仍然持有 500 cETH

但这 500 cETH 正在支撑她的 DAI 债务,不能随便全部取走或转走。


7. 第四步:市场计息

过了一段时间,有人和 cDAI 市场交互,触发:

cDAI.accrueInterest()

源码路径

accrueInterest()
  -> get currentBlockNumber
  -> get cashPrior
  -> get totalBorrows
  -> get totalReserves
  -> interestRateModel.getBorrowRate(cash, borrows, reserves)
  -> blockDelta = currentBlock - accrualBlockNumber
  -> simpleInterestFactor = borrowRate * blockDelta
  -> interestAccumulated = simpleInterestFactor * totalBorrows
  -> totalBorrowsNew = totalBorrows + interestAccumulated
  -> totalReservesNew = reserves + interestAccumulated * reserveFactor
  -> borrowIndexNew = borrowIndex * (1 + simpleInterestFactor)

假设 Alice 借款后,市场产生了一些利息。

原来 Alice 快照:

principal = 10,000 DAI
interestIndex = 1.00

现在:

borrowIndex = 1.05

Alice 当前债务变成:

borrowBalance = 10,000 * 1.05 / 1.00
              = 10,500 DAI

注意,Alice 的 accountBorrows.principal 可能还没被直接更新。 但通过 borrowIndex,她的当前债务已经能算出来。


8. 第五步:Alice 部分还款

Alice 调用:

cDAI.repayBorrow(2000e18)

也就是还 2,000 DAI。

源码路径

cDAI.repayBorrow(2000)
  -> repayBorrowInternal(2000)
    -> accrueInterest()
    -> repayBorrowFresh(
         payer = Alice,
         borrower = Alice,
         repayAmount = 2000
       )

repayBorrowFresh 主流程

1. Comptroller.repayBorrowAllowed(cDAI, Alice, Alice, 2000)
2. 检查 cDAI 市场 fresh
3. 计算 Alice 当前债务
4. repayAmountFinal = 2000
5. doTransferIn(Alice, 2000)
6. actualRepayAmount = 实际到账
7. accountBorrowsNew = accountBorrowsPrev - actualRepayAmount
8. totalBorrowsNew = totalBorrows - actualRepayAmount
9. 更新 Alice BorrowSnapshot
10. 更新 totalBorrows
11. emit RepayBorrow

假设还款前 Alice 当前债务:

10,500 DAI

还款:

2,000 DAI

还款后:

accountBorrowsNew = 10,500 - 2,000
                  = 8,500 DAI

新的快照:

principal = 8,500 DAI
interestIndex = 当前 borrowIndex

如果当前 borrowIndex 是 1.05,那么:

Alice accountBorrows[cDAI]:
  principal = 8,500
  interestIndex = 1.05

后续利息从这个新快照继续增长。


9. 第六步:ETH 价格下跌

现在市场变化:

ETH price 从 2000 USD 跌到 1000 USD

Alice 抵押还是:

500 cETH
exchangeRate = 0.02 ETH / cETH
underlying = 10 ETH

新的抵押市值:

10 ETH * 1000 USD = 10,000 USD

乘抵押因子:

10,000 * 75% = 7,500 USD

Alice 当前 DAI 债务假设因为继续计息,已经变成:

8,800 DAI

借款价值:

8,800 USD

账户状态:

collateral value = 7,500 USD
borrow value = 8,800 USD

shortfall = 8,800 - 7,500
          = 1,300 USD

Alice 账户不健康,可以被清算。


10. Bob 检查 Alice 是否可清算

Bob 或清算机器人会调用:

Comptroller.getAccountLiquidity(Alice)

返回类似:

liquidity = 0
shortfall = 1,300 USD

这说明 Alice 可以被清算。

清算机器人还会计算:

最多能清算多少?
清算能拿多少 cETH?
扣掉 gas 和滑点是否赚钱?

11. 第七步:Bob 清算 Alice

Bob 调用:

cDAI.liquidateBorrow(
  Alice,
  4000e18,
  cETH
)

意思是:

Bob 在 cDAI 市场替 Alice 还 4,000 DAI,
然后拿走 Alice 的 cETH 抵押品。

注意,调用发生在 借款市场 cDAI 上,而不是 cETH。


清算源码路径

cDAI.liquidateBorrow(Alice, 4000, cETH)
  -> liquidateBorrowInternal(Alice, 4000, cETH)
    -> cDAI.accrueInterest()
    -> cETH.accrueInterest()
    -> liquidateBorrowFresh(Bob, Alice, 4000, cETH)

清算必须让两个市场都 fresh:

cDAI fresh:
  Alice 当前债务准确

cETH fresh:
  cETH exchangeRate 准确

12. liquidateBorrowAllowed 检查

liquidateBorrowFresh 第一件大事是:

Comptroller.liquidateBorrowAllowed(
  cDAI,
  cETH,
  Bob,
  Alice,
  4000
)

Comptroller 会检查:

1. cDAI 市场 listed
2. cETH 市场 listed
3. 清算未暂停
4. Alice shortfall > 0
5. repayAmount <= closeFactor * Alice 当前 DAI 债务
6. Oracle 价格有效

假设 Alice 当前债务:

8,800 DAI

closeFactor:

50%

单次最多清算:

maxClose = 8,800 * 50%
         = 4,400 DAI

Bob 想还:

4,000 DAI

满足:

4,000 <= 4,400

允许清算。


13. Bob 替 Alice 还款

清算内部调用:

repayBorrowFresh(
  payer = Bob,
  borrower = Alice,
  repayAmount = 4000
)

也就是:

Bob 付款
Alice 债务减少

流程:

1. doTransferIn(Bob, 4000 DAI)
2. actualRepayAmount = 4000 DAI
3. Alice accountBorrowsNew = 8,800 - 4,000 = 4,800 DAI
4. cDAI totalBorrows 减少 4,000 DAI
5. 更新 Alice BorrowSnapshot

此时 Alice 的 DAI 债务下降了。

但清算还没结束,Bob 还要拿抵押品。


14. 计算 seizeTokens

调用:

Comptroller.liquidateCalculateSeizeTokens(
  cDAI,
  cETH,
  actualRepayAmount
)

公式:

seizeTokens =
  actualRepayAmount
  * liquidationIncentive
  * borrowedAssetPrice
  / collateralAssetPrice
  / collateralExchangeRate

参数:

actualRepayAmount = 4,000 DAI
liquidationIncentive = 1.08
DAI price = 1 USD
ETH price = 1000 USD
cETH exchangeRate = 0.02 ETH / cETH

先算 Bob 应得抵押品 underlying 价值:

4,000 * 1.08 = 4,320 USD

换成 ETH:

4,320 / 1000 = 4.32 ETH

换成 cETH:

4.32 / 0.02 = 216 cETH

所以:

seizeTokens = 216 cETH

15. seize:转移 Alice 的 cETH 给 Bob

清算调用:

cETH.seize(
  Bob,
  Alice,
  216 cETH
)

进入:

cETH.seizeInternal(...)
  -> Comptroller.seizeAllowed(...)
  -> accountTokens[Alice] -= 216
  -> accountTokens[Bob] += 216
  -> emit Transfer(Alice, Bob, 216)

操作前:

Alice cETH = 500
Bob cETH = 0

操作后:

Alice cETH = 284
Bob cETH = 216

cETH totalSupply 通常不变,因为只是从 Alice 转给 Bob。

清算事件:

emit LiquidateBorrow(
  liquidator = Bob,
  borrower = Alice,
  actualRepayAmount = 4000 DAI,
  cTokenCollateral = cETH,
  seizeTokens = 216 cETH
)

16. 清算后 Alice 状态

Alice 债务:

从 8,800 DAI 降到 4,800 DAI

Alice 抵押:

从 500 cETH 降到 284 cETH

对应 ETH:

284 * 0.02 = 5.68 ETH

当前抵押市值:

5.68 ETH * 1000 USD = 5,680 USD

可借抵押价值:

5,680 * 75% = 4,260 USD

Alice 借款价值:

4,800 USD

Alice 可能仍然有 shortfall:

4,800 - 4,260 = 540 USD

所以 Alice 还可能继续被清算。

这就是为什么 closeFactor 限制下,一个坏账账户可能需要多次清算。


17. Bob 拿到的是 cETH,不是 ETH

Bob 此时拿到:

216 cETH

不是直接拿到 ETH。

这些 cETH 对应 underlying:

216 * 0.02 = 4.32 ETH

Bob 如果想拿 ETH,需要调用:

cETH.redeem(216 cETH)

或:

cETH.redeemUnderlying(4.32 ETH)

18. 第八步:Bob redeem cETH

Bob 调用:

cETH.redeem(216)

源码路径

cETH.redeem(216)
  -> redeemInternal(216)
    -> accrueInterest()
    -> redeemFresh(Bob, redeemTokensIn = 216, redeemAmountIn = 0)

redeemFresh 做什么?

1. 检查 cETH fresh
2. 计算 exchangeRate
3. redeemAmount = 216 * 0.02 = 4.32 ETH
4. Comptroller.redeemAllowed(cETH, Bob, 216)
5. 检查 cETH cash >= 4.32 ETH
6. totalSupply -= 216
7. accountTokens[Bob] -= 216
8. doTransferOut(Bob, 4.32 ETH)
9. emit Redeem
10. emit Transfer(Bob, address(0), 216)

如果 Bob 没有借款,redeemAllowed 通常没问题。 但仍然要检查:

cETH cash >= 4.32 ETH

如果 cETH 市场现金不足,Bob 可能无法马上 redeem。

这就是清算人的流动性风险。


19. 这条完整链路涉及哪些模块?

这一整套故事里,所有核心模块都出现了:

CEther / CErc20:
  用户入口

CToken:
  mint / redeem / borrow / repay / liquidate 核心执行

Comptroller:
  enterMarkets
  borrowAllowed
  redeemAllowed
  liquidateBorrowAllowed
  seizeAllowed
  getAccountLiquidity

InterestRateModel:
  accrueInterest 中计算 borrowRate

PriceOracle:
  计算 ETH / DAI 价值
  判断 shortfall
  计算 seizeTokens

ExponentialNoError:
  exchangeRate
  collateralFactor
  liquidationIncentive
  rate
  index

Proxy:
  cDAI Delegator / Delegate
  Unitroller / Comptroller implementation

Governance:
  管理 collateralFactor、closeFactor、oracle、interestRateModel 等参数

COMP Flywheel:
  mint / borrow / repay / transfer 时可能顺便更新 COMP 奖励

你看,Compound v2 的一次“借贷 + 清算”不是单个函数,而是一组模块协作。


20. 用一张总流程图串起来

Alice mint ETH
 |
 v
cETH.mint
 |
 |-- accrueInterest
 |-- Comptroller.mintAllowed
 |-- mint cETH to Alice
 |
 v
Alice enterMarkets([cETH])
 |
 v
Comptroller
 |
 |-- markets[cETH].accountMembership[Alice] = true
 |-- accountAssets[Alice].push(cETH)
 |
 v
Alice borrow DAI
 |
 v
cDAI.borrow
 |
 |-- accrueInterest
 |-- Comptroller.borrowAllowed
 |     |-- Oracle ETH price
 |     |-- Oracle DAI price
 |     |-- cETH exchangeRate
 |     |-- collateralFactor
 |     |-- getHypotheticalAccountLiquidity
 |
 |-- cash check
 |-- update Alice BorrowSnapshot
 |-- transfer DAI to Alice
 |
 v
ETH price falls
 |
 v
Comptroller.getAccountLiquidity(Alice)
 |
 |-- shortfall > 0
 |
 v
Bob liquidates
 |
 v
cDAI.liquidateBorrow(Alice, repayAmount, cETH)
 |
 |-- cDAI.accrueInterest
 |-- cETH.accrueInterest
 |-- Comptroller.liquidateBorrowAllowed
 |     |-- shortfall check
 |     |-- closeFactor check
 |
 |-- repayBorrowFresh(Bob, Alice, repayAmount)
 |-- Comptroller.liquidateCalculateSeizeTokens
 |     |-- DAI price
 |     |-- ETH price
 |     |-- liquidationIncentive
 |     |-- cETH exchangeRate
 |
 |-- cETH.seize(Bob, Alice, seizeTokens)
 |
 v
Bob gets cETH
 |
 v
Bob redeem cETH
 |
 |-- cETH.accrueInterest
 |-- Comptroller.redeemAllowed
 |-- cash check
 |-- burn Bob cETH
 |-- transfer ETH to Bob

这张图就是 Compound v2 的核心运行闭环。


21. 状态变量变化总表

Alice mint ETH

cETH.cash                  ↑
cETH.totalSupply           ↑
cETH.accountTokens[Alice]  ↑

Alice enterMarkets

Comptroller.markets[cETH].accountMembership[Alice] = true
Comptroller.accountAssets[Alice].push(cETH)

Alice borrow DAI

cDAI.cash                         ↓
cDAI.totalBorrows                 ↑
cDAI.accountBorrows[Alice].principal      ↑
cDAI.accountBorrows[Alice].interestIndex  = current borrowIndex
Alice DAI wallet balance          ↑

cDAI accrueInterest

cDAI.totalBorrows        ↑
cDAI.totalReserves       ↑
cDAI.borrowIndex         ↑
cDAI.accrualBlockNumber  = current block

Alice repay DAI

cDAI.cash                         ↑
cDAI.totalBorrows                 ↓
cDAI.accountBorrows[Alice].principal      ↓
cDAI.accountBorrows[Alice].interestIndex  = current borrowIndex
Alice DAI wallet balance          ↓

ETH price falls

No CToken storage necessarily changes
Oracle price changes
Comptroller account liquidity result changes

这一点非常关键:

价格变动可能不改用户仓位,
但会改变用户是否可被清算。

Bob liquidates Alice

借款市场 cDAI:

cDAI.cash                         ↑
cDAI.totalBorrows                 ↓
cDAI.accountBorrows[Alice].principal      ↓
cDAI.accountBorrows[Alice].interestIndex  = current borrowIndex
Bob DAI wallet balance            ↓

抵押市场 cETH:

cETH.accountTokens[Alice]  ↓
cETH.accountTokens[Bob]    ↑
cETH.totalSupply           通常不变

Bob redeem cETH

cETH.cash                ↓
cETH.totalSupply         ↓
cETH.accountTokens[Bob]  ↓
Bob ETH balance          ↑

22. 事件总表

这些操作会触发事件:

Alice mint:
  Mint
  Transfer(address(0), Alice, mintTokens)

Alice borrow:
  Borrow

Alice repay:
  RepayBorrow

Bob liquidate:
  RepayBorrow
  Transfer(Alice, Bob, seizeTokens)
  LiquidateBorrow

Bob redeem:
  Transfer(Bob, address(0), redeemTokens)
  Redeem

看真实链上交易时,事件是非常好的线索。

比如清算交易里你通常会看到:

RepayBorrow
Transfer collateral cToken from borrower to liquidator
LiquidateBorrow

如果清算人随后 redeem,还会看到:

Redeem
Transfer cToken to address(0)

23. 三个最核心的“价值换算”

整个 Compound v2 可以压缩成三个换算。

供应侧换算

underlying <-> cToken

公式:

mintTokens = mintAmount / exchangeRate

redeemAmount = redeemTokens * exchangeRate

风控侧换算

cToken -> underlying -> USD-ish value -> collateral value

公式:

collateralValue =
  cTokenBalance
  * exchangeRate
  * oraclePrice
  * collateralFactor

借款价值:

borrowValue =
  borrowBalance
  * oraclePrice

清算侧换算

repaid borrowed asset -> seized collateral cToken

公式:

seizeTokens =
  actualRepayAmount
  * liquidationIncentive
  * borrowedAssetPrice
  / collateralAssetPrice
  / collateralExchangeRate

把这三个换算吃透,Compound v2 80% 的核心逻辑就稳了。


24. 三个核心 index

Compound v2 里有三个非常重要的 index 思想。

borrowIndex

用于借款利息:

borrowBalance =
  principal * currentBorrowIndex / snapshotInterestIndex

exchangeRate

用于供应者收益:

underlyingBalance =
  cTokenBalance * exchangeRate

COMP reward index

用于 COMP 分发:

supplierReward =
  supplierBalance * (globalSupplyIndex - supplierIndex)

borrowerReward =
  borrowBalance * (globalBorrowRewardIndex - borrowerIndex)

它们的共同思想是:

不遍历所有用户;
维护一个全局增长指标;
用户交互时按快照差值结算。

这是 Compound v2 最漂亮的设计模式之一。


25. 为什么这套架构可扩展?

因为 Compound 避免了链上最贵的东西:

遍历所有用户

它没有:

for each supplier:
  update supplier interest

for each borrower:
  update borrower debt

for each comp recipient:
  distribute COMP

而是用:

global index + user snapshot

所以无论有 100 个用户还是 100 万个用户,单次用户操作主要只处理当前用户和当前市场。

这就是 DeFi 协议可扩展性的核心技巧。


26. 一笔真实 borrow 交易应该怎么看?

以后你看链上 borrow 交易,可以按这个顺序解读:

1. 是哪个 cToken 市场?
   例如 cDAI

2. 借了多少 underlying?
   borrowAmount

3. 交易前有没有 AccrueInterest?
   看 AccrueInterest 事件或状态变化

4. Comptroller 是否允许?
   如果成功,说明 borrowAllowed 过了

5. 账户抵押是什么?
   查 accountAssets[user]

6. 借款后 accountBorrows 怎么变?
   principal / interestIndex

7. totalBorrows 怎么变?
   totalBorrows += borrowAmount

8. cash 怎么变?
   cash -= borrowAmount

9. 事件 Borrow 里的 accountBorrowsNew 是多少?

如果 borrow 失败,常见原因:

账户抵押不足
市场 cash 不足
价格无效
borrow paused
市场未 listed
accrueInterest 失败

27. 一笔真实 liquidation 交易应该怎么看?

清算交易更复杂,按这个顺序:

1. borrowed market 是哪个?
   调用哪个 cToken 的 liquidateBorrow

2. collateral market 是哪个?
   参数 cTokenCollateral

3. borrower 是谁?
   被清算账户

4. liquidator 是谁?
   msg.sender

5. repayAmount 是多少?
   清算人想还多少

6. actualRepayAmount 是多少?
   看 RepayBorrow 事件

7. seizeTokens 是多少?
   看 LiquidateBorrow 事件

8. borrower collateral cToken 是否减少?
   看 Transfer(borrower, liquidator, seizeTokens)

9. 是否随后 redeem?
   看 liquidator 是否调用 collateral cToken redeem

10. 清算是否有利润?
   比较 repay 成本、seized collateral 价值、gas、滑点

清算失败常见原因:

borrower 还没有 shortfall
repayAmount 超过 closeFactor
repayAmount 为 0 或 uint(-1)
价格无效
allowance 不够
清算市场 paused
抵押市场不 fresh
借款市场不 fresh

28. Compound v2 学习总地图

到现在,我们已经把主要模块都走过了:

1. 总架构
2. mint / supply
3. accrueInterest
4. borrow
5. repayBorrow
6. redeem / withdraw
7. liquidateBorrow
8. Comptroller
9. InterestRateModel
10. PriceOracle
11. ExponentialNoError
12. Proxy / Upgrade
13. Governance
14. COMP Flywheel
15. 安全设计
16. 完整交易 walkthrough

这已经是一套相当完整的 Compound v2 源码学习路径。


29. 如果继续深入,下一阶段学什么?

下一阶段可以从“读懂源码”进入“实战验证”。

我建议后面按这几个方向继续:

方向一:搭本地测试环境
  fork mainnet
  部署 mock token / mock oracle
  跑 mint / borrow / liquidate 测试

方向二:逐函数对照真实源码
  打开 CToken.sol
  逐行读 mintFresh / borrowFresh / liquidateBorrowFresh

方向三:读测试文件
  Compound v2 的测试比注释更像说明书

方向四:做一笔主网交易解析
  找一笔真实 liquidation
  用事件和状态变化还原全过程

方向五:审计一个 Compound fork
  检查 oracle、decimals、market listing、storage layout、admin 权限

30. 第十六讲要记住的 8 个结论

第一,一次完整借贷生命周期会跨越 CToken、Comptroller、Oracle、InterestRateModel、Math、Proxy 等多个模块。

第二,供应 ETH 后,只有调用 enterMarkets,cETH 才会作为抵押品进入账户健康度计算。

第三,borrow 的核心是:

先计息
再问 Comptroller
再检查 cash
再更新 BorrowSnapshot
最后转出 underlying

第四,价格变化本身不一定改变用户仓位,但会改变账户 liquidity / shortfall。

第五,清算本质是:

repayBorrowFresh + liquidateCalculateSeizeTokens + seize

第六,清算人拿到的是抵押品 cToken,不一定能立刻 redeem underlying。

第七,Compound v2 的核心模式是:

全局 index + 用户 snapshot

第八,真实交易分析要从事件、状态变量、Comptroller 检查和 Oracle 价格四个角度一起看。


到这里,Compound v2 的主线已经完整闭环了。后续最值得做的是:拿一份真实源码,从 CToken.solmintFresh 开始逐行对照你已经学过的模型读。

点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
tomenengr
tomenengr
区块链工程专业学生一枚,喜欢倒腾ai应用,努力学习区块链ing