Files
assetxContracts/contracts/ytLending/Lending.sol

686 lines
29 KiB
Solidity
Raw Normal View History

2025-12-18 13:07:35 +08:00
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./LendingStorage.sol";
import "./LendingMath.sol";
2025-12-22 14:46:35 +08:00
import "../interfaces/ILending.sol";
2025-12-24 16:41:26 +08:00
import "../interfaces/IYTLendingPriceFeed.sol";
2025-12-18 13:07:35 +08:00
/**
* @title Lending
* @notice
*/
contract Lending is
ILending,
LendingStorage,
UUPSUpgradeable,
OwnableUpgradeable,
PausableUpgradeable,
ReentrancyGuardUpgradeable
{
using SafeERC20 for IERC20;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @notice
* @param config
*/
function initialize(Configuration calldata config) external initializer {
__UUPSUpgradeable_init();
__Ownable_init(msg.sender);
__Pausable_init();
__ReentrancyGuard_init();
// 设置基础配置
baseToken = config.baseToken;
2025-12-24 16:41:26 +08:00
lendingPriceSource = config.lendingPriceSource;
2025-12-22 13:25:06 +08:00
2025-12-18 13:07:35 +08:00
// 常量:一年的秒数
2025-12-22 13:25:06 +08:00
uint64 SECONDS_PER_YEAR = 365 * 24 * 60 * 60; // 31,536,000
2025-12-18 13:07:35 +08:00
2025-12-19 10:54:52 +08:00
// 设置利率参数
2025-12-18 13:07:35 +08:00
supplyKink = config.supplyKink;
supplyPerSecondInterestRateSlopeLow = uint64(config.supplyPerYearInterestRateSlopeLow / SECONDS_PER_YEAR);
supplyPerSecondInterestRateSlopeHigh = uint64(config.supplyPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR);
supplyPerSecondInterestRateBase = uint64(config.supplyPerYearInterestRateBase / SECONDS_PER_YEAR);
borrowKink = config.borrowKink;
borrowPerSecondInterestRateSlopeLow = uint64(config.borrowPerYearInterestRateSlopeLow / SECONDS_PER_YEAR);
borrowPerSecondInterestRateSlopeHigh = uint64(config.borrowPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR);
borrowPerSecondInterestRateBase = uint64(config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR);
// 设置其他参数
storeFrontPriceFactor = config.storeFrontPriceFactor;
baseBorrowMin = config.baseBorrowMin;
targetReserves = config.targetReserves;
// 初始化利息累计因子
supplyIndex = 1e18;
borrowIndex = 1e18;
lastAccrualTime = block.timestamp;
// 设置抵押资产配置
for (uint i = 0; i < config.assetConfigs.length; i++) {
AssetConfig memory assetConfig = config.assetConfigs[i];
// 验证参数合法性(必须 < 1
2025-12-19 10:54:52 +08:00
if(assetConfig.liquidationFactor >= 1e18) revert InvalidLiquidationFactor();
if(assetConfig.borrowCollateralFactor >= 1e18) revert InvalidBorrowCollateralFactor();
if(assetConfig.liquidateCollateralFactor >= 1e18) revert InvalidLiquidateCollateralFactor();
2025-12-18 13:07:35 +08:00
assetConfigs[assetConfig.asset] = assetConfig;
assetList.push(assetConfig.asset);
}
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
function pause() external onlyOwner {
_pause();
}
2025-12-22 14:12:50 +08:00
2025-12-18 13:07:35 +08:00
function unpause() external onlyOwner {
_unpause();
}
function setTargetReserves(uint104 newTargetReserves) external onlyOwner {
targetReserves = newTargetReserves;
emit TargetReservesUpdated(targetReserves);
}
2025-12-23 14:05:41 +08:00
/**
* @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);
}
2025-12-18 13:07:35 +08:00
/**
* @notice
*/
function accrueInterest() public {
uint256 timeElapsed = block.timestamp - lastAccrualTime;
if (timeElapsed == 0) return;
2025-12-23 14:05:41 +08:00
// 使用辅助函数计算新索引
(supplyIndex, borrowIndex) = accruedInterestIndices(timeElapsed);
2025-12-18 13:07:35 +08:00
lastAccrualTime = block.timestamp;
}
/**
* @notice
*/
function supply(uint256 amount) external override nonReentrant whenNotPaused {
accrueInterest();
IERC20(baseToken).transferFrom(msg.sender, address(this), amount);
2025-12-18 13:07:35 +08:00
// 获取用户当前本金
UserBasic memory user = userBasic[msg.sender];
int104 oldPrincipal = user.principal;
// 计算当前实际余额(含利息)
uint256 index = oldPrincipal >= 0 ? supplyIndex : borrowIndex;
int256 oldBalance = LendingMath.principalToBalance(oldPrincipal, index);
// 计算新余额(增加存款)
int256 newBalance = oldBalance + int256(amount);
// 转换为新本金(可能从借款变为存款)
uint256 newIndex = newBalance >= 0 ? supplyIndex : borrowIndex;
int104 newPrincipal = LendingMath.balanceToPrincipal(newBalance, newIndex);
2025-12-22 13:25:06 +08:00
// 根据新旧本金,计算还款和存款金额
2025-12-18 13:07:35 +08:00
(uint104 repayAmount, uint104 supplyAmount) = LendingMath.repayAndSupplyAmount(oldPrincipal, newPrincipal);
// 更新全局状态
totalBorrowBase -= repayAmount;
totalSupplyBase += supplyAmount;
// 更新用户本金
userBasic[msg.sender].principal = newPrincipal;
emit Supply(msg.sender, msg.sender, amount);
}
/**
* @notice
* @dev amount borrowRate borrowPeriod
2025-12-18 13:07:35 +08:00
*/
function withdraw(uint256 amount) external override nonReentrant whenNotPaused {
accrueInterest();
// 获取用户当前本金
UserBasic memory user = userBasic[msg.sender];
int104 oldPrincipal = user.principal;
// 计算当前实际余额(含利息)
uint256 index = oldPrincipal >= 0 ? supplyIndex : borrowIndex;
int256 oldBalance = LendingMath.principalToBalance(oldPrincipal, index);
// 计算新余额
int256 newBalance = oldBalance - int256(amount);
// 转换为新本金
uint256 newIndex = newBalance >= 0 ? supplyIndex : borrowIndex;
int104 newPrincipal = LendingMath.balanceToPrincipal(newBalance, newIndex);
// 计算提取和借款金额
(uint104 withdrawAmount, uint104 borrowAmount) = LendingMath.withdrawAndBorrowAmount(oldPrincipal, newPrincipal);
// 更新全局状态
totalSupplyBase -= withdrawAmount;
totalBorrowBase += borrowAmount;
// 更新用户本金
userBasic[msg.sender].principal = newPrincipal;
// 如果变成负余额(借款),检查抵押品
if (newBalance < 0) {
if (uint256(-newBalance) < baseBorrowMin) revert BorrowTooSmall();
if (!_isSolvent(msg.sender)) revert InsufficientCollateral();
}
IERC20(baseToken).safeTransfer(msg.sender, amount);
emit Withdraw(msg.sender, msg.sender, amount);
}
/**
* @notice
* @dev
2025-12-18 13:07:35 +08:00
*/
function supplyCollateral(address asset, uint256 amount) external override nonReentrant whenNotPaused {
AssetConfig memory config = assetConfigs[asset];
if (config.asset == address(0)) revert Unauthorized();
uint256 newTotal = userCollateral[msg.sender][asset] + amount;
if (newTotal > config.supplyCap) revert SupplyCapExceeded();
IERC20(asset).transferFrom(msg.sender, address(this), amount);
2025-12-18 13:07:35 +08:00
userCollateral[msg.sender][asset] += amount;
emit SupplyCollateral(msg.sender, msg.sender, asset, amount);
}
/**
* @notice
*/
function withdrawCollateral(address asset, uint256 amount) external override nonReentrant whenNotPaused {
accrueInterest();
if (userCollateral[msg.sender][asset] < amount) revert InsufficientBalance();
userCollateral[msg.sender][asset] -= amount;
// 检查是否仍有足够的抵押品(如果有债务)
int104 principal = userBasic[msg.sender].principal;
if (principal < 0) {
if (!_isSolvent(msg.sender)) revert InsufficientCollateral();
}
IERC20(asset).safeTransfer(msg.sender, amount);
emit WithdrawCollateral(msg.sender, msg.sender, asset, amount);
}
/**
* @notice
2025-12-22 13:25:06 +08:00
* @dev liquidateCollateralFactor后liquidateCollateralFactor大于债务价值baseToken显示
2025-12-18 13:07:35 +08:00
*/
function _absorbInternal(address absorber, address borrower) internal {
if (!isLiquidatable(borrower)) revert NotLiquidatable();
// 获取用户当前本金
UserBasic memory user = userBasic[borrower];
int104 oldPrincipal = user.principal;
// 计算当前实际余额(含利息累计的债务)
int256 oldBalance = LendingMath.principalToBalance(oldPrincipal, borrowIndex);
if (oldBalance >= 0) revert NotLiquidatable();
// 计算所有抵押品的总价值(按 liquidationFactor 折扣)
2025-12-24 16:41:26 +08:00
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
2025-12-18 13:07:35 +08:00
uint256 totalCollateralValue = 0;
for (uint i = 0; i < assetList.length; i++) {
address asset = assetList[i];
uint256 collateralAmount = userCollateral[borrower][asset];
if (collateralAmount > 0) {
AssetConfig memory assetConfig = assetConfigs[asset];
2025-12-24 16:41:26 +08:00
uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
2025-12-18 13:07:35 +08:00
2025-12-29 15:30:10 +08:00
// 计算抵押品价值-用于事件记录
2025-12-18 13:07:35 +08:00
uint256 assetScale = 10 ** assetConfig.decimals;
uint256 collateralValueUSD = (collateralAmount * assetPrice) / assetScale;
2025-12-23 14:05:41 +08:00
// 直接计算折扣后的价值,避免二次除法
// discounted = (amount * price * factor) / (scale * 1e18)
uint256 discountedValue = (collateralAmount * assetPrice * assetConfig.liquidationFactor) / (assetScale * 1e18);
2025-12-18 13:07:35 +08:00
totalCollateralValue += discountedValue;
// 将抵押品转移到清算库存
userCollateral[borrower][asset] = 0;
collateralReserves[asset] += collateralAmount;
// 发射抵押品吸收事件
emit AbsorbCollateral(absorber, borrower, asset, collateralAmount, collateralValueUSD);
}
}
// 将抵押品价值转换为 baseToken 数量
uint256 baseScale = 10 ** IERC20Metadata(baseToken).decimals();
uint256 collateralInBase = (totalCollateralValue * baseScale) / basePrice;
// 计算新余额oldBalance负数+ 抵押品价值
int256 newBalance = oldBalance + int256(collateralInBase);
// 如果新余额仍为负,强制归零(坏账由协议承担)
if (newBalance < 0) {
newBalance = 0;
}
// 转换为新本金
int104 newPrincipal = LendingMath.balanceToPrincipal(newBalance, supplyIndex);
// 更新用户本金
userBasic[borrower].principal = newPrincipal;
// 计算偿还和供应金额
(uint104 repayAmount, uint104 supplyAmount) = LendingMath.repayAndSupplyAmount(oldPrincipal, newPrincipal);
// 更新全局状态(储备金通过减少 totalBorrowBase 和增加 totalSupplyBase 来承担坏账)
2025-12-18 13:07:35 +08:00
totalSupplyBase += supplyAmount;
totalBorrowBase -= repayAmount;
// 计算协议承担的坏账部分
// 坏账 = 用户债务 - 抵押品价值(当抵押品不足时)
uint256 basePaidOut = 0;
if (int256(collateralInBase) < -oldBalance) {
// 抵押品不足以覆盖债务,差额由协议储备金承担
basePaidOut = uint256(-oldBalance) - collateralInBase;
}
// 如果 collateralInBase >= -oldBalance说明抵押品足够无坏账
2025-12-18 13:07:35 +08:00
uint256 valueOfBasePaidOut = (basePaidOut * basePrice) / baseScale;
// 发射债务吸收事件
emit AbsorbDebt(absorber, borrower, basePaidOut, valueOfBasePaidOut);
}
/**
* @notice
*/
function absorb(address borrower) external override nonReentrant whenNotPaused {
accrueInterest();
_absorbInternal(msg.sender, borrower);
}
/**
* @notice
*/
function absorbMultiple(address absorber, address[] calldata accounts) external override nonReentrant whenNotPaused {
accrueInterest();
for (uint i = 0; i < accounts.length; ) {
_absorbInternal(absorber, accounts[i]);
unchecked { i++; }
}
}
/**
* @notice
2026-01-07 10:49:49 +08:00
* @dev
2025-12-18 13:07:35 +08:00
*/
function buyCollateral(
address asset,
uint256 minAmount,
uint256 baseAmount,
address recipient
) external override nonReentrant whenNotPaused {
if (collateralReserves[asset] == 0) revert InsufficientBalance();
2025-12-23 14:05:41 +08:00
// 检查储备金是否充足(使用实时计算的储备金)
int256 currentReserves = getReserves();
if (currentReserves >= 0 && uint256(currentReserves) >= targetReserves) {
revert NotForSale(); // 储备金充足,无需出售
}
2026-01-07 10:49:49 +08:00
// 计算可购买的抵押品数量(基于用户愿意支付的 baseAmount
2025-12-18 13:07:35 +08:00
uint256 collateralAmount = quoteCollateral(asset, baseAmount);
2026-01-07 10:49:49 +08:00
// 自动限制到可用储备量
// 这样可以防止价格波动导致交易失败
if (collateralAmount > collateralReserves[asset]) {
collateralAmount = collateralReserves[asset];
}
// 滑点保护:确保购买量不低于用户的最小期望
2025-12-18 13:07:35 +08:00
if (collateralAmount < minAmount) revert InsufficientBalance();
2026-01-07 10:49:49 +08:00
// 根据实际购买量计算需要支付的金额(而非固定的 baseAmount
// 这样如果购买量被限制,用户只需支付相应的费用
uint256 actualBaseAmount = quoteBaseAmount(asset, collateralAmount);
// 收取实际需要的资金
IERC20(baseToken).transferFrom(msg.sender, address(this), actualBaseAmount);
2025-12-18 13:07:35 +08:00
// 抵押品出库
collateralReserves[asset] -= collateralAmount;
// 转账抵押品到指定接收人
IERC20(asset).safeTransfer(recipient, collateralAmount);
// 注意:收入会自动体现在 getReserves() 中,因为 balance 增加了
2026-01-07 10:49:49 +08:00
emit BuyCollateral(msg.sender, asset, actualBaseAmount, collateralAmount);
}
/**
* @notice baseToken
* @param asset
* @param collateralAmount
* @return baseToken
*/
function quoteBaseAmount(address asset, uint256 collateralAmount) internal view returns (uint256) {
AssetConfig memory assetConfig = assetConfigs[asset];
uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
uint256 FACTOR_SCALE = 1e18;
uint256 baseScale = 10 ** uint256(IERC20Metadata(baseToken).decimals());
uint256 assetScale = 10 ** uint256(assetConfig.decimals);
// 计算折扣因子
uint256 discountFactor = (storeFrontPriceFactor * (FACTOR_SCALE - assetConfig.liquidationFactor)) / FACTOR_SCALE;
// 计算折扣后的资产价格
uint256 effectiveAssetPrice = (assetPrice * (FACTOR_SCALE - discountFactor)) / FACTOR_SCALE;
// 反向计算baseAmount = (collateralAmount * effectiveAssetPrice * baseScale) / (basePrice * assetScale)
if (baseScale == assetScale) {
return (collateralAmount * effectiveAssetPrice) / basePrice;
} else {
uint256 adjustedAmount = (collateralAmount * baseScale) / assetScale;
return (adjustedAmount * effectiveAssetPrice) / basePrice;
}
2025-12-18 13:07:35 +08:00
}
/**
* @notice baseAmount可购买的抵押品数量
2025-12-24 16:41:26 +08:00
* @dev 1e30
2025-12-18 13:07:35 +08:00
*/
function quoteCollateral(address asset, uint256 baseAmount) public view override returns (uint256) {
AssetConfig memory assetConfig = assetConfigs[asset];
2025-12-24 16:41:26 +08:00
uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
2025-12-18 13:07:35 +08:00
uint256 FACTOR_SCALE = 1e18;
uint256 baseScale = 10 ** uint256(IERC20Metadata(baseToken).decimals());
uint256 assetScale = 10 ** uint256(assetConfig.decimals);
2025-12-24 16:41:26 +08:00
// 计算折扣因子
2025-12-23 14:05:41 +08:00
uint256 discountFactor = (storeFrontPriceFactor * (FACTOR_SCALE - assetConfig.liquidationFactor)) / FACTOR_SCALE;
2025-12-24 16:41:26 +08:00
// 计算折扣后的资产价格 (保持 1e30 精度)
uint256 effectiveAssetPrice = (assetPrice * (FACTOR_SCALE - discountFactor)) / FACTOR_SCALE;
// 为了避免溢出,我们需要重新排列计算:
// result = (basePrice * baseAmount * assetScale) / (effectiveAssetPrice * baseScale)
//
// 由于所有价格都是 1e30 精度,我们可以先约简价格:
// priceRatio = basePrice / effectiveAssetPrice (保持精度)
// result = (baseAmount * priceRatio * assetScale) / (1e30 * baseScale)
//
// 但为了避免精度损失,我们分步计算:
// step1 = baseAmount * assetScale / baseScale (token amount conversion)
// step2 = step1 * basePrice / effectiveAssetPrice (price conversion)
// 如果 baseScale 和 assetScale 相同(都是18),可以简化
if (baseScale == assetScale) {
// result = baseAmount * basePrice / effectiveAssetPrice
return (baseAmount * basePrice) / effectiveAssetPrice;
} else {
// 一般情况:分步计算避免溢出
uint256 adjustedAmount = (baseAmount * assetScale) / baseScale;
return (adjustedAmount * basePrice) / effectiveAssetPrice;
}
2025-12-18 13:07:35 +08:00
}
/**
* @notice
*/
function _isSolvent(address account) internal view returns (bool) {
int104 principal = userBasic[account].principal;
if (principal >= 0) return true;
// 计算实际债务(含利息)- 使用 borrowIndex
int256 balance = LendingMath.principalToBalance(principal, borrowIndex);
uint256 debt = uint256(-balance);
// 将 debt 转换为美元价值(使用 baseToken 价格)
2025-12-24 16:41:26 +08:00
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
2025-12-18 13:07:35 +08:00
uint256 baseDecimals = IERC20Metadata(baseToken).decimals();
uint256 debtValue = (debt * basePrice) / (10 ** baseDecimals);
// 计算借款能力(抵押品价值已经在 _getCollateralValue 中应用了借款系数)
uint256 borrowCapacity = _getCollateralValue(account);
// 比较:借款能力 >= 债务价值
return borrowCapacity >= debtValue;
}
/**
* @notice
*/
function _getCollateralValue(address account) internal view returns (uint256) {
uint256 totalValue = 0;
for (uint i = 0; i < assetList.length; i++) {
address asset = assetList[i];
uint256 amount = userCollateral[account][asset];
if (amount > 0) {
AssetConfig memory config = assetConfigs[asset];
2025-12-24 16:41:26 +08:00
uint256 price = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
2025-12-18 13:07:35 +08:00
uint256 value = LendingMath.getCollateralValue(amount, price, config.decimals);
totalValue += (value * config.borrowCollateralFactor) / 1e18;
}
}
return totalValue;
}
// ========== View Functions ==========
function getBalance(address account) external view override returns (int256) {
int104 principal = userBasic[account].principal;
// 使用 supplyIndex 计算实际余额(含利息)
return LendingMath.principalToBalance(principal, supplyIndex);
}
2025-12-22 14:12:50 +08:00
function supplyBalanceOf(address account) external view override returns (uint256) {
2025-12-18 13:07:35 +08:00
int104 principal = userBasic[account].principal;
if (principal <= 0) return 0;
// 只返回正余额(存款)
return uint256(LendingMath.principalToBalance(principal, supplyIndex));
}
function borrowBalanceOf(address account) external view override returns (uint256) {
int104 principal = userBasic[account].principal;
if (principal >= 0) return 0;
// 只返回负余额(借款),转为正数
int256 balance = LendingMath.principalToBalance(principal, borrowIndex);
return uint256(-balance);
}
function getCollateral(address account, address asset) external view override returns (uint256) {
return userCollateral[account][asset];
}
function isLiquidatable(address account) public view override returns (bool) {
int104 principal = userBasic[account].principal;
if (principal >= 0) return false;
// 计算实际债务(含利息)
int256 balance = LendingMath.principalToBalance(principal, borrowIndex);
uint256 debt = uint256(-balance);
// 将 debt 转换为美元价值(使用 baseToken 价格和 price feed 精度)
2025-12-24 16:41:26 +08:00
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
2025-12-18 13:07:35 +08:00
uint256 baseDecimals = IERC20Metadata(baseToken).decimals();
uint256 debtValue = (debt * basePrice) / (10 ** baseDecimals);
// 计算抵押品总价值(清算阈值)
uint256 collateralValue = 0;
for (uint i = 0; i < assetList.length; i++) {
address asset = assetList[i];
uint256 amount = userCollateral[account][asset];
if (amount > 0) {
AssetConfig memory config = assetConfigs[asset];
2025-12-24 16:41:26 +08:00
uint256 price = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
2025-12-18 13:07:35 +08:00
uint256 value = LendingMath.getCollateralValue(amount, price, config.decimals);
collateralValue += (value * config.liquidateCollateralFactor) / 1e18;
}
}
// 比较:债务价值 > 抵押品清算阈值价值
return debtValue > collateralValue;
}
function getTotalSupply() external view returns (uint256) {
return (uint256(totalSupplyBase) * supplyIndex) / 1e18;
}
function getTotalBorrow() external view returns (uint256) {
return (uint256(totalBorrowBase) * borrowIndex) / 1e18;
}
function getCollateralReserves(address asset) external view override returns (uint256) {
return collateralReserves[asset];
}
2025-12-23 14:05:41 +08:00
function getReserves() public view override returns (int256) {
// 计算最新的利息索引(不修改状态)
uint256 timeElapsed = block.timestamp - lastAccrualTime;
(uint256 newSupplyIndex, uint256 newBorrowIndex) = accruedInterestIndices(timeElapsed);
// 使用最新索引计算实际总供应和总借款(含利息)
2025-12-18 13:07:35 +08:00
uint256 balance = IERC20(baseToken).balanceOf(address(this));
2025-12-23 14:05:41 +08:00
uint256 totalSupply = (uint256(totalSupplyBase) * newSupplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * newBorrowIndex) / 1e18;
2025-12-18 13:07:35 +08:00
// reserves = balance - totalSupply + totalBorrow
return int256(balance) - int256(totalSupply) + int256(totalBorrow);
}
function getUtilization() external view override returns (uint256) {
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
return LendingMath.getUtilization(totalSupply, totalBorrow);
}
function getSupplyRate() external view override returns (uint64) {
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
uint64 utilization = LendingMath.getUtilization(totalSupply, totalBorrow);
uint64 perSecondRate = LendingMath.getSupplyRate(
utilization,
supplyKink,
supplyPerSecondInterestRateSlopeLow,
supplyPerSecondInterestRateSlopeHigh,
supplyPerSecondInterestRateBase
);
// 转换为年化利率APY
return perSecondRate * 31536000; // SECONDS_PER_YEAR
}
2025-12-22 13:25:06 +08:00
function getBorrowRate() external view override returns (uint64) {
uint256 totalSupply = (uint256(totalSupplyBase) * supplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * borrowIndex) / 1e18;
uint64 utilization = LendingMath.getUtilization(totalSupply, totalBorrow);
uint64 perSecondRate = LendingMath.getBorrowRate(
utilization,
borrowKink,
borrowPerSecondInterestRateSlopeLow,
borrowPerSecondInterestRateSlopeHigh,
borrowPerSecondInterestRateBase
);
// 转换为年化利率APY
return perSecondRate * 31536000; // SECONDS_PER_YEAR
}
2025-12-18 13:07:35 +08:00
/**
* @notice owner
*/
function withdrawReserves(address to, uint256 amount) external override onlyOwner nonReentrant {
2025-12-23 14:05:41 +08:00
// 使用实时计算的储备金
int256 currentReserves = getReserves();
2025-12-18 13:07:35 +08:00
// 检查储备金是否充足
if (currentReserves < 0 || amount > uint256(currentReserves)) {
revert InsufficientReserves();
}
// 转账储备金
IERC20(baseToken).safeTransfer(to, amount);
emit WithdrawReserves(to, amount);
}
/**
* @dev
* 50slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap;
2025-12-18 13:07:35 +08:00
}