Aave V3 利率模型与数学原理

1. 数学库基础

1.1 精度定义

Aave V3 使用两种高精度定点数来避免浮点数精度问题:

// WadRayMath.sol
library WadRayMath {
    uint256 internal constant WAD = 1e18;   // 18 位精度 (Wad)
    uint256 internal constant RAY = 1e27;   // 27 位精度 (Ray)
    uint256 internal constant WAD_RAY_RATIO = 1e9;  // 转换比率
 
    // 半精度值,用于四舍五入
    uint256 internal constant HALF_WAD = 0.5e18;
    uint256 internal constant HALF_RAY = 0.5e27;
}

为什么需要两种精度?

精度用途示例
WAD (1e18)代币金额、价格1.5 ETH = 1.5e18
RAY (1e27)利率、指数年利率 5% = 0.05e27

1.2 核心数学运算

// Ray 乘法 (带四舍五入)
function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    // 为避免溢出,先除后乘
    assembly {
        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
            revert(0, 0)
        }
        c := div(add(mul(a, b), HALF_RAY), RAY)
    }
}
 
// Ray 除法 (带四舍五入)
function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
    assembly {
        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
            revert(0, 0)
        }
        c := div(add(mul(a, RAY), div(b, 2)), b)
    }
}
 
// Wad 转 Ray
function wadToRay(uint256 a) internal pure returns (uint256 b) {
    assembly {
        b := mul(a, WAD_RAY_RATIO)
        if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
            revert(0, 0)
        }
    }
}
 
// Ray 转 Wad
function rayToWad(uint256 a) internal pure returns (uint256 b) {
    assembly {
        b := div(a, WAD_RAY_RATIO)
    }
}

1.3 百分比运算

// PercentageMath.sol
library PercentageMath {
    uint256 internal constant PERCENTAGE_FACTOR = 1e4;  // 100.00%
    uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
 
    // 百分比乘法: value * percentage / 100
    function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) {
        if (value == 0 || percentage == 0) {
            return 0;
        }
        return (value * percentage + HALF_PERCENTAGE_FACTOR) / PERCENTAGE_FACTOR;
    }
 
    // 百分比除法: value * 100 / percentage
    function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) {
        return (value * PERCENTAGE_FACTOR + percentage / 2) / percentage;
    }
}

百分比表示

  • 100% = 10000 (1e4)
  • 50% = 5000
  • 5% = 500
  • 0.01% = 1

2. 双斜率利率模型

2.1 模型概述

Aave V3 采用双斜率(Kinked)利率模型,根据资金利用率动态调整借贷利率:

利用率 (U) = 总借贷 / 总存款

             利率
              │
              │                          ╱
              │                        ╱
              │                      ╱ Slope2 (陡)
              │                    ╱
              │               ╱---╱ ← 拐点 (U_optimal)
              │           ╱
              │       ╱ Slope1 (缓)
              │   ╱
              │╱
              └────────────────────────── 利用率
                                  U_optimal    100%

2.2 数学公式

当 U ≤ U_optimal 时(正常区间):

BorrowRate = BaseRate + (U / U_optimal) × Slope1

当 U > U_optimal 时(高利用率区间):

ExcessUtilization = (U - U_optimal) / (1 - U_optimal)
BorrowRate = BaseRate + Slope1 + ExcessUtilization × Slope2

存款利率

SupplyRate = BorrowRate × U × (1 - ReserveFactor)

2.3 代码实现

// DefaultReserveInterestRateStrategy.sol
contract DefaultReserveInterestRateStrategy is IDefaultInterestRateStrategy {
    // 常量定义
    uint256 public immutable OPTIMAL_USAGE_RATIO;        // 最优利用率
    uint256 public immutable MAX_EXCESS_USAGE_RATIO;     // 1 - OPTIMAL_USAGE_RATIO
 
    // 利率参数
    uint256 internal immutable _baseVariableBorrowRate;  // 基础利率
    uint256 internal immutable _variableRateSlope1;      // 斜率1
    uint256 internal immutable _variableRateSlope2;      // 斜率2
    uint256 internal immutable _stableRateSlope1;        // 固定利率斜率1
    uint256 internal immutable _stableRateSlope2;        // 固定利率斜率2
    uint256 internal immutable _baseStableRateOffset;    // 固定利率偏移
 
    function calculateInterestRates(
        DataTypes.CalculateInterestRatesParams calldata params
    ) external view override returns (uint256, uint256, uint256) {
        uint256 vars.totalDebt = params.totalStableDebt + params.totalVariableDebt;
 
        // 计算利用率
        uint256 currentLiquidityRate;
        uint256 currentVariableBorrowRate;
        uint256 currentStableBorrowRate;
 
        uint256 availableLiquidity = IERC20(params.reserve).balanceOf(params.aToken) +
            params.liquidityAdded - params.liquidityTaken;
        uint256 totalLiquidity = availableLiquidity + vars.totalDebt;
 
        // 利用率 = 总债务 / (可用流动性 + 总债务)
        vars.currentUsageRatio = totalLiquidity == 0
            ? 0
            : vars.totalDebt.rayDiv(totalLiquidity);
 
        // 计算稳定利率
        currentStableBorrowRate = _calculateStableRate(vars.currentUsageRatio, vars.totalDebt);
 
        // 计算可变利率
        if (vars.currentUsageRatio > OPTIMAL_USAGE_RATIO) {
            // 超过最优利用率:使用陡峭的斜率2
            uint256 excessUsageRatio = (vars.currentUsageRatio - OPTIMAL_USAGE_RATIO).rayDiv(
                MAX_EXCESS_USAGE_RATIO
            );
            currentVariableBorrowRate = _baseVariableBorrowRate +
                _variableRateSlope1 +
                _variableRateSlope2.rayMul(excessUsageRatio);
        } else {
            // 正常利用率:使用平缓的斜率1
            currentVariableBorrowRate = _baseVariableBorrowRate +
                vars.currentUsageRatio.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_USAGE_RATIO);
        }
 
        // 计算存款利率
        // supplyRate = borrowRate × utilizationRate × (1 - reserveFactor)
        currentLiquidityRate = _getOverallBorrowRate(
            vars.totalDebt,
            params.totalStableDebt,
            params.totalVariableDebt,
            vars.currentAvgStableBorrowRate,
            currentVariableBorrowRate
        ).rayMul(vars.currentUsageRatio).percentMul(
            PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor
        );
 
        return (currentLiquidityRate, currentStableBorrowRate, currentVariableBorrowRate);
    }
}

2.4 典型参数配置

稳定币 (USDC/USDT/DAI)

参数说明
最优利用率90%高利用率容忍
基础利率0%无基础成本
Slope14%正常区间缓慢上升
Slope260%超过 90% 后急剧上升

主流资产 (ETH/WBTC)

参数说明
最优利用率80%中等利用率
基础利率0%无基础成本
Slope14%正常区间
Slope280%高利用率惩罚

波动性资产

参数说明
最优利用率45%低利用率目标
基础利率0%无基础成本
Slope14%正常区间
Slope2300%极高惩罚

3. 复利计算

3.1 线性复利(存款)

存款利息使用简单的线性复利:

// MathUtils.sol
function calculateLinearInterest(
    uint256 rate,        // 年利率 (RAY)
    uint40 lastUpdateTimestamp
) internal view returns (uint256) {
    uint256 result = rate * (block.timestamp - uint256(lastUpdateTimestamp));
    unchecked {
        // rate × time / SECONDS_PER_YEAR + 1
        result = result / SECONDS_PER_YEAR + WadRayMath.RAY;
    }
    return result;
}

公式

利息因子 = 1 + rate × Δt / SECONDS_PER_YEAR
新指数 = 旧指数 × 利息因子

3.2 复合复利(借贷)

借贷利息使用泰勒展开近似的复合复利:

function calculateCompoundedInterest(
    uint256 rate,
    uint40 lastUpdateTimestamp,
    uint256 currentTimestamp
) internal pure returns (uint256) {
    uint256 exp = currentTimestamp - uint256(lastUpdateTimestamp);
 
    if (exp == 0) {
        return WadRayMath.RAY;
    }
 
    uint256 expMinusOne;
    uint256 expMinusTwo;
    uint256 basePowerTwo;
    uint256 basePowerThree;
 
    unchecked {
        expMinusOne = exp - 1;
        expMinusTwo = exp > 2 ? exp - 2 : 0;
 
        basePowerTwo = rate.rayMul(rate) / (SECONDS_PER_YEAR * SECONDS_PER_YEAR);
        basePowerThree = basePowerTwo.rayMul(rate) / SECONDS_PER_YEAR;
    }
 
    // 泰勒展开: e^(rt) ≈ 1 + rt + (rt)²/2! + (rt)³/3!
    uint256 secondTerm = exp * expMinusOne * basePowerTwo;
    unchecked {
        secondTerm /= 2;
    }
    uint256 thirdTerm = exp * expMinusOne * expMinusTwo * basePowerThree;
    unchecked {
        thirdTerm /= 6;
    }
 
    return WadRayMath.RAY + (rate * exp) / SECONDS_PER_YEAR + secondTerm + thirdTerm;
}

泰勒展开公式

e^(rt) ≈ 1 + rt + (rt)²/2! + (rt)³/3! + ...

其中:
- r = 年利率
- t = 时间(秒)
- rt = r × t / SECONDS_PER_YEAR

3.3 为什么使用泰勒展开?

  1. Gas 效率:避免指数运算的高成本
  2. 精度足够:对于短时间间隔(通常几秒到几小时),三阶展开精度足够
  3. 可预测性:确定性计算,无浮点误差

精度分析

时间间隔    | 年利率 10% | 误差
1 秒       | 0.0000003% | < 1e-15
1 小时      | 0.00114%   | < 1e-10
1 天       | 0.0274%    | < 1e-8
1 周       | 0.192%     | < 1e-6

4. 指数系统

4.1 流动性指数 (Liquidity Index)

// 更新流动性指数
function _updateIndexes(
    DataTypes.ReserveData storage reserve,
    DataTypes.ReserveCache memory reserveCache
) internal {
    // 计算自上次更新以来的累积利息
    uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
        reserveCache.currLiquidityRate,
        reserveCache.reserveLastUpdateTimestamp
    );
 
    // 新指数 = 旧指数 × 累积利息
    reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul(
        reserveCache.currLiquidityIndex
    );
    reserve.liquidityIndex = reserveCache.nextLiquidityIndex.toUint128();
}

用途

  • 追踪存款的累积收益
  • 计算 aToken 的实际余额

计算示例

初始存款: 100 USDC
存款时指数: 1.05
当前指数: 1.10

实际余额 = 缩放余额 × 当前指数
         = (100 / 1.05) × 1.10
         = 95.24 × 1.10
         = 104.76 USDC

收益 = 104.76 - 100 = 4.76 USDC

4.2 可变借贷指数 (Variable Borrow Index)

// 更新可变借贷指数
if (reserveCache.currScaledVariableDebt != 0) {
    uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
        reserveCache.currVariableBorrowRate,
        reserveCache.reserveLastUpdateTimestamp,
        block.timestamp
    );
 
    reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
        reserveCache.currVariableBorrowIndex
    );
    reserve.variableBorrowIndex = reserveCache.nextVariableBorrowIndex.toUint128();
}

用途

  • 追踪可变利率债务的累积利息
  • 计算用户的实际债务

5. 健康因子计算

5.1 公式定义

健康因子 (HF) = Σ(抵押品价值_i × 清算阈值_i) / 总债务

其中:
- 抵押品价值 = 代币数量 × 价格
- 清算阈值 = 该资产可用于抵押的最大比例

5.2 代码实现

// GenericLogic.sol
function calculateUserAccountData(
    mapping(address => DataTypes.ReserveData) storage reservesData,
    mapping(uint256 => address) storage reservesList,
    mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
    DataTypes.CalculateUserAccountDataParams memory params
) internal view returns (
    uint256 totalCollateralInBaseCurrency,
    uint256 totalDebtInBaseCurrency,
    uint256 avgLtv,
    uint256 avgLiquidationThreshold,
    uint256 healthFactor,
    bool hasZeroLtvCollateral
) {
    CalculateUserAccountDataVars memory vars;
 
    // 遍历用户所有储备
    while (vars.i < params.reservesCount) {
        if (!params.userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
            unchecked { ++vars.i; }
            continue;
        }
 
        vars.currentReserveAddress = reservesList[vars.i];
        DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
        DataTypes.ReserveConfigurationMap memory currentReserveConfig = currentReserve.configuration;
 
        // 获取资产价格和单位
        vars.assetPrice = IPriceOracleGetter(params.oracle).getAssetPrice(vars.currentReserveAddress);
        vars.assetUnit = 10 ** currentReserveConfig.getDecimals();
 
        // 获取风险参数
        (vars.ltv, vars.liquidationThreshold, vars.liquidationBonus, , ) =
            currentReserveConfig.getParams();
 
        // E-Mode 参数覆盖
        if (params.userEModeCategory != 0) {
            DataTypes.EModeCategory storage eModeCategory = eModeCategories[params.userEModeCategory];
            if (currentReserveConfig.getEModeCategory() == params.userEModeCategory) {
                vars.ltv = eModeCategory.ltv;
                vars.liquidationThreshold = eModeCategory.liquidationThreshold;
            }
        }
 
        // 累加抵押品价值
        if (params.userConfig.isUsingAsCollateral(vars.i)) {
            vars.userBalanceInBaseCurrency = _getUserBalanceInBaseCurrency(
                vars.currentReserveAddress,
                currentReserve,
                vars.assetPrice,
                vars.assetUnit
            );
 
            totalCollateralInBaseCurrency += vars.userBalanceInBaseCurrency;
 
            if (vars.ltv != 0) {
                avgLtv += vars.userBalanceInBaseCurrency * vars.ltv;
            } else {
                hasZeroLtvCollateral = true;
            }
 
            avgLiquidationThreshold += vars.userBalanceInBaseCurrency * vars.liquidationThreshold;
        }
 
        // 累加债务价值
        if (params.userConfig.isBorrowing(vars.i)) {
            totalDebtInBaseCurrency += _getUserDebtInBaseCurrency(
                vars.currentReserveAddress,
                currentReserve,
                vars.assetPrice,
                vars.assetUnit
            );
        }
 
        unchecked { ++vars.i; }
    }
 
    // 计算加权平均值
    if (totalCollateralInBaseCurrency != 0) {
        avgLtv = avgLtv / totalCollateralInBaseCurrency;
        avgLiquidationThreshold = avgLiquidationThreshold / totalCollateralInBaseCurrency;
    }
 
    // 计算健康因子
    healthFactor = totalDebtInBaseCurrency == 0
        ? type(uint256).max
        : totalCollateralInBaseCurrency.percentMul(avgLiquidationThreshold).wadDiv(totalDebtInBaseCurrency);
}

5.3 健康因子示例

用户资产:
- 10 ETH @ $2000 = $20,000 (清算阈值 82.5%)
- 5000 USDC @ $1 = $5,000 (清算阈值 85%)

用户债务:
- 15,000 USDT

计算:
抵押品价值加权 = 20,000 × 0.825 + 5,000 × 0.85 = 16,500 + 4,250 = 20,750
健康因子 = 20,750 / 15,000 = 1.383

结论:HF > 1,头寸安全

6. E-Mode 数学

6.1 E-Mode 资本效率

E-Mode 允许相关性资产获得更高的 LTV 和清算阈值:

普通模式:
- ETH LTV: 80%
- ETH 清算阈值: 82.5%

E-Mode (稳定币):
- USDC/USDT/DAI LTV: 97%
- 清算阈值: 98%

6.2 效率对比

场景:用户存入 10,000 USDC,想借出其他稳定币

普通模式:
- 最大借贷 = 10,000 × 80% = 8,000 USDC
- 杠杆率 = 1.8x

E-Mode (稳定币):
- 最大借贷 = 10,000 × 97% = 9,700 USDC
- 杠杆率 = 33x (理论最大)

资本效率提升 = 9,700 / 8,000 = 1.21x (21% 提升)

6.3 E-Mode 参数应用

// 在计算用户账户数据时应用 E-Mode 参数
if (EModeLogic.isInEModeCategory(params.userEModeCategory, vars.eModeAssetCategory)) {
    DataTypes.EModeCategory storage eModeCategory = eModeCategories[params.userEModeCategory];
 
    // 使用 E-Mode 的更高参数
    vars.ltv = eModeCategory.ltv;
    vars.liquidationThreshold = eModeCategory.liquidationThreshold;
 
    // E-Mode 可能使用特定的价格预言机
    if (eModeCategory.priceSource != address(0)) {
        vars.assetPrice = IPriceOracleGetter(eModeCategory.priceSource)
            .getAssetPrice(vars.currentReserveAddress);
    }
}

7. 清算奖励计算

7.1 奖励公式

基础抵押品 = 债务金额 × 债务价格 / 抵押品价格
清算人获得 = 基础抵押品 × 清算奖励比例
协议费用 = (清算人获得 - 基础抵押品) × 协议费率

7.2 代码实现

function _calculateAvailableCollateralToLiquidate(
    DataTypes.ReserveData storage collateralReserve,
    DataTypes.ReserveCache memory debtReserveCache,
    address collateralAsset,
    address debtAsset,
    uint256 debtToCover,
    uint256 userCollateralBalance,
    uint256 liquidationBonus,
    IPriceOracleGetter oracle
) internal view returns (uint256, uint256, uint256) {
    // 获取价格
    uint256 collateralPrice = oracle.getAssetPrice(collateralAsset);
    uint256 debtAssetPrice = oracle.getAssetPrice(debtAsset);
 
    // 基础抵押品 = 债务价值 / 抵押品价格
    uint256 baseCollateral = (debtAssetPrice * debtToCover * collateralAssetUnit) /
        (collateralPrice * debtAssetUnit);
 
    // 含奖励的抵押品 = 基础抵押品 × 清算奖励
    uint256 maxCollateralToLiquidate = baseCollateral.percentMul(liquidationBonus);
 
    // 如果用户抵押品不足
    if (maxCollateralToLiquidate > userCollateralBalance) {
        collateralAmount = userCollateralBalance;
        // 反推可清算债务
        debtAmountNeeded = ((collateralPrice * collateralAmount * debtAssetUnit) /
            (debtAssetPrice * collateralAssetUnit)).percentDiv(liquidationBonus);
    } else {
        collateralAmount = maxCollateralToLiquidate;
        debtAmountNeeded = debtToCover;
    }
 
    // 协议费用 = 奖励部分 × 协议费率
    uint256 bonusCollateral = collateralAmount - collateralAmount.percentDiv(liquidationBonus);
    uint256 liquidationProtocolFee = bonusCollateral.percentMul(liquidationProtocolFeePercentage);
 
    return (
        collateralAmount - liquidationProtocolFee,  // 清算人获得
        debtAmountNeeded,                           // 需偿还债务
        liquidationProtocolFee                      // 协议费用
    );
}

7.3 清算计算示例

场景:
- 债务: 7,500 USDC
- ETH 价格: $1,800
- 清算奖励: 5% (10500 = 105%)
- 协议费率: 10%

计算:
1. 基础抵押品 = 7,500 / 1,800 = 4.167 ETH
2. 含奖励抵押品 = 4.167 × 1.05 = 4.375 ETH
3. 奖励部分 = 4.375 - 4.167 = 0.208 ETH
4. 协议费用 = 0.208 × 10% = 0.0208 ETH
5. 清算人获得 = 4.375 - 0.0208 = 4.354 ETH

清算人利润 = 4.354 × $1,800 - $7,500 = $337.2 (4.5% 利润)

8. 小结

组件数学基础精度
利率计算双斜率模型RAY (1e27)
复利计算泰勒展开RAY (1e27)
健康因子加权平均WAD (1e18)
清算奖励百分比运算1e4

关键公式速查

利用率: U = TotalDebt / TotalLiquidity
借贷利率: R = BaseRate + Slope × U
存款利率: S = R × U × (1 - ReserveFactor)
健康因子: HF = (Collateral × LiqThreshold) / Debt
流动性指数: LI_new = LI_old × (1 + rate × Δt)

下一章将详细讲解 aToken 和债务代币的代币化机制。