目录
- 为什么 DeFi 借贷协议需要动态利率
- 先理解借贷池中的三类资金
- 资金利用率的计算方法
- 利用率为什么能调节市场供需
- 借款利率与存款利率的关系
- 为什么存款利率必须乘以利用率
- 储备因子与协议收入
- Aave V3 的分段线性拐点模型
- 使用 DAI 参数完整计算借款利率
- 完整计算存款利率与协议收入
- 利用率变化时各参与者会发生什么
- Aave V3 与 Compound V2 的共同点和差异
- Solidity 实现时的定点数与精度问题
- 常见误区
- 总结
1. 为什么 DeFi 借贷协议需要动态利率
在传统金融中,中央银行、商业银行和市场共同影响利率。银行可以人工调整贷款利率,也可以通过内部风控决定不同客户的利率。
DeFi 借贷协议没有银行柜员,也不会为每一笔贷款单独开会定价。智能合约需要根据链上的资金状态,自动计算当前利率。
Aave V3 和 Compound V2 的核心思路可以概括为:
池子里的钱越紧张,借款利率越高;池子里的闲钱越多,借款利率越低。
协议通过提高或降低利率,引导用户改变行为:
- 资金紧张时,提高借款成本,促使借款人还款,同时吸引更多存款人提供资金。
- 闲置资金过多时,降低借款成本,鼓励更多用户借款,同时减少协议需要支付的存款利息。
这个自动调节过程的核心指标就是资金利用率。
2. 先理解借贷池中的三类资金
为了简化理解,假设某个 USDC 借贷池中共有以下资金:
- 存款人一共存入 1,000,000 USDC。
- 借款人已经借走 700,000 USDC。
- 合约中还剩 300,000 USDC 可以被借出或取走。
这里可以区分三个概念:
| 概念 | 数量 | 含义 |
|---|---|---|
| 总流动性 | 1,000,000 USDC | 存款人提供的全部可借资金 |
| 已借资金 | 700,000 USDC | 当前正在被借款人使用的资金 |
| 可用流动性 | 300,000 USDC | 暂时没有被借走的资金 |
需要特别注意:
存款人看到的账户余额仍然可能是 1,000,000 USDC 加上累计利息,但这些资金并不是全部以现金形式躺在合约里。其中 700,000 USDC 已经借给了借款人。
如果所有存款人同时要求取款,协议只能立即拿出当前可用的 300,000 USDC。剩余资金需要等待借款人还款,或者等待新的存款进入。
3. 资金利用率的计算方法
最直观的利用率公式是:
资金利用率 U = 已借资金 ÷ 总资金
因为:
总资金 = 已借资金 + 可用流动性
所以也可以写成:
U = 已借资金 ÷(已借资金 + 可用流动性)
继续使用前面的例子:
已借资金 = 700,000 USDC
可用流动性 = 300,000 USDC
U = 700,000 ÷ (700,000 + 300,000)
= 700,000 ÷ 1,000,000
= 70%
3.1 借款不变,新增存款会怎样
假设又有用户存入 400,000 USDC,但借款仍然是 700,000 USDC:
已借资金 = 700,000 USDC
可用流动性 = 700,000 USDC
U = 700,000 ÷ 1,400,000
= 50%
借款数量没有减少,但由于可用资金增加,池子不再那么紧张,利用率从 70% 降到了 50%。
3.2 存款不变,借款增加会怎样
假设总资金仍然是 1,000,000 USDC,但借款从 700,000 增加到 900,000 USDC:
U = 900,000 ÷ 1,000,000
= 90%
此时池子中只剩 100,000 USDC。协议需要提高利率,避免资金被全部借光。
4. 利用率为什么能调节市场供需
利用率不是一个单纯用于展示的数据,它会直接影响借款利率。
4.1 利用率上升
利用率上升说明借款需求旺盛,池子中的可用资金正在减少。
协议通常会提高借款利率,产生以下效果:
- 新借款人发现借款成本变高,可能放弃借款。
- 已有借款人需要支付更多利息,因此更愿意提前还款。
- 存款利率通常也会提高,吸引更多存款人提供资金。
- 还款和新增存款都会增加可用流动性。
- 可用流动性增加后,利用率下降。
4.2 利用率下降
利用率下降说明池子里有大量资金没有被使用。
协议通常会降低借款利率,产生以下效果:
- 借款成本降低,吸引更多用户借款。
- 存款利率也会降低,减少资金继续涌入的动力。
- 借款增加或存款减少后,闲置资金下降。
- 利用率重新上升。
因此,利率模型实际上是一个自动反馈系统。
5. 借款利率与存款利率的关系
借款人支付的利息,是存款人收益和协议收入的来源。
暂时忽略协议收费时,存款利率可以近似表示为:
存款利率 = 借款利率 × 资金利用率
假设:
借款利率 = 10%
资金利用率 = 50%
那么:
存款利率 = 10% × 50%
= 5%
存款人获得的年化利率是 5%,而不是 10%。
6. 为什么存款利率必须乘以利用率
假设:
- Alice 向协议存入 100 USDC。
- Bob 从协议借走 50 USDC。
- 借款年利率是 10%。
- 暂时忽略复利和协议收费。
一年后 Bob 需要支付的利息为:
50 × 10% = 5 USDC
协议只从借款人那里赚到了 5 USDC,因此最多只能把这 5 USDC 分给存款人。
Alice 最初存入了 100 USDC,她获得 5 USDC 利息,对应的收益率为:
5 ÷ 100 = 5%
这也等于:
借款利率 × 利用率
= 10% × (50 ÷ 100)
= 10% × 50%
= 5%
如果协议不乘以利用率,直接承诺给 Alice 10%:
协议应付 Alice 的利息 = 100 × 10% = 10 USDC
协议从 Bob 收到的利息 = 50 × 10% = 5 USDC
资金缺口 = 10 - 5 = 5 USDC
协议凭空多欠了 5 USDC,这种模式无法长期运行。
7. 储备因子与协议收入
实际协议不会把全部借款利息都分给存款人。协议通常会保留一部分收入,用于国库、风险储备或其他治理用途。
这一比例叫作储备因子:
Reserve Factor
加入储备因子后,存款利率公式变为:
存款利率
= 借款利率 × 资金利用率 ×(1 - 储备因子)
假设:
借款利率 = 10%
资金利用率 = 50%
储备因子 = 20%
分三步计算:
第一步:所有借款利息能够支撑的毛存款利率
10% × 50% = 5%
第二步:存款人可以获得的比例
1 - 20% = 80%
第三步:存款人最终获得的利率
5% × 80% = 4%
最终:
- 存款人年化利率为 4%。
- 对应 1% 的总存款本金收益率进入协议。
- 这里的 1% 等于 5% 毛收益的 20%,不是借款本金的 20%。
8. Aave V3 的分段线性拐点模型
如果借款利率从 0% 利用率到 100% 利用率始终以相同速度增长,就无法在资金即将耗尽时产生足够强的制动力。
因此,Aave V3 使用带拐点的分段线性模型。
协议首先设置一个最优利用率:
U_optimal
利率曲线被分成两段:
- 第一段:利用率不超过最优利用率,利率缓慢上升。
- 第二段:利用率超过最优利用率,利率快速上升。
8.1 第一段:正常区间
当:
U ≤ U_optimal
借款利率为:
R_borrow
= R_base + (U ÷ U_optimal) × R_slope1
参数含义:
| 参数 | 含义 |
|---|---|
| U | 当前资金利用率 |
| U_optimal | 协议希望维持的最优利用率 |
| R_base | 利用率为 0 时的基础借款利率 |
| R_slope1 | 从 0 到最优利用率区间内增加的利率 |
这里必须先计算 U ÷ U_optimal,因为协议需要知道当前利用率已经走完正常区间的百分之多少。
8.2 第二段:高风险区间
当:
U > U_optimal
借款利率为:
R_borrow
= R_base
+ R_slope1
+ ((U - U_optimal) ÷ (1 - U_optimal)) × R_slope2
超过拐点后:
R_base + R_slope1是到达拐点时已经产生的利率。U - U_optimal是超过拐点的利用率。1 - U_optimal是拐点之后剩余的完整区间。- 两者相除,可以得到当前已经走完高风险区间的百分之多少。
- 再乘以
R_slope2,得到高风险区间新增的利率。
R_slope2 通常明显大于 R_slope1,所以曲线在拐点后会陡峭上升。
9. 使用 DAI 参数完整计算借款利率
原文使用的一组 Aave DAI 利率参数如下:
U_optimal = 80%
R_base = 0%
R_slope1 = 4%
R_slope2 = 75%
这些参数表达的含义是:
- 利用率从 0% 增长到 80% 时,借款利率从 0% 缓慢增长到 4%。
- 利用率从 80% 增长到 100% 时,借款利率会在已有 4% 的基础上继续增加最多 75%。
- 当利用率达到 100% 时,借款利率为 79%。
9.1 利用率为 40%
40% 没有超过 80%,使用第一段公式:
R_borrow
= 0% + (40% ÷ 80%) × 4%
= 0% + 50% × 4%
= 2%
此时借款年利率为 2%。
直观理解:
40% 正好走完了正常区间的一半,所以也只使用了 R_slope1 的一半。
9.2 利用率为 80%
80% 正好位于拐点:
R_borrow
= 0% + (80% ÷ 80%) × 4%
= 4%
此时借款年利率为 4%。
9.3 利用率为 90%
90% 超过了 80%,使用第二段公式。
第一步,计算超过拐点的部分:
90% - 80% = 10%
第二步,计算拐点之后的完整区间:
100% - 80% = 20%
第三步,计算已经走完高风险区间的比例:
10% ÷ 20% = 50%
第四步,计算第二段新增利率:
50% × 75% = 37.5%
第五步,加上拐点处已有的利率:
R_borrow
= 0% + 4% + 37.5%
= 41.5%
利用率只是从 80% 增加到了 90%,借款利率却从 4% 增加到了 41.5%。
这不是计算错误,而是协议故意制造的强烈经济激励:
- 借款人会发现继续占用资金非常昂贵。
- 部分借款人会尽快还款。
- 高利率也会推高存款收益,吸引新资金进入。
9.4 利用率为 95%
超过拐点的利用率 = 95% - 80% = 15%
高风险区间总长度 = 100% - 80% = 20%
已走完高风险区间 = 15% ÷ 20% = 75%
第二段新增利率 = 75% × 75% = 56.25%
R_borrow = 0% + 4% + 56.25%
= 60.25%
此时借款年利率为 60.25%。
9.5 利用率为 100%
超过拐点的利用率 = 100% - 80% = 20%
已走完高风险区间 = 20% ÷ 20% = 100%
第二段新增利率 = 100% × 75% = 75%
R_borrow = 0% + 4% + 75%
= 79%
此时借款年利率为 79%。
把几个关键点放在一起:
| 利用率 | 借款年利率 |
|---|---|
| 0% | 0% |
| 40% | 2% |
| 80% | 4% |
| 90% | 41.5% |
| 95% | 60.25% |
| 100% | 79% |
可以清楚看到,80% 是整个曲线的关键拐点。
10. 完整计算存款利率与协议收入
继续使用下面的状态:
总存款 = 1,000,000 DAI
已借资金 = 900,000 DAI
资金利用率 = 90%
借款利率 = 41.5%
储备因子 = 10%
10.1 计算存款利率
存款利率
= 41.5% × 90% × (1 - 10%)
= 41.5% × 90% × 90%
= 33.615%
存款人的理论年化利率约为 33.615%。
10.2 使用实际金额验证
借款人一年产生的总利息:
900,000 × 41.5% = 373,500 DAI
协议按 10% 的储备因子保留:
373,500 × 10% = 37,350 DAI
分给存款人的利息:
373,500 - 37,350 = 336,150 DAI
相对于 1,000,000 DAI 总存款:
336,150 ÷ 1,000,000 = 33.615%
结果与公式完全一致。
10.3 不要把高年化理解为固定收益
41.5% 和 33.615% 是当前资金状态对应的年化速率,不代表该利率会持续一年。
如果高利率促使借款人大量还款,利用率可能在几个区块后下降,借款利率和存款利率也会随之下降。
因此,界面显示的 APY 或 APR 通常是基于当前状态推算的结果,不是协议对未来一年的收益承诺。
11. 利用率变化时各参与者会发生什么
11.1 利用率从 50% 上升到 80%
- 借款需求增加。
- 可供取款的流动性减少。
- 借款利率逐渐上升。
- 存款利率也逐渐上升。
- 市场仍处于协议认为相对正常的区间。
11.2 利用率从 80% 上升到 90%
- 可用资金快速减少。
- 借款利率进入陡峭区间。
- 新借款变得非常昂贵。
- 已有借款人受到更强的还款激励。
- 存款人受到更强的存款激励。
11.3 利用率接近 100%
- 几乎没有可用资金。
- 存款人可能暂时无法足额取款。
- 借款利率接近模型设定的最高值。
- 协议通过极高利率试图恢复流动性。
高利用率不等于协议已经资不抵债。
它首先表示流动性紧张:资产可能仍然存在于借款人的抵押贷款头寸中,但没有足够的空闲现金满足所有即时取款。
12. Aave V3 与 Compound V2 的共同点和差异
12.1 共同点
Aave V3 和 Compound V2 在本文讨论的核心经济逻辑上非常接近:
- 使用资金利用率衡量资金紧张程度。
- 利用率决定借款利率。
- 借款利率结合利用率,决定存款人能够获得的收益。
- 协议可以通过储备因子保留部分利息收入。
- 使用带拐点的曲线,使高利用率区间的利率更快上升。
12.2 差异
两者在具体合约结构、变量命名、每区块或每秒的计息方式、参数管理和指数累计方式上存在实现差异。
学习时应该分成两个层次:
第一层是经济模型:
利用率 → 借款利率 → 存款利率
第二层是合约实现:
如何保存参数
如何计算每秒或每区块利率
如何累计借款指数
如何累计存款指数
如何处理定点数精度
不要因为实现代码不同,就误以为两者的核心经济逻辑完全不同。
另外,Compound V3 的存款利率模型与 Compound V2 不完全相同。Compound V3 可以直接根据利用率计算存款利率,而不是简单地使用“借款利率乘以利用率”这一形式。
13. Solidity 实现时的定点数与精度问题
Solidity 没有原生浮点数,不能直接保存 0.8、0.04 或 0.75。
Aave 会使用高精度定点数表示比例。原文提到相关参数使用 27 位小数精度的 Ray:
1 = 10^27
0.8 = 8 × 10^26
0.04 = 4 × 10^25
0.75 = 75 × 10^25
可以把它理解为:
合约先把所有小数统一放大 10^27 倍,使用整数完成计算,最后再按 10^27 的比例解释结果。
13.1 一个简化的定点数例子
为了方便展示,假设只使用 10^4 精度:
1.0000 = 10,000
0.8000 = 8,000
0.0400 = 400
计算:
0.4 ÷ 0.8 × 0.04
放大后分别是:
0.4 → 4,000
0.8 → 8,000
0.04 → 400
定点数乘法和除法需要主动恢复缩放比例,否则结果会多放大或少放大若干倍。
例如,两个都放大了 10,000 倍的数相乘:
(4,000 × 400) ÷ 10,000 = 160
160 按照 10,000 精度解释就是:
160 ÷ 10,000 = 0.016 = 1.6%
实际生产合约通常会使用经过审计的数学库处理 Ray 乘法、除法和舍入,避免重复编写容易出错的缩放逻辑。
13.2 审计时应重点检查
- 分段条件在
U == U_optimal时选择哪一段。 - 计算顺序是否导致整数除法提前截断。
- 乘法是否可能溢出。
- 缩放因子是否被遗漏或重复使用。
U_optimal是否可能被设置为 0 或 100%,从而造成除零。- 治理设置的
R_slope2是否过高或过低。 - 储备因子是否被正确限制在合法范围内。
- 利率是年化值、每秒值还是每区块值。
- 展示层的 APR 与 APY 转换是否正确。
14. 常见误区
14.1 利用率 90% 表示 90% 的用户正在借款
错误。
利用率衡量的是被借走的资金占总资金的比例,与用户数量没有直接关系。一个大户也可能借走池子中 90% 的资金。
14.2 借款利率 10%,存款利率也应该是 10%
错误。
只有被借走的资金才会产生借款利息,而且协议还可能保留一部分收入。存款利率通常低于借款利率。
14.3 储备因子 20% 表示协议拿走全部存款的 20%
错误。
协议拿走的是借款利息中的 20%,不是存款本金的 20%。
14.4 利用率越高越好
不完全正确。
高利用率说明资金使用效率高,但过高会导致存款人难以及时取款。协议需要在资金效率和可用流动性之间取得平衡。
14.5 100% 利用率等于协议破产
错误。
100% 利用率首先意味着可用流动性几乎为零,不一定意味着抵押资产不足。协议是否资不抵债,还要看借款人的抵押品价值、清算机制和坏账情况。
14.6 当前显示 30% APY,就一定能获得一年 30% 收益
错误。
利率会随着利用率不断变化。当前显示值只是按照当前状态进行的年化换算。
15. 总结
Aave V3 和 Compound V2 利率模型可以浓缩为以下链路:
借款需求变化
↓
资金利用率变化
↓
借款利率变化
↓
借款人和存款人的行为变化
↓
资金利用率重新趋向合理区间
最重要的四个公式是:
1. 资金利用率
U = 已借资金 ÷(已借资金 + 可用流动性)
2. 拐点之前的借款利率
R_borrow
= R_base + (U ÷ U_optimal) × R_slope1
3. 拐点之后的借款利率
R_borrow
= R_base
+ R_slope1
+ ((U - U_optimal) ÷ (1 - U_optimal)) × R_slope2
4. 考虑储备因子后的存款利率
R_supply
= R_borrow × U × (1 - Reserve Factor)
理解这个模型时,不要只背公式。真正关键的是理解它背后的目标:
- 正常区间内,让资金保持较高使用效率。
- 资金紧张时,快速提高借款成本。
- 用高利率促使借款人还款并吸引新存款。
- 保留足够流动性,让存款人能够取回资金。
- 在存款人、借款人和协议三方之间合理分配借款利息。