Fix & Optimize contract

This commit is contained in:
2025-12-23 14:05:41 +08:00
parent b927acf3b7
commit d2e9377f78
117 changed files with 745 additions and 191 deletions

View File

@@ -96,6 +96,47 @@ contract Lending is
_unpause();
}
/**
* @notice 计算累计利息后的索引(不修改状态)
* @param timeElapsed 经过的时间
* @return 新的 supplyIndex 和 borrowIndex
*/
function accruedInterestIndices(uint256 timeElapsed) internal view returns (uint256, uint256) {
uint256 newSupplyIndex = supplyIndex;
uint256 newBorrowIndex = borrowIndex;
if (timeElapsed > 0) {
// 计算实际的 totalSupply 和 totalBorrow含利息
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
uint64 utilization = LendingMath.getUtilization(totalSupply, totalBorrow);
// 计算供应利率和借款利率(每秒利率)
uint64 supplyRate = LendingMath.getSupplyRate(
utilization,
supplyKink,
supplyPerSecondInterestRateSlopeLow,
supplyPerSecondInterestRateSlopeHigh,
supplyPerSecondInterestRateBase
);
uint64 borrowRate = LendingMath.getBorrowRate(
utilization,
borrowKink,
borrowPerSecondInterestRateSlopeLow,
borrowPerSecondInterestRateSlopeHigh,
borrowPerSecondInterestRateBase
);
// 计算新的利息累计因子
newSupplyIndex = LendingMath.accrueInterest(supplyIndex, supplyRate, timeElapsed);
newBorrowIndex = LendingMath.accrueInterest(borrowIndex, borrowRate, timeElapsed);
}
return (newSupplyIndex, newBorrowIndex);
}
/**
* @notice 计提利息
*/
@@ -103,34 +144,8 @@ contract Lending is
uint256 timeElapsed = block.timestamp - lastAccrualTime;
if (timeElapsed == 0) return;
// 计算实际的 totalSupply 和 totalBorrow含利息
// 注意totalSupplyBase 和 totalBorrowBase 都是正数本金(正数=存款本金,负数=借款本金)
// supplyIndex 用于存款borrowIndex 用于借款
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
uint64 utilization = LendingMath.getUtilization(totalSupply, totalBorrow);
// 计算供应利率和借款利率(每秒利率)
uint64 supplyRate = LendingMath.getSupplyRate(
utilization,
supplyKink,
supplyPerSecondInterestRateSlopeLow,
supplyPerSecondInterestRateSlopeHigh,
supplyPerSecondInterestRateBase
);
uint64 borrowRate = LendingMath.getBorrowRate(
utilization,
borrowKink,
borrowPerSecondInterestRateSlopeLow,
borrowPerSecondInterestRateSlopeHigh,
borrowPerSecondInterestRateBase
);
// 更新利息累计因子(使用每秒利率)
supplyIndex = LendingMath.accrueInterest(supplyIndex, supplyRate, timeElapsed);
borrowIndex = LendingMath.accrueInterest(borrowIndex, borrowRate, timeElapsed);
// 使用辅助函数计算新索引
(supplyIndex, borrowIndex) = accruedInterestIndices(timeElapsed);
lastAccrualTime = block.timestamp;
}
@@ -323,12 +338,13 @@ contract Lending is
AssetConfig memory assetConfig = assetConfigs[asset];
uint256 assetPrice = IPriceFeed(assetConfig.priceFeed).getPrice();
// 计算抵押品价值USD8位精度
// 计算抵押品价值USD8位精度- 用于事件记录
uint256 assetScale = 10 ** assetConfig.decimals;
uint256 collateralValueUSD = (collateralAmount * assetPrice) / assetScale;
// 应用 liquidationFactor 折扣
uint256 discountedValue = (collateralValueUSD * assetConfig.liquidationFactor) / 1e18;
// 直接计算折扣后的价值,避免二次除法
// discounted = (amount * price * factor) / (scale * 1e18)
uint256 discountedValue = (collateralAmount * assetPrice * assetConfig.liquidationFactor) / (assetScale * 1e18);
totalCollateralValue += discountedValue;
// 将抵押品转移到清算库存
@@ -410,6 +426,12 @@ contract Lending is
) external override nonReentrant whenNotPaused {
if (collateralReserves[asset] == 0) revert InsufficientBalance();
// 检查储备金是否充足(使用实时计算的储备金)
int256 currentReserves = getReserves();
if (currentReserves >= 0 && uint256(currentReserves) >= targetReserves) {
revert NotForSale(); // 储备金充足,无需出售
}
// 计算可购买的抵押品数量
uint256 collateralAmount = quoteCollateral(asset, baseAmount);
@@ -417,15 +439,6 @@ contract Lending is
if (collateralAmount < minAmount) revert InsufficientBalance();
if (collateralAmount > collateralReserves[asset]) revert InsufficientBalance();
// 检查储备金是否充足(如果已达到目标,不再出售抵押品)
uint256 balance = IERC20(baseToken).balanceOf(address(this));
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
int256 currentReserves = int256(balance) - int256(totalSupply) + int256(totalBorrow);
if (currentReserves >= 0 && uint256(currentReserves) >= targetReserves) {
revert NotForSale(); // 储备金充足,无需出售
}
// 收取清算人支付的资金
IERC20(baseToken).safeTransferFrom(msg.sender, address(this), baseAmount);
@@ -448,23 +461,17 @@ contract Lending is
uint256 assetPrice = IPriceFeed(assetConfig.priceFeed).getPrice();
uint256 basePrice = IPriceFeed(baseTokenPriceFeed).getPrice();
// 计算折扣率
// discountFactor = storeFrontPriceFactor * (FACTOR_SCALE - liquidationFactor) / FACTOR_SCALE
uint256 FACTOR_SCALE = 1e18;
uint256 discountFactor = (storeFrontPriceFactor * (FACTOR_SCALE - assetConfig.liquidationFactor)) / FACTOR_SCALE;
// 计算折扣后的资产价格
// assetPriceDiscounted = assetPrice * (FACTOR_SCALE - discountFactor) / FACTOR_SCALE
uint256 assetPriceDiscounted = (assetPrice * (FACTOR_SCALE - discountFactor)) / FACTOR_SCALE;
// 计算可购买的抵押品数量
// 公式:(basePrice * baseAmount * assetScale) / (assetPriceDiscounted * baseScale)
uint256 baseScale = 10 ** uint256(IERC20Metadata(baseToken).decimals());
uint256 assetScale = 10 ** uint256(assetConfig.decimals);
// 使用中间变量分步计算,避免潜在的溢出
// 先计算分子和分母,再进行除法
return (basePrice * baseAmount * assetScale) / (assetPriceDiscounted * baseScale);
// 折扣因子
uint256 discountFactor = (storeFrontPriceFactor * (FACTOR_SCALE - assetConfig.liquidationFactor)) / FACTOR_SCALE;
// 分子: basePrice * baseAmount * assetScale * FACTOR_SCALE
// 分母: assetPrice * (FACTOR_SCALE - discountFactor) * baseScale
return (basePrice * baseAmount * assetScale * FACTOR_SCALE) /
(assetPrice * (FACTOR_SCALE - discountFactor) * baseScale);
}
/**
@@ -579,11 +586,15 @@ contract Lending is
return collateralReserves[asset];
}
function getReserves() external view override returns (int256) {
// 计算实际总供应和总借款(含利息
function getReserves() public view override returns (int256) {
// 计算最新的利息索引(不修改状态
uint256 timeElapsed = block.timestamp - lastAccrualTime;
(uint256 newSupplyIndex, uint256 newBorrowIndex) = accruedInterestIndices(timeElapsed);
// 使用最新索引计算实际总供应和总借款(含利息)
uint256 balance = IERC20(baseToken).balanceOf(address(this));
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
uint256 totalSupply = (uint256(totalSupplyBase) * newSupplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * newBorrowIndex) / 1e18;
// reserves = balance - totalSupply + totalBorrow
return int256(balance) - int256(totalSupply) + int256(totalBorrow);
@@ -629,10 +640,8 @@ contract Lending is
* @notice 提取协议储备金(仅 owner
*/
function withdrawReserves(address to, uint256 amount) external override onlyOwner nonReentrant {
uint256 balance = IERC20(baseToken).balanceOf(address(this));
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
int256 currentReserves = int256(balance) - int256(totalSupply) + int256(totalBorrow);
// 使用实时计算的储备金
int256 currentReserves = getReserves();
// 检查储备金是否充足
if (currentReserves < 0 || amount > uint256(currentReserves)) {