aave v3 资产状态更新(updateState)合约代码解析
我们知道aave是基于流动性指数(liquidityIndex )和可变借款指数( variableBorrowIndex )来计算用户的存款利息和借款利息的,在理解此方法之前需要先阅读aave v3 存款与借款利息的计算方式这篇文章。每次有借贷,还款,存取,清算,等操作都会触发利率的变化,而updateState这个合约函数就是专门处理这一环节的,在借贷,还款,存取,清算这些操作执行前都要把状态更新成最新的。
function updateState(DataTypes.ReserveData storage reserve,DataTypes.ReserveCache memory reserveCache) internal {// If time didn't pass since last stored timestamp, skip state update//solium-disable-next-lineif (reserveCache.reserveLastUpdateTimestamp == uint40(block.timestamp)) {return;}_updateIndexes(reserve, reserveCache);_accrueToTreasury(reserve, reserveCache);//solium-disable-next-linereserve.lastUpdateTimestamp = uint40(block.timestamp);reserveCache.reserveLastUpdateTimestamp = uint40(block.timestamp);}
_updateIndexes部分在aave v3 存款与借款利息的计算方式这篇文章中已经解析过,这里不再赘述。我们直接看_accrueToTreasury;
function _accrueToTreasury(DataTypes.ReserveData storage reserve,DataTypes.ReserveCache memory reserveCache) internal {if (reserveCache.reserveFactor == 0) {return;}// debt accrued is the sum of the current debt minus the sum of the debt at the last update// Rounding down to undermint to the treasury and keep the invariant healthy.uint256 totalDebtAccrued = reserveCache.currScaledVariableDebt.rayMulFloor(reserveCache.nextVariableBorrowIndex - reserveCache.currVariableBorrowIndex);uint256 amountToMint = totalDebtAccrued.percentMul(reserveCache.reserveFactor);if (amountToMint != 0) {reserve.accruedToTreasury += amountToMint.getATokenMintScaledAmount(reserveCache.nextLiquidityIndex).toUint128();}}
这个方法是向协议累积收益的核心函数。将借贷利息的一部分分配给协议 ,这是 Aave 的收入来源机制。
第一步检查储备因子reserveFactor 如果为 0,则直接返回 这个参数存储在ReserveConfigurationMap 第64位(详情见状态存储)
第二步计算本次借出多少钱
reserveCache.currScaledVariableDebt 表示当前所有被借出的可变利率债务的缩放后总金额
总累积债务利息 = 当前缩放债务 × (新借款指数 - 旧借款指数)
上面公式可能不太好理解,我们拆分一下:
- 上次更新时总共借出的本息= 当前缩放债务×旧借款指数
- 本次更新后总共借出的本息 = 当前缩放债务×新借款指数
- 二者的差值代表从上次更新状态到目前位置产生了多少新的利息(这个方法是存取,借贷之前前进行处理的方法,还没有对本金进行处理,所以这里的差值只是利息)
reserveFactor (储备因子)是Aave协议中用于控制协议收入比例的参数。
协议收入 = 累积债务利息 × reserveFactor
最后由于利润会转变为储蓄,所以要通过当前的储蓄利息指数转换再累加到协议的总收入上
reserve.accruedToTreasury += amountToMint.getATokenMintScaledAmount(reserveCache.nextLiquidityIndex).toUint128();
累加的金额需要先将实际金额转换为aToken的缩放金额
- amountToMint :需要铸造到金库的实际金额(以基础资产为单位,如DAI)
- reserveCache.nextLiquidityIndex :下一个流动性指数,用于缩放计算
- getATokenMintScaledAmount :来自TokenMath库的方法,计算公式为:
缩放金额 = 实际金额 ÷ 流动性指数
