audit
This commit is contained in:
@@ -7,10 +7,6 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
||||
import "./ConfiguratorStorage.sol";
|
||||
import "./LendingFactory.sol";
|
||||
|
||||
/**
|
||||
* @title Configurator
|
||||
* @notice 借贷池配置管理合约
|
||||
*/
|
||||
contract Configurator is
|
||||
ConfiguratorStorage,
|
||||
UUPSUpgradeable,
|
||||
@@ -37,16 +33,8 @@ contract Configurator is
|
||||
__Ownable_init(msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 授权升级函数 - 只有 owner 可以升级
|
||||
*/
|
||||
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
|
||||
|
||||
/**
|
||||
* @notice 设置工厂合约地址
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param newFactory 新工厂地址
|
||||
*/
|
||||
function setFactory(address lendingProxy, address newFactory) external onlyOwner {
|
||||
if (newFactory == address(0)) revert InvalidAddress();
|
||||
|
||||
@@ -55,27 +43,19 @@ contract Configurator is
|
||||
emit SetFactory(lendingProxy, oldFactory, newFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 设置市场配置
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param newConfiguration 新配置
|
||||
*/
|
||||
function setConfiguration(address lendingProxy, Configuration calldata newConfiguration)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
Configuration memory oldConfiguration = configuratorParams[lendingProxy];
|
||||
|
||||
// 防止修改不可变参数
|
||||
if (oldConfiguration.baseToken != address(0) &&
|
||||
(oldConfiguration.baseToken != newConfiguration.baseToken ||
|
||||
oldConfiguration.trackingIndexScale != newConfiguration.trackingIndexScale))
|
||||
revert ConfigurationAlreadyExists();
|
||||
|
||||
// 删除旧的资产配置
|
||||
delete configuratorParams[lendingProxy];
|
||||
|
||||
// 设置新配置
|
||||
configuratorParams[lendingProxy].baseToken = newConfiguration.baseToken;
|
||||
configuratorParams[lendingProxy].lendingPriceSource = newConfiguration.lendingPriceSource;
|
||||
configuratorParams[lendingProxy].supplyKink = newConfiguration.supplyKink;
|
||||
@@ -91,7 +71,6 @@ contract Configurator is
|
||||
configuratorParams[lendingProxy].baseBorrowMin = newConfiguration.baseBorrowMin;
|
||||
configuratorParams[lendingProxy].targetReserves = newConfiguration.targetReserves;
|
||||
|
||||
// 复制资产配置
|
||||
for (uint i = 0; i < newConfiguration.assetConfigs.length; i++) {
|
||||
configuratorParams[lendingProxy].assetConfigs.push(newConfiguration.assetConfigs[i]);
|
||||
}
|
||||
@@ -99,11 +78,6 @@ contract Configurator is
|
||||
emit SetConfiguration(lendingProxy, oldConfiguration, newConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 添加抵押资产
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param assetConfig 资产配置
|
||||
*/
|
||||
function addAsset(address lendingProxy, AssetConfig calldata assetConfig)
|
||||
external
|
||||
onlyOwner
|
||||
@@ -112,11 +86,6 @@ contract Configurator is
|
||||
emit AddAsset(lendingProxy, assetConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 更新资产配置
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param newAssetConfig 新资产配置
|
||||
*/
|
||||
function updateAsset(address lendingProxy, AssetConfig calldata newAssetConfig)
|
||||
external
|
||||
onlyOwner
|
||||
@@ -127,12 +96,6 @@ contract Configurator is
|
||||
emit UpdateAsset(lendingProxy, oldAssetConfig, newAssetConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 更新资产抵押率
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param asset 资产地址
|
||||
* @param newBorrowCF 新借款抵押率
|
||||
*/
|
||||
function updateAssetBorrowCollateralFactor(
|
||||
address lendingProxy,
|
||||
address asset,
|
||||
@@ -145,12 +108,6 @@ contract Configurator is
|
||||
configuratorParams[lendingProxy].assetConfigs[assetIndex].borrowCollateralFactor = newBorrowCF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 更新资产供应上限
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param asset 资产地址
|
||||
* @param newSupplyCap 新供应上限
|
||||
*/
|
||||
function updateAssetSupplyCap(
|
||||
address lendingProxy,
|
||||
address asset,
|
||||
@@ -163,23 +120,12 @@ contract Configurator is
|
||||
configuratorParams[lendingProxy].assetConfigs[assetIndex].supplyCap = newSupplyCap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 部署新的 Lending 实现
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @return 新实现合约地址
|
||||
*/
|
||||
function deploy(address lendingProxy) external onlyOwner returns (address) {
|
||||
address newLending = LendingFactory(factory[lendingProxy]).deploy();
|
||||
emit LendingDeployed(lendingProxy, newLending);
|
||||
return newLending;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取资产索引
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @param asset 资产地址
|
||||
* @return 资产在配置数组中的索引
|
||||
*/
|
||||
function getAssetIndex(address lendingProxy, address asset) public view returns (uint) {
|
||||
AssetConfig[] memory assetConfigs = configuratorParams[lendingProxy].assetConfigs;
|
||||
uint numAssets = assetConfigs.length;
|
||||
@@ -192,19 +138,9 @@ contract Configurator is
|
||||
revert AssetDoesNotExist();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取市场配置
|
||||
* @param lendingProxy Lending 代理地址
|
||||
* @return 配置信息
|
||||
*/
|
||||
function getConfiguration(address lendingProxy) external view returns (Configuration memory) {
|
||||
return configuratorParams[lendingProxy];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 预留存储空间,用于未来升级时添加新的状态变量
|
||||
* 50个slot = 50 * 32 bytes = 1600 bytes
|
||||
*/
|
||||
uint256[50] private __gap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,15 +3,8 @@ pragma solidity ^0.8.0;
|
||||
|
||||
import "./LendingConfiguration.sol";
|
||||
|
||||
/**
|
||||
* @title ConfiguratorStorage
|
||||
* @notice Configurator 存储定义
|
||||
*/
|
||||
abstract contract ConfiguratorStorage is LendingConfiguration {
|
||||
// Lending 代理地址 => 工厂合约地址
|
||||
mapping(address => address) public factory;
|
||||
|
||||
// Lending 代理地址 => 配置参数
|
||||
mapping(address => Configuration) public configuratorParams;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,10 +14,6 @@ import "./LendingMath.sol";
|
||||
import "../interfaces/ILending.sol";
|
||||
import "../interfaces/IYTLendingPriceFeed.sol";
|
||||
|
||||
/**
|
||||
* @title Lending
|
||||
* @notice 借贷池核心合约
|
||||
*/
|
||||
contract Lending is
|
||||
ILending,
|
||||
LendingStorage,
|
||||
@@ -33,24 +29,17 @@ contract Lending is
|
||||
_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
|
||||
uint64 SECONDS_PER_YEAR = 365 * 24 * 60 * 60;
|
||||
|
||||
// 设置利率参数
|
||||
supplyKink = config.supplyKink;
|
||||
supplyPerSecondInterestRateSlopeLow = uint64(config.supplyPerYearInterestRateSlopeLow / SECONDS_PER_YEAR);
|
||||
supplyPerSecondInterestRateSlopeHigh = uint64(config.supplyPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR);
|
||||
@@ -61,22 +50,18 @@ contract Lending is
|
||||
borrowPerSecondInterestRateSlopeHigh = uint64(config.borrowPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR);
|
||||
borrowPerSecondInterestRateBase = uint64(config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR);
|
||||
|
||||
// 设置其他参数
|
||||
storeFrontPriceFactor = config.storeFrontPriceFactor;
|
||||
trackingIndexScale = config.trackingIndexScale;
|
||||
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();
|
||||
@@ -96,23 +81,16 @@ 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,
|
||||
@@ -129,7 +107,6 @@ contract Lending is
|
||||
borrowPerSecondInterestRateBase
|
||||
);
|
||||
|
||||
// 计算新的利息累计因子
|
||||
newSupplyIndex = LendingMath.accrueInterest(supplyIndex, supplyRate, timeElapsed);
|
||||
newBorrowIndex = LendingMath.accrueInterest(borrowIndex, borrowRate, timeElapsed);
|
||||
}
|
||||
@@ -137,88 +114,62 @@ contract Lending is
|
||||
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();
|
||||
@@ -229,10 +180,6 @@ contract Lending is
|
||||
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();
|
||||
@@ -247,9 +194,6 @@ contract Lending is
|
||||
emit SupplyCollateral(msg.sender, msg.sender, asset, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 取出抵押品
|
||||
*/
|
||||
function withdrawCollateral(address asset, uint256 amount) external override nonReentrant whenNotPaused {
|
||||
accrueInterest();
|
||||
|
||||
@@ -257,7 +201,6 @@ contract Lending is
|
||||
|
||||
userCollateral[msg.sender][asset] -= amount;
|
||||
|
||||
// 检查是否仍有足够的抵押品(如果有债务)
|
||||
int104 principal = userBasic[msg.sender].principal;
|
||||
if (principal < 0) {
|
||||
if (!_isSolvent(msg.sender)) revert InsufficientCollateral();
|
||||
@@ -268,42 +211,29 @@ contract Lending is
|
||||
emit WithdrawCollateral(msg.sender, msg.sender, asset, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 借款
|
||||
* @dev baseBorrowMin 是用户借款的最小金额,如果用户借款后,余额小于 baseBorrowMin(由正数变为负数同理),则抛出 BorrowTooSmall 错误
|
||||
*/
|
||||
function borrow(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);
|
||||
|
||||
// 检查最小借款额
|
||||
if (newBalance < 0 && uint256(-newBalance) < baseBorrowMin) revert BorrowTooSmall();
|
||||
|
||||
// 转换为新本金(新状态可能从存款变为借款)
|
||||
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 (!_isSolvent(msg.sender)) revert InsufficientCollateral();
|
||||
|
||||
IERC20(baseToken).safeTransfer(msg.sender, amount);
|
||||
@@ -311,22 +241,15 @@ contract Lending is
|
||||
emit Withdraw(msg.sender, msg.sender, 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;
|
||||
|
||||
@@ -338,75 +261,52 @@ contract Lending is
|
||||
AssetConfig memory assetConfig = assetConfigs[asset];
|
||||
uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
|
||||
|
||||
// 计算抵押品价值(USD,8位精度)- 用于事件记录
|
||||
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; ) {
|
||||
@@ -415,9 +315,6 @@ contract Lending is
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 购买清算后的抵押品
|
||||
*/
|
||||
function buyCollateral(
|
||||
address asset,
|
||||
uint256 minAmount,
|
||||
@@ -426,36 +323,25 @@ contract Lending is
|
||||
) external override nonReentrant whenNotPaused {
|
||||
if (collateralReserves[asset] == 0) revert InsufficientBalance();
|
||||
|
||||
// 检查储备金是否充足(使用实时计算的储备金)
|
||||
int256 currentReserves = getReserves();
|
||||
if (currentReserves >= 0 && uint256(currentReserves) >= targetReserves) {
|
||||
revert NotForSale(); // 储备金充足,无需出售
|
||||
revert NotForSale();
|
||||
}
|
||||
|
||||
// 计算可购买的抵押品数量
|
||||
uint256 collateralAmount = quoteCollateral(asset, baseAmount);
|
||||
|
||||
// 验证数量
|
||||
if (collateralAmount < minAmount) revert InsufficientBalance();
|
||||
if (collateralAmount > collateralReserves[asset]) revert InsufficientBalance();
|
||||
|
||||
// 收取清算人支付的资金
|
||||
IERC20(baseToken).transferFrom(msg.sender, address(this), baseAmount);
|
||||
|
||||
// 抵押品出库
|
||||
collateralReserves[asset] -= collateralAmount;
|
||||
|
||||
// 转账抵押品到指定接收人
|
||||
IERC20(asset).safeTransfer(recipient, collateralAmount);
|
||||
|
||||
// 注意:收入会自动体现在 getReserves() 中,因为 balance 增加了
|
||||
emit BuyCollateral(msg.sender, asset, baseAmount, collateralAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算支付指定baseAmount可购买的抵押品数量
|
||||
* @dev 重新设计以避免在 1e30 价格精度下溢出
|
||||
*/
|
||||
function quoteCollateral(address asset, uint256 baseAmount) public view override returns (uint256) {
|
||||
AssetConfig memory assetConfig = assetConfigs[asset];
|
||||
|
||||
@@ -465,61 +351,35 @@ contract Lending is
|
||||
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;
|
||||
|
||||
@@ -537,25 +397,20 @@ contract Lending is
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
// ========== View Functions ==========
|
||||
|
||||
function getBalance(address account) external view override returns (int256) {
|
||||
int104 principal = userBasic[account].principal;
|
||||
// 使用 supplyIndex 计算实际余额(含利息)
|
||||
return LendingMath.principalToBalance(principal, supplyIndex);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -568,16 +423,13 @@ contract Lending is
|
||||
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];
|
||||
@@ -590,7 +442,6 @@ contract Lending is
|
||||
}
|
||||
}
|
||||
|
||||
// 比较:债务价值 > 抵押品清算阈值价值
|
||||
return debtValue > collateralValue;
|
||||
}
|
||||
|
||||
@@ -607,16 +458,13 @@ contract Lending is
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -637,8 +485,7 @@ contract Lending is
|
||||
supplyPerSecondInterestRateSlopeHigh,
|
||||
supplyPerSecondInterestRateBase
|
||||
);
|
||||
// 转换为年化利率(APY)
|
||||
return perSecondRate * 31536000; // SECONDS_PER_YEAR
|
||||
return perSecondRate * 31536000;
|
||||
}
|
||||
|
||||
function getBorrowRate() external view override returns (uint64) {
|
||||
@@ -652,32 +499,20 @@ contract Lending is
|
||||
borrowPerSecondInterestRateSlopeHigh,
|
||||
borrowPerSecondInterestRateBase
|
||||
);
|
||||
// 转换为年化利率(APY)
|
||||
return perSecondRate * 31536000; // SECONDS_PER_YEAR
|
||||
return perSecondRate * 31536000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @title LendingConfiguration
|
||||
* @notice 借贷池配置结构体定义
|
||||
*/
|
||||
contract LendingConfiguration {
|
||||
struct AssetConfig {
|
||||
address asset; // 资产地址
|
||||
@@ -19,7 +15,6 @@ contract LendingConfiguration {
|
||||
address baseToken; // 基础资产
|
||||
address lendingPriceSource; // 借贷价格源
|
||||
|
||||
// 利率模型参数
|
||||
uint64 supplyKink; // 供应拐点利用率
|
||||
uint64 supplyPerYearInterestRateSlopeLow; // 供应拐点前斜率
|
||||
uint64 supplyPerYearInterestRateSlopeHigh; // 供应拐点后斜率
|
||||
@@ -30,7 +25,6 @@ contract LendingConfiguration {
|
||||
uint64 borrowPerYearInterestRateSlopeHigh; // 借款拐点后斜率
|
||||
uint64 borrowPerYearInterestRateBase; // 借款基础利率
|
||||
|
||||
// 其他核心参数
|
||||
uint64 storeFrontPriceFactor; // 清算价格折扣
|
||||
uint64 trackingIndexScale; // 追踪索引比例
|
||||
uint104 baseBorrowMin; // 最小借款额
|
||||
@@ -38,5 +32,4 @@ contract LendingConfiguration {
|
||||
|
||||
AssetConfig[] assetConfigs; // 抵押资产配置数组
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,14 +11,9 @@ contract LendingFactory is LendingConfiguration, Ownable {
|
||||
|
||||
event LendingDeployed(address indexed lending);
|
||||
|
||||
/**
|
||||
* @notice 部署新的 Lending 实现合约
|
||||
* @return 新 Lending 合约地址
|
||||
*/
|
||||
function deploy() external onlyOwner returns (address) {
|
||||
Lending lending = new Lending();
|
||||
emit LendingDeployed(address(lending));
|
||||
return address(lending);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +1,47 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @title LendingMath
|
||||
* @notice 借贷池数学计算库
|
||||
*/
|
||||
library LendingMath {
|
||||
uint256 internal constant FACTOR_SCALE = 1e18;
|
||||
uint256 internal constant SECONDS_PER_YEAR = 365 * 24 * 60 * 60;
|
||||
|
||||
/**
|
||||
* @notice 将本金转换为实际余额(含利息)
|
||||
* @param principal 本金(正数或负数)
|
||||
* @param index 利息索引
|
||||
* @return 实际余额
|
||||
*/
|
||||
function principalToBalance(int104 principal, uint256 index) internal pure returns (int256) {
|
||||
return int256(principal) * int256(index) / int256(FACTOR_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 将实际余额转换为本金
|
||||
* @param balance 实际余额(正数或负数)
|
||||
* @param index 利息索引
|
||||
* @return 本金
|
||||
*/
|
||||
function balanceToPrincipal(int256 balance, uint256 index) internal pure returns (int104) {
|
||||
return int104((balance * int256(FACTOR_SCALE)) / int256(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算供应方本金变化和借款方本金变化
|
||||
* @dev 用于 absorb 时计算账户状态变化
|
||||
*/
|
||||
function repayAndSupplyAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
|
||||
// 如果新本金小于旧本金,没有偿还或供应
|
||||
if (newPrincipal < oldPrincipal) return (0, 0);
|
||||
|
||||
if (newPrincipal <= 0) {
|
||||
// 从负数变得更接近0(偿还债务)
|
||||
return (uint104(newPrincipal - oldPrincipal), 0);
|
||||
} else if (oldPrincipal >= 0) {
|
||||
// 两个都是正数(增加存款)
|
||||
return (0, uint104(newPrincipal - oldPrincipal));
|
||||
} else {
|
||||
// 从负数变正数(偿还所有债务并存款)
|
||||
return (uint104(-oldPrincipal), uint104(newPrincipal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算提取金额和借款金额
|
||||
* @dev 用于 withdraw/borrow 时计算账户状态变化
|
||||
*/
|
||||
function withdrawAndBorrowAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
|
||||
// 如果新本金大于旧本金,没有提取或借款
|
||||
if (newPrincipal > oldPrincipal) return (0, 0);
|
||||
|
||||
if (newPrincipal >= 0) {
|
||||
// 还是正数(提取存款)
|
||||
return (uint104(oldPrincipal - newPrincipal), 0);
|
||||
} else if (oldPrincipal <= 0) {
|
||||
// 两个都是负数(增加借款)
|
||||
return (0, uint104(oldPrincipal - newPrincipal));
|
||||
} else {
|
||||
// 从正数变负数(提取所有存款并借款)
|
||||
return (uint104(oldPrincipal), uint104(-newPrincipal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算利用率
|
||||
* @param totalSupply 总供应量
|
||||
* @param totalBorrow 总借款量
|
||||
* @return 利用率 (scaled by 1e18)
|
||||
*/
|
||||
function getUtilization(uint256 totalSupply, uint256 totalBorrow) internal pure returns (uint64) {
|
||||
if (totalSupply == 0) return 0;
|
||||
return uint64((totalBorrow * FACTOR_SCALE) / totalSupply);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算供应利率(每秒利率)
|
||||
*/
|
||||
function getSupplyRate(
|
||||
uint256 utilization,
|
||||
uint64 supplyKink,
|
||||
@@ -99,9 +58,6 @@ library LendingMath {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算借款利率(每秒利率)
|
||||
*/
|
||||
function getBorrowRate(
|
||||
uint256 utilization,
|
||||
uint64 borrowKink,
|
||||
@@ -118,26 +74,15 @@ library LendingMath {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算复利后的利息累计因子
|
||||
* @param index 当前利息累计因子
|
||||
* @param interestRatePerSecond 每秒利率
|
||||
* @param timeElapsed 经过的秒数
|
||||
* @return 新的利息累计因子
|
||||
*/
|
||||
function accrueInterest(
|
||||
uint256 index,
|
||||
uint64 interestRatePerSecond,
|
||||
uint256 timeElapsed
|
||||
) internal pure returns (uint256) {
|
||||
// 优化:每秒利率直接乘以时间,只需一次除法
|
||||
uint256 interestAccrued = (index * interestRatePerSecond * timeElapsed) / FACTOR_SCALE;
|
||||
return index + interestAccrued;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算抵押品价值
|
||||
*/
|
||||
function getCollateralValue(
|
||||
uint256 collateralAmount,
|
||||
uint256 collateralPrice,
|
||||
@@ -146,14 +91,10 @@ library LendingMath {
|
||||
return (collateralAmount * collateralPrice) / (10 ** collateralDecimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 计算借款能力
|
||||
*/
|
||||
function getBorrowCapacity(
|
||||
uint256 collateralValue,
|
||||
uint64 borrowCollateralFactor
|
||||
) internal pure returns (uint256) {
|
||||
return (collateralValue * borrowCollateralFactor) / FACTOR_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,5 +55,4 @@ contract LendingPriceFeed is OwnableUpgradeable, UUPSUpgradeable {
|
||||
|
||||
return uint256(price) * 1e22; // 1e22 = 10^(30-8)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,17 +3,11 @@ pragma solidity ^0.8.0;
|
||||
|
||||
import "./LendingConfiguration.sol";
|
||||
|
||||
/**
|
||||
* @title LendingStorage
|
||||
* @notice 借贷池存储变量定义
|
||||
*/
|
||||
abstract contract LendingStorage is LendingConfiguration {
|
||||
|
||||
// 市场配置
|
||||
address public baseToken;
|
||||
address public lendingPriceSource;
|
||||
|
||||
// 利率参数(每秒利率,已从年化利率转换)
|
||||
uint64 public supplyKink;
|
||||
uint64 public supplyPerSecondInterestRateSlopeLow;
|
||||
uint64 public supplyPerSecondInterestRateSlopeHigh;
|
||||
@@ -24,36 +18,27 @@ abstract contract LendingStorage is LendingConfiguration {
|
||||
uint64 public borrowPerSecondInterestRateSlopeHigh;
|
||||
uint64 public borrowPerSecondInterestRateBase;
|
||||
|
||||
// 清算参数
|
||||
uint64 public storeFrontPriceFactor;
|
||||
uint64 public trackingIndexScale;
|
||||
uint104 public baseBorrowMin;
|
||||
uint104 public targetReserves;
|
||||
|
||||
// 资产映射
|
||||
mapping(address => AssetConfig) public assetConfigs;
|
||||
address[] public assetList;
|
||||
|
||||
// 用户账户信息
|
||||
struct UserBasic {
|
||||
int104 principal; // 本金(正数=存款本金,负数=借款本金)
|
||||
int104 principal;
|
||||
}
|
||||
mapping(address => UserBasic) public userBasic;
|
||||
|
||||
// 用户抵押品余额
|
||||
mapping(address => mapping(address => uint256)) public userCollateral;
|
||||
|
||||
// 总存款本金和总借款本金
|
||||
uint104 public totalSupplyBase;
|
||||
uint104 public totalBorrowBase;
|
||||
|
||||
// 利息索引
|
||||
uint256 public supplyIndex;
|
||||
uint256 public borrowIndex;
|
||||
uint256 public lastAccrualTime;
|
||||
|
||||
// 清算后的抵押品库存(不同于 reserves!)
|
||||
// reserves 通过公式动态计算:balance - totalSupply + totalBorrow
|
||||
mapping(address => uint256) public collateralReserves;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -193,16 +193,13 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
|
||||
|
||||
uint256 usdyAmount = _ytLPAmount * aumInUsdy / ytLPSupply;
|
||||
|
||||
// 先销毁ytLP
|
||||
IYTLPToken(ytLP).burn(_account, _ytLPAmount);
|
||||
|
||||
// 检查余额,只铸造差额部分
|
||||
uint256 usdyBalance = IERC20(usdy).balanceOf(address(this));
|
||||
if (usdyAmount > usdyBalance) {
|
||||
IUSDY(usdy).mint(address(this), usdyAmount - usdyBalance);
|
||||
}
|
||||
|
||||
// 转账USDY到Vault并换回代币
|
||||
IERC20(usdy).safeTransfer(ytVault, usdyAmount);
|
||||
uint256 amountOut = IYTVault(ytVault).sellUSDY(_tokenOut, _receiver);
|
||||
|
||||
@@ -225,7 +222,7 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
|
||||
function getAumInUsdy(bool _maximise) public view returns (uint256) {
|
||||
uint256 aum = IYTVault(ytVault).getPoolValue(_maximise);
|
||||
|
||||
aum += aumAddition; // aumAddition是协议额外增加的AUM,用来“预留风险缓冲 / 扣除潜在负债”
|
||||
aum += aumAddition;
|
||||
if (aum > aumDeduction) {
|
||||
aum -= aumDeduction;
|
||||
} else {
|
||||
|
||||
@@ -24,11 +24,11 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
|
||||
|
||||
uint256 public constant PRICE_PRECISION = 10 ** 30;
|
||||
uint256 public constant BASIS_POINTS_DIVISOR = 10000;
|
||||
uint256 public constant MAX_SPREAD_BASIS_POINTS = 200; // 最大2%价差
|
||||
uint256 public constant MAX_SPREAD_BASIS_POINTS = 200;
|
||||
|
||||
address public usdcAddress;
|
||||
|
||||
uint256 public maxPriceChangeBps; // 5% 最大价格变动
|
||||
uint256 public maxPriceChangeBps;
|
||||
|
||||
AggregatorV3Interface internal usdcPriceFeed;
|
||||
|
||||
@@ -58,7 +58,7 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
|
||||
usdcAddress = _usdcAddress;
|
||||
usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed);
|
||||
gov = msg.sender;
|
||||
maxPriceChangeBps = 500; // 5% 最大价格变动
|
||||
maxPriceChangeBps = 500;
|
||||
}
|
||||
|
||||
function setUSDCAddress(address _usdcAddress) external onlyGov {
|
||||
@@ -78,7 +78,7 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
|
||||
}
|
||||
|
||||
function setMaxPriceChangeBps(uint256 _maxPriceChangeBps) external onlyGov {
|
||||
if (_maxPriceChangeBps > 2000) revert MaxChangeTooHigh(); // 最大20%
|
||||
if (_maxPriceChangeBps > 2000) revert MaxChangeTooHigh();
|
||||
maxPriceChangeBps = _maxPriceChangeBps;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +259,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
|
||||
uint256 usdyAmountAfterFees = amountAfterFees * price / PRICE_PRECISION;
|
||||
usdyAmountAfterFees = _adjustForDecimals(usdyAmountAfterFees, _token, usdy);
|
||||
|
||||
// 手续费直接留在池子中:全部代币加入poolAmount,但只铸造扣费后的USDY
|
||||
_increasePoolAmount(_token, tokenAmount);
|
||||
_increaseUsdyAmount(_token, usdyAmountAfterFees);
|
||||
|
||||
@@ -436,7 +435,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
|
||||
address _tokenOut,
|
||||
uint256 _usdyAmount
|
||||
) private view returns (uint256) {
|
||||
// 稳定币交换是指两个代币都是稳定币(如 USDC <-> USDT)
|
||||
bool isStableSwap = stableTokens[_tokenIn] && stableTokens[_tokenOut];
|
||||
uint256 baseBps = isStableSwap ? stableSwapFeeBasisPoints : swapFeeBasisPoints;
|
||||
uint256 taxBps = isStableSwap ? stableTaxBasisPoints : taxBasisPoints;
|
||||
|
||||
@@ -201,12 +201,7 @@ contract YTAssetVault is
|
||||
revert HardCapExceeded();
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), _usdcAmount);
|
||||
=======
|
||||
// 转入USDC
|
||||
IERC20(usdcAddress).transferFrom(msg.sender, address(this), _usdcAmount);
|
||||
>>>>>>> usdc
|
||||
|
||||
_mint(msg.sender, ytAmount);
|
||||
|
||||
@@ -382,12 +377,7 @@ contract YTAssetVault is
|
||||
managedAssets -= _amount;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
||||
=======
|
||||
// 从manager转入USDC到合约
|
||||
IERC20(usdcAddress).transferFrom(msg.sender, address(this), _amount);
|
||||
>>>>>>> usdc
|
||||
|
||||
emit AssetsDeposited(_amount);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user