update lending contract

This commit is contained in:
2025-12-29 15:38:25 +08:00
parent dba8fa1af0
commit 2cd584be76
28 changed files with 50 additions and 88 deletions

File diff suppressed because one or more lines are too long

View File

@@ -27,7 +27,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

@@ -50,8 +50,7 @@ contract Configurator is
Configuration memory oldConfiguration = configuratorParams[lendingProxy]; Configuration memory oldConfiguration = configuratorParams[lendingProxy];
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();
delete configuratorParams[lendingProxy]; delete configuratorParams[lendingProxy];
@@ -67,7 +66,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

@@ -51,7 +51,6 @@ contract Lending is
borrowPerSecondInterestRateBase = uint64(config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR); borrowPerSecondInterestRateBase = uint64(config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR);
storeFrontPriceFactor = config.storeFrontPriceFactor; storeFrontPriceFactor = config.storeFrontPriceFactor;
trackingIndexScale = config.trackingIndexScale;
baseBorrowMin = config.baseBorrowMin; baseBorrowMin = config.baseBorrowMin;
targetReserves = config.targetReserves; targetReserves = config.targetReserves;
@@ -211,36 +210,6 @@ contract Lending is
emit WithdrawCollateral(msg.sender, msg.sender, asset, amount); emit WithdrawCollateral(msg.sender, msg.sender, asset, amount);
} }
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);
}
function _absorbInternal(address absorber, address borrower) internal { function _absorbInternal(address absorber, address borrower) internal {
if (!isLiquidatable(borrower)) revert NotLiquidatable(); if (!isLiquidatable(borrower)) revert NotLiquidatable();

View File

@@ -26,7 +26,6 @@ contract LendingConfiguration {
uint64 borrowPerYearInterestRateBase; // 借款基础利率 uint64 borrowPerYearInterestRateBase; // 借款基础利率
uint64 storeFrontPriceFactor; // 清算价格折扣 uint64 storeFrontPriceFactor; // 清算价格折扣
uint64 trackingIndexScale; // 追踪索引比例
uint104 baseBorrowMin; // 最小借款额 uint104 baseBorrowMin; // 最小借款额
uint104 targetReserves; // 目标储备金 uint104 targetReserves; // 目标储备金

View File

@@ -19,7 +19,6 @@ abstract contract LendingStorage is LendingConfiguration {
uint64 public borrowPerSecondInterestRateBase; uint64 public borrowPerSecondInterestRateBase;
uint64 public storeFrontPriceFactor; uint64 public storeFrontPriceFactor;
uint64 public trackingIndexScale;
uint104 public baseBorrowMin; uint104 public baseBorrowMin;
uint104 public targetReserves; uint104 public targetReserves;

View File

@@ -602,7 +602,7 @@
--- ---
## 5. 借款流程(Borrow ## 5. 借款流程(Withdraw如果余额不足会自动借款
``` ```
┌─────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────┐
@@ -614,12 +614,12 @@
│ 计划: 借款 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) │
@@ -1824,7 +1824,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":"0x608080604052346013576039908160188239f35b5f80fdfe5f80fdfea2646970667358221220b04c3c7a0eacf47d0507fbb0a0a2dcb975a2a0626145dd2c26e6cc0eba61456664736f6c634300081e0033","sourceMap":"57:1677:11:-:0;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x5f80fdfea2646970667358221220b04c3c7a0eacf47d0507fbb0a0a2dcb975a2a0626145dd2c26e6cc0eba61456664736f6c634300081e0033","sourceMap":"57:1677:11:-:0;;","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.30+commit.73712a01\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"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\":\"0xad0926447becfa7fd4d742ab3e8e30eba2913a991e2dff6362ccabdf67af3220\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a7f540e927a359c0196a7e0385c1684f31cad5ec5d75143e6c869327d95ea436\",\"dweb:/ipfs/QmfExMH3ajtieteArNbCgnNFFqybz2k7WCm2AR6yheD6TG\"]}},\"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":"0xad0926447becfa7fd4d742ab3e8e30eba2913a991e2dff6362ccabdf67af3220","urls":["bzz-raw://a7f540e927a359c0196a7e0385c1684f31cad5ec5d75143e6c869327d95ea436","dweb:/ipfs/QmfExMH3ajtieteArNbCgnNFFqybz2k7WCm2AR6yheD6TG"],"license":"MIT"}},"version":1},"id":11} {"abi":[],"bytecode":{"object":"0x608080604052346013576039908160188239f35b5f80fdfe5f80fdfea2646970667358221220f8112b2e9d03bdb94e5e5cd9ad0c208785a05e855bbd3fb5fb6de208e49fa01864736f6c634300081e0033","sourceMap":"57:1599:11:-:0;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x5f80fdfea2646970667358221220f8112b2e9d03bdb94e5e5cd9ad0c208785a05e855bbd3fb5fb6de208e49fa01864736f6c634300081e0033","sourceMap":"57:1599:11:-:0;;","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.30+commit.73712a01\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"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\":\"0x5bc25b91410dd160bbb8d141b079b269b82677dab41b65742ae757df6da889bd\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d721d8b4c60e7f495586426d71fc9d03c8c2615f0005b90d2a3d85ef1a7ae3b9\",\"dweb:/ipfs/QmTBUTpF22QVJYYF6f72bqBWKvdgj8ottEVc2QXgVBcYnk\"]}},\"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":"0x5bc25b91410dd160bbb8d141b079b269b82677dab41b65742ae757df6da889bd","urls":["bzz-raw://d721d8b4c60e7f495586426d71fc9d03c8c2615f0005b90d2a3d85ef1a7ae3b9","dweb:/ipfs/QmTBUTpF22QVJYYF6f72bqBWKvdgj8ottEVc2QXgVBcYnk"],"license":"MIT"}},"version":1},"id":11}

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":"f52e10224748efca","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

@@ -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. 时间流逝,利息累积