Files
assetxContracts/contracts/ytLending/Lending.sol
2026-01-12 14:33:16 +08:00

687 lines
29 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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";
import "../interfaces/ILending.sol";
import "../interfaces/IYTLendingPriceFeed.sol";
/**
* @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;
lendingPriceSource = config.lendingPriceSource;
// 常量:一年的秒数
uint64 SECONDS_PER_YEAR = 365 * 24 * 60 * 60; // 31,536,000
// 设置利率参数
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
if(assetConfig.liquidationFactor >= 1e18) revert InvalidLiquidationFactor();
if(assetConfig.borrowCollateralFactor >= 1e18) revert InvalidBorrowCollateralFactor();
if(assetConfig.liquidateCollateralFactor >= 1e18) revert InvalidLiquidateCollateralFactor();
assetConfigs[assetConfig.asset] = assetConfig;
assetList.push(assetConfig.asset);
}
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
function setTargetReserves(uint104 newTargetReserves) external onlyOwner {
targetReserves = newTargetReserves;
emit TargetReservesUpdated(targetReserves);
}
/**
* @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 计提利息
*/
function accrueInterest() public {
uint256 timeElapsed = block.timestamp - lastAccrualTime;
if (timeElapsed == 0) return;
// 使用辅助函数计算新索引
(supplyIndex, borrowIndex) = accruedInterestIndices(timeElapsed);
lastAccrualTime = block.timestamp;
}
/**
* @notice 存入基础资产
*/
function supply(uint256 amount) external override nonReentrant whenNotPaused {
accrueInterest();
IERC20(baseToken).transferFrom(msg.sender, address(this), amount);
// 获取用户当前本金
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 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
*/
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 由于不涉及债务计算,存入抵押品反而会让账户更安全,所以不用更新利息因子
*/
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);
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 清算不良债务(内部实现)
* @dev 当用户抵押品由于乘以liquidateCollateralFactor后小于债务价值时会进行清算清算后如果实际抵押品价值乘以liquidateCollateralFactor大于债务价值则将差额部分作为用户本金本金以baseToken显示否则将差额部分作为坏账由协议承担
*/
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 折扣)
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
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];
uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
// 计算抵押品价值-用于事件记录
uint256 assetScale = 10 ** assetConfig.decimals;
uint256 collateralValueUSD = (collateralAmount * assetPrice) / assetScale;
// 直接计算折扣后的价值,避免二次除法
// discounted = (amount * price * factor) / (scale * 1e18)
uint256 discountedValue = (collateralAmount * assetPrice * assetConfig.liquidationFactor) / (assetScale * 1e18);
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 来承担坏账)
totalSupplyBase += supplyAmount;
totalBorrowBase -= repayAmount;
// 计算协议承担的坏账部分
// 坏账 = 用户债务 - 抵押品价值(当抵押品不足时)
uint256 basePaidOut = 0;
if (int256(collateralInBase) < -oldBalance) {
// 抵押品不足以覆盖债务,差额由协议储备金承担
basePaidOut = uint256(-oldBalance) - collateralInBase;
}
// 如果 collateralInBase >= -oldBalance说明抵押品足够无坏账
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 购买清算后的抵押品
* @dev 自动限制购买量到可用储备,只收取实际需要的费用
*/
function buyCollateral(
address asset,
uint256 minAmount,
uint256 baseAmount,
address recipient
) external override nonReentrant whenNotPaused {
if (collateralReserves[asset] == 0) revert InsufficientBalance();
// 检查储备金是否充足(使用实时计算的储备金)
int256 currentReserves = getReserves();
if (currentReserves >= 0 && uint256(currentReserves) >= targetReserves) {
revert NotForSale(); // 储备金充足,无需出售
}
// 计算可购买的抵押品数量(基于用户愿意支付的 baseAmount
uint256 collateralAmount = quoteCollateral(asset, baseAmount);
// 自动限制到可用储备量
// 这样可以防止价格波动导致交易失败
if (collateralAmount > collateralReserves[asset]) {
collateralAmount = collateralReserves[asset];
}
// 滑点保护:确保购买量不低于用户的最小期望
if (collateralAmount < minAmount) revert InsufficientBalance();
// 根据实际购买量计算需要支付的金额(而非固定的 baseAmount
// 这样如果购买量被限制,用户只需支付相应的费用
uint256 actualBaseAmount = quoteBaseAmount(asset, collateralAmount);
// 收取实际需要的资金
IERC20(baseToken).transferFrom(msg.sender, address(this), actualBaseAmount);
// 抵押品出库
collateralReserves[asset] -= collateralAmount;
// 转账抵押品到指定接收人
IERC20(asset).safeTransfer(recipient, collateralAmount);
// 注意:收入会自动体现在 getReserves() 中,因为 balance 增加了
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;
}
}
/**
* @notice 计算支付指定baseAmount可购买的抵押品数量
* @dev 重新设计以避免在 1e30 价格精度下溢出
*/
function quoteCollateral(address asset, uint256 baseAmount) public view override 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;
// 计算折扣后的资产价格 (保持 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;
}
}
/**
* @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 价格)
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
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];
uint256 price = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
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负余额用borrowIndex
uint256 index = principal >= 0 ? supplyIndex : borrowIndex;
return LendingMath.principalToBalance(principal, index);
}
function supplyBalanceOf(address account) external view override returns (uint256) {
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 精度)
uint256 basePrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(baseToken);
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];
uint256 price = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
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];
}
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) * newSupplyIndex) / 1e18;
uint256 totalBorrow = (uint256(totalBorrowBase) * newBorrowIndex) / 1e18;
// 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
}
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
}
/**
* @notice 提取协议储备金(仅 owner
*/
function withdrawReserves(address to, uint256 amount) external override onlyOwner nonReentrant {
// 使用实时计算的储备金
int256 currentReserves = getReserves();
// 检查储备金是否充足
if (currentReserves < 0 || amount > uint256(currentReserves)) {
revert InsufficientReserves();
}
// 转账储备金
IERC20(baseToken).safeTransfer(to, amount);
emit WithdrawReserves(to, amount);
}
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap;
}