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