---
# System prepended metadata

title: 'Aave v4: 动态风险配置'
tags: [DeFi]

---

Aave v4的Spoke.sol合约中，状态变量为：
```solidity
  /// @dev Number of reserves listed in the Spoke.
  uint256 internal _reserveCount;

  /// @dev Map of user addresses and reserve identifiers to user positions.
  mapping(address user => mapping(uint256 reserveId => UserPosition)) internal _userPositions;

  /// @dev Map of user addresses to their position status.
  mapping(address user => PositionStatus) internal _positionStatus;

  /// @dev Map of reserve identifiers to their Reserve data.
  mapping(uint256 reserveId => Reserve) internal _reserves;

  /// @dev Map of position manager addresses to their configuration data.
  mapping(address positionManager => PositionManagerConfig) internal _positionManager;

  /// @dev Map of reserve identifiers and dynamic configuration keys to the dynamic configuration data.
  mapping(uint256 reserveId => mapping(uint24 dynamicConfigKey => DynamicReserveConfig))
    internal _dynamicConfig;

  /// @dev Liquidation configuration for the Spoke.
  LiquidationConfig internal _liquidationConfig;

  /// @dev Map of hub addresses and asset identifiers to whether the reserve exists.
  mapping(address hub => mapping(uint256 assetId => bool)) internal _reserveExists;
```
![image](https://hackmd.io/_uploads/S1Xneg-VZl.png)

一开始整个看下来，让我疑惑的一点是：为什么要特意把 **collateralFactor (CF)**、**maxLiquidationBonus (LB)**、**liquidationFee (LF)** 另外划分为`DynamicReserveConfig`?

> 如果是让我来设计，那我大概率会把CF直接分配到`Reserve`中，LB和LF则分配到`LiquidationConfig`中。当然，LB和LF可能会针对代币进行特定配置，此时则可以分配到`Reserve`中。
> 

<br>

**那官方特意把 CF、LB和LF划分为`DynamicReserveConfig` 的目的是什么呢？**

先说一下结论，目的是：**减少协议在风险调控时的负担**。

Aave的风险调控是为了避免坏账的产生，当察觉到某一代币未来可能会发生预料外的下跌风险时，社区会下调该代币的 **collateralFactor**。但该下调行为时极具负担的，因为CF参数会直接影响到用户仓位的健康因子，使得下调后用户仓位可能会直接变为可清算的。

当考虑到这个影响，调控需要十分谨慎，不仅仅要考虑到未来，还要考虑已有的仓位。而使用该代币作为抵押品的用户，由于个人利益，也会抵触该调控的实施。

为了解决这个问题，协议不把 **collateralFactor** 简单地设为全局参数，而是会记录CF的不同版本。在Reserve中会指向某个版本，视为**最新的版本**。而用户的仓位也会指向某个版本，但并不一定是最新的。


具体机制：
- 当用户把某代币设为抵押品时，仓位会初始化为最新的CF版本；
- 当用户的行为会导致**健康因子下降**时，会触发仓位CF版本的更新。行为如：
    - 取消某代币作为抵押品；
    - 借款；
    - 提出抵押品；
- 用户可以自行更新为最新版本的；
- 当用户的仓位被清算时，CF 使用的是用户仓位当前的版本，而不是使用最新的；

除此之外，协议保留了直接指定修改某一版本的参数，以应对一些预料不到的特殊情况。

<br>

那 **maxLiquidationBonus (LB)** 和 **liquidationFee (LF)** 为什么也会划分进去？

对于LB，是为了和CF进行兼容。在清算步骤中，计算要恢复到目标健康因子需要减少多少债务时：
```solidity
// aave-v4/src/spoke/libraries/LiquidationLogic.sol
/// @notice Calculates the amount of debt needed to be liquidated to restore a position to the target health factor. 
function _calculateDebtToTargetHealthFactor( 
   CalculateDebtToTargetHealthFactorParams memory params 
) internal pure returns (uint256) { 
   uint256 liquidationPenalty = params.liquidationBonus.bpsToWad().percentMulUp( 
     params.collateralFactor 
   ); 
  
   // denominator cannot be zero as `liquidationPenalty` is always < PercentageMath.PERCENTAGE_FACTOR 
   // `liquidationBonus.percentMulUp(collateralFactor) < PercentageMath.PERCENTAGE_FACTOR` is enforced in `_validateDynamicReserveConfig` 
   // and targetHealthFactor is always >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD 
   return 
     params.totalDebtValue.mulDivUp( 
       params.debtAssetUnit * (params.targetHealthFactor - params.healthFactor), 
       (params.targetHealthFactor - liquidationPenalty) * params.debtAssetPrice.toWad() 
     ); 
 } 
 
// aave-v4/src/spoke/Spoke.sol
/// there is enough collateral to cover liquidation. 
function _validateDynamicReserveConfig(DynamicReserveConfig calldata config) internal pure { 
   require( 
     config.collateralFactor < PercentageMath.PERCENTAGE_FACTOR && 
       config.maxLiquidationBonus >= PercentageMath.PERCENTAGE_FACTOR && 
       config.maxLiquidationBonus.percentMulUp(config.collateralFactor) < 
       PercentageMath.PERCENTAGE_FACTOR, 
     InvalidCollateralFactorAndMaxLiquidationBonus() 
   ); 
   require(config.liquidationFee <= PercentageMath.PERCENTAGE_FACTOR, InvalidLiquidationFee()); 
 } 
```
为了确保分母不为0，也即`(params.targetHealthFactor - liquidationPenalty) * params.debtAssetPrice.toWad()` 不为0，所以始终约束着 CF * LB < 100%。因此CF和LB需要互相兼容。

其实，个人觉得也是可以将LB设为全局的，这样的话，在调控 CF 时，需要满足 CF < 1 / LB的约束。官方可能是觉得这样子不够灵活，因此就划分在了一起。 

> 个人疑问：为什么不是约束 CF * LB < targetHealthFactor，而是PERCENTAGE_FACTOR (100%)。虽然实际情况下，targetHealthFactor 是会默认大于 1，但用targetHealthFactor岂不是更加严谨?
【补充】从代码贡献者的[解答](https://github.com/aave/aave-v4/issues/1079#issuecomment-3701737212)了解到：如果CF*LB > **清算前的健康因子**, 会使得清算后的健康因子变得更小，这种结果是不合理的。
> 

而对于 **liquidationFee (LF)**，并没有找到特别必要原因。在[该迁移的提议](https://github.com/aave/aave-v4/issues/529)中，提到：在提高LB时，如果LF保持不变，可能会些许抵消LB调控的效果。因此，大概也是为了灵活调控。

<br>

参考：
1. https://github.com/aave/aave-v4/blob/main/docs/overview.md#dynamic-risk-configuration 
2. https://governance.aave.com/t/aave-v4-new-features-and-risk-parameter-analysis/23443#p-59818-h-2-dynamic-risk-configuration-22 
3. https://github.com/aave/aave-v4/pull/405 
4. https://github.com/aave/aave-v4/issues/459 
5. https://github.com/aave/aave-v4/issues/529