update lending contract

This commit is contained in:
2025-12-29 15:30:10 +08:00
parent 6fe3fa93b3
commit b1b3b07b21
30 changed files with 53 additions and 101 deletions

File diff suppressed because one or more lines are too long

View File

@@ -31,7 +31,6 @@ interface ILending {
function withdraw(uint256 amount) external; function withdraw(uint256 amount) external;
function supplyCollateral(address asset, uint256 amount) external; function supplyCollateral(address asset, uint256 amount) external;
function withdrawCollateral(address asset, uint256 amount) external; function withdrawCollateral(address asset, uint256 amount) external;
function borrow(uint256 amount) external;
function absorb(address borrower) external; function absorb(address borrower) external;
function absorbMultiple(address absorber, address[] calldata accounts) external; function absorbMultiple(address absorber, address[] calldata accounts) external;
function buyCollateral(address asset, uint256 minAmount, uint256 baseAmount, address recipient) external; function buyCollateral(address asset, uint256 minAmount, uint256 baseAmount, address recipient) external;

View File

@@ -68,8 +68,7 @@ contract Configurator is
// 防止修改不可变参数 // 防止修改不可变参数
if (oldConfiguration.baseToken != address(0) && if (oldConfiguration.baseToken != address(0) &&
(oldConfiguration.baseToken != newConfiguration.baseToken || oldConfiguration.baseToken != newConfiguration.baseToken)
oldConfiguration.trackingIndexScale != newConfiguration.trackingIndexScale))
revert ConfigurationAlreadyExists(); revert ConfigurationAlreadyExists();
// 删除旧的资产配置 // 删除旧的资产配置
@@ -87,7 +86,6 @@ contract Configurator is
configuratorParams[lendingProxy].borrowPerYearInterestRateSlopeHigh = newConfiguration.borrowPerYearInterestRateSlopeHigh; configuratorParams[lendingProxy].borrowPerYearInterestRateSlopeHigh = newConfiguration.borrowPerYearInterestRateSlopeHigh;
configuratorParams[lendingProxy].borrowPerYearInterestRateBase = newConfiguration.borrowPerYearInterestRateBase; configuratorParams[lendingProxy].borrowPerYearInterestRateBase = newConfiguration.borrowPerYearInterestRateBase;
configuratorParams[lendingProxy].storeFrontPriceFactor = newConfiguration.storeFrontPriceFactor; configuratorParams[lendingProxy].storeFrontPriceFactor = newConfiguration.storeFrontPriceFactor;
configuratorParams[lendingProxy].trackingIndexScale = newConfiguration.trackingIndexScale;
configuratorParams[lendingProxy].baseBorrowMin = newConfiguration.baseBorrowMin; configuratorParams[lendingProxy].baseBorrowMin = newConfiguration.baseBorrowMin;
configuratorParams[lendingProxy].targetReserves = newConfiguration.targetReserves; configuratorParams[lendingProxy].targetReserves = newConfiguration.targetReserves;

View File

@@ -63,7 +63,6 @@ contract Lending is
// 设置其他参数 // 设置其他参数
storeFrontPriceFactor = config.storeFrontPriceFactor; storeFrontPriceFactor = config.storeFrontPriceFactor;
trackingIndexScale = config.trackingIndexScale;
baseBorrowMin = config.baseBorrowMin; baseBorrowMin = config.baseBorrowMin;
targetReserves = config.targetReserves; targetReserves = config.targetReserves;
@@ -268,49 +267,6 @@ contract Lending is
emit WithdrawCollateral(msg.sender, msg.sender, asset, amount); 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);
emit Withdraw(msg.sender, msg.sender, amount);
}
/** /**
* @notice 清算不良债务(内部实现) * @notice 清算不良债务(内部实现)
* @dev 当用户抵押品由于乘以liquidateCollateralFactor后小于债务价值时会进行清算清算后如果实际抵押品价值乘以liquidateCollateralFactor大于债务价值则将差额部分作为用户本金本金以baseToken显示否则将差额部分作为坏账由协议承担 * @dev 当用户抵押品由于乘以liquidateCollateralFactor后小于债务价值时会进行清算清算后如果实际抵押品价值乘以liquidateCollateralFactor大于债务价值则将差额部分作为用户本金本金以baseToken显示否则将差额部分作为坏账由协议承担
@@ -338,7 +294,7 @@ contract Lending is
AssetConfig memory assetConfig = assetConfigs[asset]; AssetConfig memory assetConfig = assetConfigs[asset];
uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset); uint256 assetPrice = IYTLendingPriceFeed(lendingPriceSource).getPrice(asset);
// 计算抵押品价值USD8位精度- 用于事件记录 // 计算抵押品价值-用于事件记录
uint256 assetScale = 10 ** assetConfig.decimals; uint256 assetScale = 10 ** assetConfig.decimals;
uint256 collateralValueUSD = (collateralAmount * assetPrice) / assetScale; uint256 collateralValueUSD = (collateralAmount * assetPrice) / assetScale;

View File

@@ -32,7 +32,6 @@ contract LendingConfiguration {
// 其他核心参数 // 其他核心参数
uint64 storeFrontPriceFactor; // 清算价格折扣 uint64 storeFrontPriceFactor; // 清算价格折扣
uint64 trackingIndexScale; // 追踪索引比例
uint104 baseBorrowMin; // 最小借款额 uint104 baseBorrowMin; // 最小借款额
uint104 targetReserves; // 目标储备金 uint104 targetReserves; // 目标储备金

View File

@@ -26,7 +26,6 @@ abstract contract LendingStorage is LendingConfiguration {
// 清算参数 // 清算参数
uint64 public storeFrontPriceFactor; uint64 public storeFrontPriceFactor;
uint64 public trackingIndexScale;
uint104 public baseBorrowMin; uint104 public baseBorrowMin;
uint104 public targetReserves; uint104 public targetReserves;

View File

@@ -614,15 +614,16 @@
│ 计划: 借款 1,000,000 USDC │ │ 计划: 借款 1,000,000 USDC │
└────────────────────────────┬────────────────────────────────────────┘ └────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 borrow(1000000e6) │ 1. 调用 withdraw(1000000e6) 进行借款
┌─────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────┐
│ Lending.borrow() │ Lending.withdraw() │
│ ───────────────────────────────────────────────────────────────── │ │ ───────────────────────────────────────────────────────────────── │
│ function borrow(uint256 amount) │ function withdraw(uint256 amount) │
│ • 非重入保护: nonReentrant │ │ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │ │ • 暂停检查: whenNotPaused │
│ • 参数: 1,000,000 USDC (1000000e6) │ │ • 参数: 1,000,000 USDC (1000000e6) │
│ • 注: withdraw 会自动处理借款逻辑 │
└────────────────────────────┬────────────────────────────────────────┘ └────────────────────────────┬────────────────────────────────────────┘
│ 2. 计提利息 │ 2. 计提利息
@@ -1824,7 +1825,6 @@ Configuration {
// 清算参数 // 清算参数
storeFrontPriceFactor: 0.95e18 // 清算购买折扣 5% storeFrontPriceFactor: 0.95e18 // 清算购买折扣 5%
trackingIndexScale: 1e15 // 追踪索引比例
baseBorrowMin: 100e6 // 最小借款 100 USDC baseBorrowMin: 100e6 // 最小借款 100 USDC
targetReserves: 5000000e6 // 目标储备 500万 targetReserves: 5000000e6 // 目标储备 500万

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"abi":[],"bytecode":{"object":"0x608080604052346013576039908160188239f35b5f80fdfe5f80fdfea264697066735822122077cd1a16fabe0cd560a7f2461fc6b4b68370dcbaef48f0fccf2effe4f34afa2464736f6c634300081e0033","sourceMap":"138:1737:11:-:0;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x5f80fdfea264697066735822122077cd1a16fabe0cd560a7f2461fc6b4b68370dcbaef48f0fccf2effe4f34afa2464736f6c634300081e0033","sourceMap":"138:1737:11:-:0;;","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.30+commit.73712a01\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"LendingConfiguration\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"\\u501f\\u8d37\\u6c60\\u914d\\u7f6e\\u7ed3\\u6784\\u4f53\\u5b9a\\u4e49\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/ytLending/LendingConfiguration.sol\":\"LendingConfiguration\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@arbitrum/=node_modules/@arbitrum/\",\":@chainlink/=node_modules/@chainlink/\",\":@ensdomains/=node_modules/@ensdomains/\",\":@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/\",\":@offchainlabs/=node_modules/@offchainlabs/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@scroll-tech/=node_modules/@scroll-tech/\",\":@zksync/=node_modules/@zksync/\",\":forge-std/=lib/forge-std/src/\",\":hardhat/=node_modules/hardhat/\",\":solady/=node_modules/solady/\"],\"viaIR\":true},\"sources\":{\"contracts/ytLending/LendingConfiguration.sol\":{\"keccak256\":\"0xbf3515c0458645c3423e888c0c38e495daf60de795e2a7a9c3db525d77bfb126\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d20e885e5a57d63cbe240fdb91d09f073e5886233159096ff151692546cd0d72\",\"dweb:/ipfs/QmYCPvxyYrsLka44r3yi7HBrvfBgsQ3zovbTZCNiHHEy1H\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.30+commit.73712a01"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@arbitrum/=node_modules/@arbitrum/","@chainlink/=node_modules/@chainlink/","@ensdomains/=node_modules/@ensdomains/","@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/","@offchainlabs/=node_modules/@offchainlabs/","@openzeppelin/=node_modules/@openzeppelin/","@scroll-tech/=node_modules/@scroll-tech/","@zksync/=node_modules/@zksync/","forge-std/=lib/forge-std/src/","hardhat/=node_modules/hardhat/","solady/=node_modules/solady/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"contracts/ytLending/LendingConfiguration.sol":"LendingConfiguration"},"evmVersion":"prague","libraries":{},"viaIR":true},"sources":{"contracts/ytLending/LendingConfiguration.sol":{"keccak256":"0xbf3515c0458645c3423e888c0c38e495daf60de795e2a7a9c3db525d77bfb126","urls":["bzz-raw://d20e885e5a57d63cbe240fdb91d09f073e5886233159096ff151692546cd0d72","dweb:/ipfs/QmYCPvxyYrsLka44r3yi7HBrvfBgsQ3zovbTZCNiHHEy1H"],"license":"MIT"}},"version":1},"id":11} {"abi":[],"bytecode":{"object":"0x608080604052346013576039908160188239f35b5f80fdfe5f80fdfea2646970667358221220505f442edb372b2168112b3b658b55a2b06b4abff3ee31fd304124bf154531e564736f6c634300081e0033","sourceMap":"138:1659:6:-:0;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x5f80fdfea2646970667358221220505f442edb372b2168112b3b658b55a2b06b4abff3ee31fd304124bf154531e564736f6c634300081e0033","sourceMap":"138:1659:6:-:0;;","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.30+commit.73712a01\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"LendingConfiguration\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"\\u501f\\u8d37\\u6c60\\u914d\\u7f6e\\u7ed3\\u6784\\u4f53\\u5b9a\\u4e49\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/ytLending/LendingConfiguration.sol\":\"LendingConfiguration\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@arbitrum/=node_modules/@arbitrum/\",\":@chainlink/=node_modules/@chainlink/\",\":@ensdomains/=node_modules/@ensdomains/\",\":@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/\",\":@offchainlabs/=node_modules/@offchainlabs/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@scroll-tech/=node_modules/@scroll-tech/\",\":@zksync/=node_modules/@zksync/\",\":forge-std/=lib/forge-std/src/\",\":hardhat/=node_modules/hardhat/\",\":solady/=node_modules/solady/\"],\"viaIR\":true},\"sources\":{\"contracts/ytLending/LendingConfiguration.sol\":{\"keccak256\":\"0x3c6287a9a4dc044ebc3de6b775de2f8804b527e645eef6f2dcea149541ed4bab\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8f72c84ef7dbf0f9400c1830fa17e4ecd0205391c58c4b9180641af8e40a92bc\",\"dweb:/ipfs/QmPP3NgAwZHt2SvyfyEiayF7svacWfkXhnX3sJr7xoUNBP\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.30+commit.73712a01"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@arbitrum/=node_modules/@arbitrum/","@chainlink/=node_modules/@chainlink/","@ensdomains/=node_modules/@ensdomains/","@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/","@offchainlabs/=node_modules/@offchainlabs/","@openzeppelin/=node_modules/@openzeppelin/","@scroll-tech/=node_modules/@scroll-tech/","@zksync/=node_modules/@zksync/","forge-std/=lib/forge-std/src/","hardhat/=node_modules/hardhat/","solady/=node_modules/solady/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"contracts/ytLending/LendingConfiguration.sol":"LendingConfiguration"},"evmVersion":"prague","libraries":{},"viaIR":true},"sources":{"contracts/ytLending/LendingConfiguration.sol":{"keccak256":"0x3c6287a9a4dc044ebc3de6b775de2f8804b527e645eef6f2dcea149541ed4bab","urls":["bzz-raw://8f72c84ef7dbf0f9400c1830fa17e4ecd0205391c58c4b9180641af8e40a92bc","dweb:/ipfs/QmPP3NgAwZHt2SvyfyEiayF7svacWfkXhnX3sJr7xoUNBP"],"license":"MIT"}},"version":1},"id":6}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"id":"059f33eca1d35bb3","source_id_to_path":{"0":"contracts/interfaces/ILending.sol","1":"contracts/interfaces/IUSDY.sol","2":"contracts/interfaces/IYTAssetVault.sol","3":"contracts/interfaces/IYTLPToken.sol","4":"contracts/interfaces/IYTLendingPriceFeed.sol","5":"contracts/interfaces/IYTPoolManager.sol","6":"contracts/interfaces/IYTPriceFeed.sol","7":"contracts/interfaces/IYTVault.sol","8":"contracts/ytLending/Configurator.sol","9":"contracts/ytLending/ConfiguratorStorage.sol","10":"contracts/ytLending/Lending.sol","11":"contracts/ytLending/LendingConfiguration.sol","12":"contracts/ytLending/LendingFactory.sol","13":"contracts/ytLending/LendingMath.sol","14":"contracts/ytLending/LendingPriceFeed.sol","15":"contracts/ytLending/LendingStorage.sol","16":"contracts/ytLp/core/YTPoolManager.sol","17":"contracts/ytLp/core/YTPriceFeed.sol","18":"contracts/ytLp/core/YTRewardRouter.sol","19":"contracts/ytLp/core/YTVault.sol","20":"contracts/ytLp/tokens/USDY.sol","21":"contracts/ytLp/tokens/YTLPToken.sol","22":"contracts/ytVault/YTAssetFactory.sol","23":"contracts/ytVault/YTAssetVault.sol","24":"lib/forge-std/src/Base.sol","25":"lib/forge-std/src/StdAssertions.sol","26":"lib/forge-std/src/StdChains.sol","27":"lib/forge-std/src/StdCheats.sol","28":"lib/forge-std/src/StdConstants.sol","29":"lib/forge-std/src/StdError.sol","30":"lib/forge-std/src/StdInvariant.sol","31":"lib/forge-std/src/StdJson.sol","32":"lib/forge-std/src/StdMath.sol","33":"lib/forge-std/src/StdStorage.sol","34":"lib/forge-std/src/StdStyle.sol","35":"lib/forge-std/src/StdToml.sol","36":"lib/forge-std/src/StdUtils.sol","37":"lib/forge-std/src/Test.sol","38":"lib/forge-std/src/Vm.sol","39":"lib/forge-std/src/console.sol","40":"lib/forge-std/src/console2.sol","41":"lib/forge-std/src/interfaces/IMulticall3.sol","42":"lib/forge-std/src/safeconsole.sol","43":"node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol","44":"node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","45":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","46":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol","47":"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","48":"node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","49":"node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","50":"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","51":"node_modules/@openzeppelin/contracts/access/Ownable.sol","52":"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol","53":"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol","54":"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol","55":"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol","56":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol","57":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol","58":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol","59":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol","60":"node_modules/@openzeppelin/contracts/proxy/Proxy.sol","61":"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol","62":"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol","63":"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol","64":"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","65":"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","66":"node_modules/@openzeppelin/contracts/utils/Address.sol","67":"node_modules/@openzeppelin/contracts/utils/Context.sol","68":"node_modules/@openzeppelin/contracts/utils/Errors.sol","69":"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol","70":"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol","71":"test/YtLending.t.sol","72":"test/YtLp.t.sol","73":"test/YtVault.t.sol"},"language":"Solidity"}

View File

@@ -0,0 +1 @@
{"id":"3b7265d3b62873e1","source_id_to_path":{"0":"contracts/interfaces/ILending.sol","1":"contracts/interfaces/IYTAssetVault.sol","2":"contracts/interfaces/IYTLendingPriceFeed.sol","3":"contracts/ytLending/Configurator.sol","4":"contracts/ytLending/ConfiguratorStorage.sol","5":"contracts/ytLending/Lending.sol","6":"contracts/ytLending/LendingConfiguration.sol","7":"contracts/ytLending/LendingFactory.sol","8":"contracts/ytLending/LendingMath.sol","9":"contracts/ytLending/LendingPriceFeed.sol","10":"contracts/ytLending/LendingStorage.sol","11":"contracts/ytVault/YTAssetFactory.sol","12":"contracts/ytVault/YTAssetVault.sol","13":"lib/forge-std/src/Base.sol","14":"lib/forge-std/src/StdAssertions.sol","15":"lib/forge-std/src/StdChains.sol","16":"lib/forge-std/src/StdCheats.sol","17":"lib/forge-std/src/StdConstants.sol","18":"lib/forge-std/src/StdError.sol","19":"lib/forge-std/src/StdInvariant.sol","20":"lib/forge-std/src/StdJson.sol","21":"lib/forge-std/src/StdMath.sol","22":"lib/forge-std/src/StdStorage.sol","23":"lib/forge-std/src/StdStyle.sol","24":"lib/forge-std/src/StdToml.sol","25":"lib/forge-std/src/StdUtils.sol","26":"lib/forge-std/src/Test.sol","27":"lib/forge-std/src/Vm.sol","28":"lib/forge-std/src/console.sol","29":"lib/forge-std/src/console2.sol","30":"lib/forge-std/src/interfaces/IMulticall3.sol","31":"lib/forge-std/src/safeconsole.sol","32":"node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol","33":"node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","34":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","35":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol","36":"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","37":"node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","38":"node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","39":"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","40":"node_modules/@openzeppelin/contracts/access/Ownable.sol","41":"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol","42":"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol","43":"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol","44":"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol","45":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol","46":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol","47":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol","48":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol","49":"node_modules/@openzeppelin/contracts/proxy/Proxy.sol","50":"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol","51":"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol","52":"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol","53":"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","54":"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","55":"node_modules/@openzeppelin/contracts/utils/Address.sol","56":"node_modules/@openzeppelin/contracts/utils/Context.sol","57":"node_modules/@openzeppelin/contracts/utils/Errors.sol","58":"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol","59":"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol","60":"test/YtLending.t.sol"},"language":"Solidity"}

View File

@@ -0,0 +1 @@
{"id":"ae1b940c327ca4d8","source_id_to_path":{"0":"contracts/interfaces/ILending.sol","1":"contracts/interfaces/IYTAssetVault.sol","2":"contracts/interfaces/IYTLendingPriceFeed.sol","3":"contracts/ytLending/Configurator.sol","4":"contracts/ytLending/ConfiguratorStorage.sol","5":"contracts/ytLending/Lending.sol","6":"contracts/ytLending/LendingConfiguration.sol","7":"contracts/ytLending/LendingFactory.sol","8":"contracts/ytLending/LendingMath.sol","9":"contracts/ytLending/LendingPriceFeed.sol","10":"contracts/ytLending/LendingStorage.sol","11":"contracts/ytVault/YTAssetFactory.sol","12":"contracts/ytVault/YTAssetVault.sol","13":"lib/forge-std/src/Base.sol","14":"lib/forge-std/src/StdAssertions.sol","15":"lib/forge-std/src/StdChains.sol","16":"lib/forge-std/src/StdCheats.sol","17":"lib/forge-std/src/StdConstants.sol","18":"lib/forge-std/src/StdError.sol","19":"lib/forge-std/src/StdInvariant.sol","20":"lib/forge-std/src/StdJson.sol","21":"lib/forge-std/src/StdMath.sol","22":"lib/forge-std/src/StdStorage.sol","23":"lib/forge-std/src/StdStyle.sol","24":"lib/forge-std/src/StdToml.sol","25":"lib/forge-std/src/StdUtils.sol","26":"lib/forge-std/src/Test.sol","27":"lib/forge-std/src/Vm.sol","28":"lib/forge-std/src/console.sol","29":"lib/forge-std/src/console2.sol","30":"lib/forge-std/src/interfaces/IMulticall3.sol","31":"lib/forge-std/src/safeconsole.sol","32":"node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol","33":"node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","34":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","35":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol","36":"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","37":"node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","38":"node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","39":"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","40":"node_modules/@openzeppelin/contracts/access/Ownable.sol","41":"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol","42":"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol","43":"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol","44":"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol","45":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol","46":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol","47":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol","48":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol","49":"node_modules/@openzeppelin/contracts/proxy/Proxy.sol","50":"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol","51":"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol","52":"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol","53":"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","54":"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","55":"node_modules/@openzeppelin/contracts/utils/Address.sol","56":"node_modules/@openzeppelin/contracts/utils/Context.sol","57":"node_modules/@openzeppelin/contracts/utils/Errors.sol","58":"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol","59":"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol","60":"test/YtLending.t.sol"},"language":"Solidity"}

View File

@@ -104,7 +104,6 @@ async function main() {
borrowPerYearInterestRateBase: ethers.parseUnits("0.015", 18), // 1.5% 基础 borrowPerYearInterestRateBase: ethers.parseUnits("0.015", 18), // 1.5% 基础
storeFrontPriceFactor: ethers.parseUnits("0.5", 18), // 50% 清算折扣 storeFrontPriceFactor: ethers.parseUnits("0.5", 18), // 50% 清算折扣
trackingIndexScale: ethers.parseUnits("1", 15), // 10^15 比例
baseBorrowMin: ethers.parseUnits("100", USDC.decimals), // 最小借 100 USDC baseBorrowMin: ethers.parseUnits("100", USDC.decimals), // 最小借 100 USDC
targetReserves: ethers.parseUnits("5000000", USDC.decimals), // 目标储备 500 万 targetReserves: ethers.parseUnits("5000000", USDC.decimals), // 目标储备 500 万

View File

@@ -156,7 +156,6 @@ contract YtLendingTest is Test {
borrowPerYearInterestRateSlopeHigh: BORROW_RATE_HIGH, borrowPerYearInterestRateSlopeHigh: BORROW_RATE_HIGH,
borrowPerYearInterestRateBase: BORROW_RATE_BASE, borrowPerYearInterestRateBase: BORROW_RATE_BASE,
storeFrontPriceFactor: STORE_FRONT_PRICE_FACTOR, storeFrontPriceFactor: STORE_FRONT_PRICE_FACTOR,
trackingIndexScale: 1e15,
baseBorrowMin: uint104(BASE_BORROW_MIN), baseBorrowMin: uint104(BASE_BORROW_MIN),
targetReserves: uint104(TARGET_RESERVES), targetReserves: uint104(TARGET_RESERVES),
assetConfigs: assetConfigs assetConfigs: assetConfigs
@@ -319,7 +318,7 @@ contract YtLendingTest is Test {
// 借款 $16,000 USDC80% LTV // 借款 $16,000 USDC80% LTV
uint256 borrowAmount = 16000e6; uint256 borrowAmount = 16000e6;
lending.borrow(borrowAmount); lending.withdraw(borrowAmount);
vm.stopPrank(); vm.stopPrank();
// 验证 // 验证
@@ -335,7 +334,7 @@ contract YtLendingTest is Test {
// Alice 尝试无抵押借款 // Alice 尝试无抵押借款
vm.prank(alice); vm.prank(alice);
vm.expectRevert(ILending.InsufficientCollateral.selector); vm.expectRevert(ILending.InsufficientCollateral.selector);
lending.borrow(1000e6); lending.withdraw(1000e6);
} }
function test_09_Borrow_FailBelowMinimum() public { function test_09_Borrow_FailBelowMinimum() public {
@@ -345,7 +344,7 @@ contract YtLendingTest is Test {
// 尝试借款低于最小值 (< 100 USDC) // 尝试借款低于最小值 (< 100 USDC)
vm.expectRevert(ILending.BorrowTooSmall.selector); vm.expectRevert(ILending.BorrowTooSmall.selector);
lending.borrow(50e6); lending.withdraw(50e6);
vm.stopPrank(); vm.stopPrank();
} }
@@ -361,7 +360,7 @@ contract YtLendingTest is Test {
// Bob 存入 10 YTToken借 8,000 USDC // Bob 存入 10 YTToken借 8,000 USDC
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
// 时间前进 365 天 // 时间前进 365 天
@@ -398,7 +397,7 @@ contract YtLendingTest is Test {
// Bob 借款 // Bob 借款
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
// 每月触发一次利息累积(模拟复利) // 每月触发一次利息累积(模拟复利)
@@ -426,7 +425,7 @@ contract YtLendingTest is Test {
// Bob 存入 10 YTToken (价值 $20,000),借 10,000 USDC // Bob 存入 10 YTToken (价值 $20,000),借 10,000 USDC
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(10000e6); lending.withdraw(10000e6);
vm.stopPrank(); vm.stopPrank();
// LTV = 50%,健康 // LTV = 50%,健康
@@ -441,7 +440,7 @@ contract YtLendingTest is Test {
// Bob 存入 10 YTToken借 16,000 USDC80% LTV // Bob 存入 10 YTToken借 16,000 USDC80% LTV
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(16000e6); lending.withdraw(16000e6);
vm.stopPrank(); vm.stopPrank();
// YTToken 价格暴跌到 $1,800 // YTToken 价格暴跌到 $1,800
@@ -463,7 +462,7 @@ contract YtLendingTest is Test {
// 1. Bob 建立借款头寸 // 1. Bob 建立借款头寸
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); // 10 YTToken @ $2000 = $20,000 lending.supplyCollateral(address(ytVault), 10e18); // 10 YTToken @ $2000 = $20,000
lending.borrow(16000e6); // $16,00080% LTV lending.withdraw(16000e6); // $16,00080% LTV
vm.stopPrank(); vm.stopPrank();
// 2. 计算精确的清算价格1e30 精度) // 2. 计算精确的清算价格1e30 精度)
@@ -507,7 +506,7 @@ contract YtLendingTest is Test {
// 1. Bob 建立不良头寸 // 1. Bob 建立不良头寸
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); // 10 YTToken @ $2000 = $20,000 lending.supplyCollateral(address(ytVault), 10e18); // 10 YTToken @ $2000 = $20,000
lending.borrow(16000e6); // $16,000 lending.withdraw(16000e6); // $16,000
vm.stopPrank(); vm.stopPrank();
// 2. YTToken 价格跌到 $1,750 // 2. YTToken 价格跌到 $1,750
@@ -544,12 +543,12 @@ contract YtLendingTest is Test {
// 1. Bob 和 Charlie 都建立不良头寸 // 1. Bob 和 Charlie 都建立不良头寸
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(16000e6); lending.withdraw(16000e6);
vm.stopPrank(); vm.stopPrank();
vm.startPrank(charlie); vm.startPrank(charlie);
lending.supplyCollateral(address(ytVault), 5e18); lending.supplyCollateral(address(ytVault), 5e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
// 2. 价格下跌 // 2. 价格下跌
@@ -581,7 +580,7 @@ contract YtLendingTest is Test {
// 1. 先清算一个账户,产生抵押品库存 // 1. 先清算一个账户,产生抵押品库存
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(16000e6); lending.withdraw(16000e6);
vm.stopPrank(); vm.stopPrank();
ytFactory.updateVaultPrices(address(ytVault), 1750e30); ytFactory.updateVaultPrices(address(ytVault), 1750e30);
@@ -619,7 +618,7 @@ contract YtLendingTest is Test {
// 设置清算库存 // 设置清算库存
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(16000e6); lending.withdraw(16000e6);
vm.stopPrank(); vm.stopPrank();
ytFactory.updateVaultPrices(address(ytVault), 1750e30); ytFactory.updateVaultPrices(address(ytVault), 1750e30);
@@ -649,7 +648,7 @@ contract YtLendingTest is Test {
// Bob 小额借款 // Bob 小额借款
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); // 10 YTToken @ $2000 = $20,000 lending.supplyCollateral(address(ytVault), 10e18); // 10 YTToken @ $2000 = $20,000
lending.borrow(100e6); // 只借 $100 lending.withdraw(100e6); // 只借 $100
vm.stopPrank(); vm.stopPrank();
// 让时间流逝以累积利息,增加 reserves // 让时间流逝以累积利息,增加 reserves
@@ -701,7 +700,7 @@ contract YtLendingTest is Test {
// Bob 借 5,000 USDC // Bob 借 5,000 USDC
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(5000e6); lending.withdraw(5000e6);
vm.stopPrank(); vm.stopPrank();
// reserves = balance - totalSupply + totalBorrow // reserves = balance - totalSupply + totalBorrow
@@ -719,7 +718,7 @@ contract YtLendingTest is Test {
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
// 时间流逝 // 时间流逝
@@ -746,7 +745,7 @@ contract YtLendingTest is Test {
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
vm.warp(block.timestamp + 365 days); vm.warp(block.timestamp + 365 days);
@@ -799,7 +798,7 @@ contract YtLendingTest is Test {
// Bob 借 8,000 USDC // Bob 借 8,000 USDC
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
// 利用率 = 8000 / 10000 = 80% // 利用率 = 8000 / 10000 = 80%
@@ -813,7 +812,7 @@ contract YtLendingTest is Test {
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(5000e6); lending.withdraw(5000e6);
vm.stopPrank(); vm.stopPrank();
uint64 supplyRate = lending.getSupplyRate(); uint64 supplyRate = lending.getSupplyRate();
@@ -831,7 +830,7 @@ contract YtLendingTest is Test {
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(8000e6); lending.withdraw(8000e6);
vm.stopPrank(); vm.stopPrank();
uint64 borrowRate = lending.getBorrowRate(); uint64 borrowRate = lending.getBorrowRate();
@@ -869,7 +868,7 @@ contract YtLendingTest is Test {
lending.supplyCollateral(address(ytVault), 10e18); // $20,000 lending.supplyCollateral(address(ytVault), 10e18); // $20,000
// 借款 $16,000正好 80% // 借款 $16,000正好 80%
lending.borrow(16000e6); lending.withdraw(16000e6);
// 应该成功 // 应该成功
assertEq(lending.borrowBalanceOf(alice), 16000e6, "Should borrow at max LTV"); assertEq(lending.borrowBalanceOf(alice), 16000e6, "Should borrow at max LTV");
@@ -887,7 +886,7 @@ contract YtLendingTest is Test {
// 尝试借 $16,001超过 80% // 尝试借 $16,001超过 80%
vm.expectRevert(ILending.InsufficientCollateral.selector); vm.expectRevert(ILending.InsufficientCollateral.selector);
lending.borrow(16001e6); lending.withdraw(16001e6);
vm.stopPrank(); vm.stopPrank();
} }
@@ -899,7 +898,7 @@ contract YtLendingTest is Test {
// Alice 借款后尝试取出抵押品 // Alice 借款后尝试取出抵押品
vm.startPrank(alice); vm.startPrank(alice);
lending.supplyCollateral(address(ytVault), 10e18); lending.supplyCollateral(address(ytVault), 10e18);
lending.borrow(16000e6); lending.withdraw(16000e6);
// 尝试取出 1 YTToken 会破坏抵押率 // 尝试取出 1 YTToken 会破坏抵押率
vm.expectRevert(ILending.InsufficientCollateral.selector); vm.expectRevert(ILending.InsufficientCollateral.selector);
@@ -929,13 +928,13 @@ contract YtLendingTest is Test {
// 2. Bob 抵押借款 // 2. Bob 抵押借款
vm.startPrank(bob); vm.startPrank(bob);
lending.supplyCollateral(address(ytVault), 20e18); // $40,000 lending.supplyCollateral(address(ytVault), 20e18); // $40,000
lending.borrow(30000e6); // 75% LTV lending.withdraw(30000e6); // 75% LTV
vm.stopPrank(); vm.stopPrank();
// 3. Charlie 也抵押借款(更激进,容易被清算) // 3. Charlie 也抵押借款(更激进,容易被清算)
vm.startPrank(charlie); vm.startPrank(charlie);
lending.supplyCollateral(address(ytVault), 5e18); // $10,000 lending.supplyCollateral(address(ytVault), 5e18); // $10,000
lending.borrow(7900e6); // 79% LTV lending.withdraw(7900e6); // 79% LTV
vm.stopPrank(); vm.stopPrank();
// 4. 时间流逝,利息累积 // 4. 时间流逝,利息累积