From b3999d818e76300b9df818df90fe9d4c255cb542 Mon Sep 17 00:00:00 2001 From: Dima Lekhovitsky Date: Thu, 20 Jun 2024 14:41:29 +0300 Subject: [PATCH] fix: fixes and optimizations in `LinearInterestRateModelV3` (#230) In this PR: * Small fixes in `calcBorrowRate` and `availableToBorrow` to address https://github.com/Gearbox-protocol/core-v3/issues/194 * Small gas optimizations like removing redundant getters and evaluating some expressions at compile time --- .../interfaces/ILinearInterestRateModelV3.sol | 7 +- contracts/libraries/Constants.sol | 2 + contracts/libraries/QuotasLogic.sol | 6 +- contracts/pool/LinearInterestRateModelV3.sol | 101 ++++++++---------- .../pool/LinearInterestRateModelV3.unit.t.sol | 55 +++++++--- contracts/test/unit/pool/PoolV3.unit.t.sol | 8 +- 6 files changed, 94 insertions(+), 85 deletions(-) diff --git a/contracts/interfaces/ILinearInterestRateModelV3.sol b/contracts/interfaces/ILinearInterestRateModelV3.sol index e54c8b0d..2369744a 100644 --- a/contracts/interfaces/ILinearInterestRateModelV3.sol +++ b/contracts/interfaces/ILinearInterestRateModelV3.sol @@ -7,18 +7,13 @@ import {IInterestRateModel} from "./base/IInterestRateModel.sol"; /// @title Linear interest rate model V3 interface interface ILinearInterestRateModelV3 is IInterestRateModel { + /// @dev Linear IRM is stateless so the overriden function is marked as `view` function calcBorrowRate(uint256 expectedLiquidity, uint256 availableLiquidity, bool checkOptimalBorrowing) external view override returns (uint256); - function availableToBorrow(uint256 expectedLiquidity, uint256 availableLiquidity) - external - view - override - returns (uint256); - function isBorrowingMoreU2Forbidden() external view returns (bool); function getModelParameters() diff --git a/contracts/libraries/Constants.sol b/contracts/libraries/Constants.sol index b045ad5d..520ee2e0 100644 --- a/contracts/libraries/Constants.sol +++ b/contracts/libraries/Constants.sol @@ -6,6 +6,8 @@ pragma solidity ^0.8.17; uint256 constant WAD = 1e18; uint256 constant RAY = 1e27; uint16 constant PERCENTAGE_FACTOR = 1e4; +uint256 constant WAD_OVER_PERCENTAGE = WAD / PERCENTAGE_FACTOR; +uint256 constant RAY_OVER_PERCENTAGE = RAY / PERCENTAGE_FACTOR; uint256 constant SECONDS_PER_YEAR = 365 days; uint256 constant EPOCH_LENGTH = 7 days; diff --git a/contracts/libraries/QuotasLogic.sol b/contracts/libraries/QuotasLogic.sol index f07edfa6..61472a77 100644 --- a/contracts/libraries/QuotasLogic.sol +++ b/contracts/libraries/QuotasLogic.sol @@ -4,9 +4,7 @@ pragma solidity ^0.8.17; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {RAY, SECONDS_PER_YEAR, PERCENTAGE_FACTOR} from "../libraries/Constants.sol"; - -uint192 constant RAY_DIVIDED_BY_PERCENTAGE = uint192(RAY / PERCENTAGE_FACTOR); +import {RAY, RAY_OVER_PERCENTAGE, SECONDS_PER_YEAR, PERCENTAGE_FACTOR} from "../libraries/Constants.sol"; /// @title Quotas logic library library QuotasLogic { @@ -21,7 +19,7 @@ library QuotasLogic { { return uint192( uint256(cumulativeIndexLU) - + RAY_DIVIDED_BY_PERCENTAGE * (block.timestamp - lastQuotaRateUpdate) * rate / SECONDS_PER_YEAR + + uint192(RAY_OVER_PERCENTAGE) * (block.timestamp - lastQuotaRateUpdate) * rate / SECONDS_PER_YEAR ); // U:[QL-1] } diff --git a/contracts/pool/LinearInterestRateModelV3.sol b/contracts/pool/LinearInterestRateModelV3.sol index 0c02a1ca..4e80caae 100644 --- a/contracts/pool/LinearInterestRateModelV3.sol +++ b/contracts/pool/LinearInterestRateModelV3.sol @@ -4,12 +4,11 @@ pragma solidity ^0.8.17; pragma abicoder v1; -import {WAD, RAY, PERCENTAGE_FACTOR} from "../libraries/Constants.sol"; import {ILinearInterestRateModelV3} from "../interfaces/ILinearInterestRateModelV3.sol"; - -// EXCEPTIONS import {IncorrectParameterException, BorrowingMoreThanU2ForbiddenException} from "../interfaces/IExceptions.sol"; +import {PERCENTAGE_FACTOR, RAY, RAY_OVER_PERCENTAGE, WAD, WAD_OVER_PERCENTAGE} from "../libraries/Constants.sol"; + /// @title Linear interest rate model V3 /// @notice Gearbox V3 uses a two-point linear interest rate model in its pools. /// Unlike previous single-point models, it has a new intermediate slope between the obtuse and steep regions @@ -18,28 +17,28 @@ import {IncorrectParameterException, BorrowingMoreThanU2ForbiddenException} from /// in order to create a reserve for exits and make rates more stable. contract LinearInterestRateModelV3 is ILinearInterestRateModelV3 { /// @notice Contract version - uint256 public constant override version = 3_00; + uint256 public constant override version = 3_10; /// @notice Whether to prevent borrowing over `U_2` utilization bool public immutable override isBorrowingMoreU2Forbidden; /// @notice The first slope change point (obtuse -> intermediate region) - uint256 public immutable U_1_WAD; + uint256 internal immutable U_1_WAD; /// @notice The second slope change point (intermediate -> steep region) - uint256 public immutable U_2_WAD; + uint256 internal immutable U_2_WAD; /// @notice Base interest rate in RAY format - uint256 public immutable R_base_RAY; + uint256 internal immutable R_base_RAY; /// @notice Slope of the obtuse region - uint256 public immutable R_slope1_RAY; + uint256 internal immutable R_slope1_RAY; /// @notice Slope of the intermediate region - uint256 public immutable R_slope2_RAY; + uint256 internal immutable R_slope2_RAY; /// @notice Slope of the steep region - uint256 public immutable R_slope3_RAY; + uint256 internal immutable R_slope3_RAY; /// @notice Constructor /// @param U_1 `U_1` in basis points @@ -49,6 +48,7 @@ contract LinearInterestRateModelV3 is ILinearInterestRateModelV3 { /// @param R_slope2 `R_slope2` in basis points /// @param R_slope3 `R_slope3` in basis points /// @param _isBorrowingMoreU2Forbidden Whether to prevent borrowing over `U_2` utilization + /// @custom:tests U:[LIM-1], U:[LIM-2] constructor( uint16 U_1, uint16 U_2, @@ -59,36 +59,28 @@ contract LinearInterestRateModelV3 is ILinearInterestRateModelV3 { bool _isBorrowingMoreU2Forbidden ) { if ( - (U_1 >= PERCENTAGE_FACTOR) || (U_2 >= PERCENTAGE_FACTOR) || (U_1 > U_2) || (R_base > PERCENTAGE_FACTOR) - || (R_slope1 > PERCENTAGE_FACTOR) || (R_slope2 > PERCENTAGE_FACTOR) || (R_slope1 > R_slope2) - || (R_slope2 > R_slope3) - ) { - revert IncorrectParameterException(); // U:[LIM-2] - } - - /// Critical utilization points are stored in WAD format + U_2 >= PERCENTAGE_FACTOR || U_1 > U_2 || R_base > PERCENTAGE_FACTOR || R_slope2 > PERCENTAGE_FACTOR + || R_slope1 > R_slope2 || R_slope2 > R_slope3 + ) revert IncorrectParameterException(); - U_1_WAD = U_1 * (WAD / PERCENTAGE_FACTOR); // U:[LIM-1] - U_2_WAD = U_2 * (WAD / PERCENTAGE_FACTOR); // U:[LIM-1] + // critical utilization points are stored in WAD format + U_1_WAD = U_1 * WAD_OVER_PERCENTAGE; + U_2_WAD = U_2 * WAD_OVER_PERCENTAGE; - /// Slopes are stored in RAY format - R_base_RAY = R_base * (RAY / PERCENTAGE_FACTOR); // U:[LIM-1] - R_slope1_RAY = R_slope1 * (RAY / PERCENTAGE_FACTOR); // U:[LIM-1] - R_slope2_RAY = R_slope2 * (RAY / PERCENTAGE_FACTOR); // U:[LIM-1] - R_slope3_RAY = R_slope3 * (RAY / PERCENTAGE_FACTOR); // U:[LIM-1] + // slopes are stored in RAY format + R_base_RAY = R_base * RAY_OVER_PERCENTAGE; + R_slope1_RAY = R_slope1 * RAY_OVER_PERCENTAGE; + R_slope2_RAY = R_slope2 * RAY_OVER_PERCENTAGE; + R_slope3_RAY = R_slope3 * RAY_OVER_PERCENTAGE; - isBorrowingMoreU2Forbidden = _isBorrowingMoreU2Forbidden; // U:[LIM-1] - } - - /// @dev Same as the next one with `checkOptimalBorrowing` set to `false`, added for compatibility with older pools - function calcBorrowRate(uint256 expectedLiquidity, uint256 availableLiquidity) external view returns (uint256) { - return calcBorrowRate(expectedLiquidity, availableLiquidity, false); + isBorrowingMoreU2Forbidden = _isBorrowingMoreU2Forbidden; } /// @notice Returns the borrow rate calculated based on expected and available liquidity /// @param expectedLiquidity Expected liquidity in the pool /// @param availableLiquidity Available liquidity in the pool /// @param checkOptimalBorrowing Whether to check if borrowing over `U_2` utilization should be prevented + /// @custom:tests U:[LIM-3], U:[LIM-4] function calcBorrowRate(uint256 expectedLiquidity, uint256 availableLiquidity, bool checkOptimalBorrowing) public view @@ -96,77 +88,78 @@ contract LinearInterestRateModelV3 is ILinearInterestRateModelV3 { returns (uint256) { if (expectedLiquidity <= availableLiquidity) { - return R_base_RAY; // U:[LIM-3] + return R_base_RAY; } // expectedLiquidity - availableLiquidity // U = ---------------------------------------- // expectedLiquidity - uint256 U_WAD = (WAD * (expectedLiquidity - availableLiquidity)) / expectedLiquidity; // U:[LIM-3] + uint256 U_WAD = (WAD * (expectedLiquidity - availableLiquidity)) / expectedLiquidity; - // If U < U_1: + // If U <= U_1: // U // borrowRate = R_base + R_slope1 * ----- // U_1 - if (U_WAD < U_1_WAD) { - return R_base_RAY + ((R_slope1_RAY * U_WAD) / U_1_WAD); // U:[LIM-3] + if (U_WAD <= U_1_WAD) { + return R_base_RAY + ((R_slope1_RAY * U_WAD) / U_1_WAD); } - // If U >= U_1 & U < U_2: + // if U > U_1 & U <= U_2: // U - U_1 // borrowRate = R_base + R_slope1 + R_slope2 * ----------- // U_2 - U_1 - if (U_WAD < U_2_WAD) { - return R_base_RAY + R_slope1_RAY + (R_slope2_RAY * (U_WAD - U_1_WAD)) / (U_2_WAD - U_1_WAD); // U:[LIM-3] + if (U_WAD <= U_2_WAD) { + return R_base_RAY + R_slope1_RAY + (R_slope2_RAY * (U_WAD - U_1_WAD)) / (U_2_WAD - U_1_WAD); } - // If U > U_2 in `isBorrowingMoreU2Forbidden` and the utilization check is requested, + // if U > U_2 in `isBorrowingMoreU2Forbidden` and the utilization check is requested, // the function will revert to prevent raising utilization over the limit if (checkOptimalBorrowing && isBorrowingMoreU2Forbidden) { - revert BorrowingMoreThanU2ForbiddenException(); // U:[LIM-3] + revert BorrowingMoreThanU2ForbiddenException(); } - // If U >= U_2: + // if U > U_2: // U - U_2 // borrowRate = R_base + R_slope1 + R_slope2 + R_slope3 * ---------- // 1 - U_2 - return R_base_RAY + R_slope1_RAY + R_slope2_RAY + R_slope3_RAY * (U_WAD - U_2_WAD) / (WAD - U_2_WAD); // U:[LIM-3] + return R_base_RAY + R_slope1_RAY + R_slope2_RAY + R_slope3_RAY * (U_WAD - U_2_WAD) / (WAD - U_2_WAD); } /// @notice Returns the model's parameters in basis points + /// @custom:tests U:[LIM-1] function getModelParameters() external view override returns (uint16 U_1, uint16 U_2, uint16 R_base, uint16 R_slope1, uint16 R_slope2, uint16 R_slope3) { - U_1 = uint16(U_1_WAD / (WAD / PERCENTAGE_FACTOR)); // U:[LIM-1] - U_2 = uint16(U_2_WAD / (WAD / PERCENTAGE_FACTOR)); // U:[LIM-1] - R_base = uint16(R_base_RAY / (RAY / PERCENTAGE_FACTOR)); // U:[LIM-1] - R_slope1 = uint16(R_slope1_RAY / (RAY / PERCENTAGE_FACTOR)); // U:[LIM-1] - R_slope2 = uint16(R_slope2_RAY / (RAY / PERCENTAGE_FACTOR)); // U:[LIM-1] - R_slope3 = uint16(R_slope3_RAY / (RAY / PERCENTAGE_FACTOR)); // U:[LIM-1] + U_1 = uint16(U_1_WAD / WAD_OVER_PERCENTAGE); + U_2 = uint16(U_2_WAD / WAD_OVER_PERCENTAGE); + R_base = uint16(R_base_RAY / RAY_OVER_PERCENTAGE); + R_slope1 = uint16(R_slope1_RAY / RAY_OVER_PERCENTAGE); + R_slope2 = uint16(R_slope2_RAY / RAY_OVER_PERCENTAGE); + R_slope3 = uint16(R_slope3_RAY / RAY_OVER_PERCENTAGE); } /// @notice Returns the amount available to borrow /// - If borrowing over `U_2` is prohibited, returns the amount that can be borrowed before `U_2` is reached /// - Otherwise, simply returns the available liquidity + /// @custom:tests U:[LIM-3], U:[LIM-4] function availableToBorrow(uint256 expectedLiquidity, uint256 availableLiquidity) external view override returns (uint256) { - if (isBorrowingMoreU2Forbidden && (expectedLiquidity >= availableLiquidity) && (expectedLiquidity != 0)) { - uint256 U_WAD = (WAD * (expectedLiquidity - availableLiquidity)) / expectedLiquidity; // U:[LIM-3] - - return (U_WAD < U_2_WAD) ? ((U_2_WAD - U_WAD) * expectedLiquidity) / WAD : 0; // U:[LIM-3] + if (isBorrowingMoreU2Forbidden && expectedLiquidity != 0) { + uint256 minAvailableLiquidity = expectedLiquidity - expectedLiquidity * U_2_WAD / WAD; + return availableLiquidity > minAvailableLiquidity ? availableLiquidity - minAvailableLiquidity : 0; } else { - return availableLiquidity; // U:[LIM-3] + return availableLiquidity; } } } diff --git a/contracts/test/unit/pool/LinearInterestRateModelV3.unit.t.sol b/contracts/test/unit/pool/LinearInterestRateModelV3.unit.t.sol index b483c6c4..e742ea78 100644 --- a/contracts/test/unit/pool/LinearInterestRateModelV3.unit.t.sol +++ b/contracts/test/unit/pool/LinearInterestRateModelV3.unit.t.sol @@ -10,7 +10,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; // TEST import "../../lib/constants.sol"; -import {PERCENTAGE_FACTOR} from "../../../libraries/Constants.sol"; +import {PERCENTAGE_FACTOR, RAY_OVER_PERCENTAGE} from "../../../libraries/Constants.sol"; // EXCEPTIONS import "../../../interfaces/IExceptions.sol"; @@ -30,7 +30,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { // TESTS // - // U:[LIM-1]: start parameters are correct + /// @notice U:[LIM-1]: start parameters are correct function test_U_LIM_01_start_parameters_correct() public { (uint16 U_1, uint16 U_2, uint16 R_base, uint16 R_slope1, uint16 R_slope2, uint16 R_slope3) = irm.getModelParameters(); @@ -55,7 +55,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { uint16 R_slope3; } - // U:[LIM-2]: linear model constructor reverts for incorrect params + /// @notice U:[LIM-2]: linear model constructor reverts for incorrect params function test_U_LIM_02_linear_model_constructor_reverts_for_incorrect_params() public { // adds liqudity to mint initial diesel tokens to change 1:1 rate @@ -177,7 +177,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { bool expectedRevert; } - // U:[LIM-3]: linear model computes available to borrow and borrow rate correctly + /// @notice U:[LIM-3]: linear model computes available to borrow and borrow rate correctly function test_U_LIM_03_linear_model_computes_available_to_borrow_and_borrow_rate_correctly() public { // adds liqudity to mint initial diesel tokens to change 1:1 rate @@ -198,7 +198,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 100, /// EXPECTED VALUES // R_base only - expectedBorrowRate: 15_00 * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: 15_00 * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 100, expectedRevert: false }), @@ -218,7 +218,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 100, /// EXPECTED VALUES // R_base only - expectedBorrowRate: 15_00 * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: 15_00 * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 95, expectedRevert: false }), @@ -238,7 +238,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 100, /// EXPECTED VALUES // R_base only - expectedBorrowRate: 15_00 * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: 15_00 * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 100, expectedRevert: false }), @@ -258,8 +258,9 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 100, /// EXPECTED VALUES // R_base only - expectedBorrowRate: 15_00 * RAY / PERCENTAGE_FACTOR, - expectedAvailableToBorrow: 100, + expectedBorrowRate: 15_00 * RAY_OVER_PERCENTAGE, + // borrowing all 100 brings utilization to 100%, while borrowing 99 leaves it at 90% + expectedAvailableToBorrow: 99, expectedRevert: false }), LinearCalculationsCase({ @@ -278,7 +279,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 60, /// EXPECTED VALUES // 15% + 5% (r1) * 40% (utilisation) / 80% (u1) - expectedBorrowRate: (15_00 + 5_00 * 40 / 80) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (15_00 + 5_00 * 40 / 80) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 60, expectedRevert: false }), @@ -298,7 +299,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 60, /// EXPECTED VALUES // 15% (rBase) + 5% (r1) * 40% (utilisation) / 80% (u1) - expectedBorrowRate: (15_00 + 5_00 * 40 / 80) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (15_00 + 5_00 * 40 / 80) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 55, expectedRevert: false }), @@ -319,7 +320,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { // EXPECTED VALUES // utilisation: 90% // 12% (rBase) + 5% (r1) + 15%(r2) * 10% / 15% (u2 - u1) - expectedBorrowRate: (12_00 + 5_00 + 15_00 * 10 / 15) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (12_00 + 5_00 + 15_00 * 10 / 15) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 10, expectedRevert: false }), @@ -339,7 +340,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 10, /// EXPECTED VALUES // 12% (rBase) + 5% (r1) + 15%(r2) * 10% / 15% (u2 - u1) - expectedBorrowRate: (12_00 + 5_00 + 15_00 * 10 / 15) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (12_00 + 5_00 + 15_00 * 10 / 15) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 5, expectedRevert: false }), @@ -360,7 +361,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { // EXPECTED VALUES // utilisation: 90% // 12% (rBase) + 5% (r1) + 15% + 90% (r3) * 3% / 5%(1 - u2) - expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00 * 3 / 5) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00 * 3 / 5) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 2, expectedRevert: false }), @@ -380,7 +381,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 2, /// EXPECTED VALUES // 12% (rBase) + 5% (r1) + 15% + 90% (r3) * 3% / 5%(1 - u2) - expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00 * 3 / 5) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00 * 3 / 5) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 0, expectedRevert: true }), @@ -401,7 +402,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { // EXPECTED VALUES // utilisation: 90% // 12% (rBase) + 5% (r1) + 15% + 90% (r3) * 3% / 5%(1 - u2) - expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 0, expectedRevert: false }), @@ -421,7 +422,7 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { availableLiquidity: 0, /// EXPECTED VALUES // 12% (rBase) + 5% (r1) + 15% + 90% (r3) * 3% / 5%(1 - u2) - expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00) * RAY / PERCENTAGE_FACTOR, + expectedBorrowRate: (12_00 + 5_00 + 15_00 + 90_00) * RAY_OVER_PERCENTAGE, expectedAvailableToBorrow: 0, expectedRevert: true }) @@ -459,4 +460,24 @@ contract LinearInterestRateModelV3UnitTest is TestHelper { ); } } + + /// @notice U:[LIM-4]: `calcBorrowRate` allows to borrow `availableToBorrow` + function test_U_LIM_04_calcBorrowRate_allows_to_borrow_availableToBorrow( + uint256 expectedLiquidity, + uint256 availableLiquidity + ) public { + expectedLiquidity = bound(expectedLiquidity, 0, 1e36); + availableLiquidity = bound(availableLiquidity, 0, expectedLiquidity); + uint256 amount = irm.availableToBorrow(expectedLiquidity, availableLiquidity); + + if (amount != 0) { + // it allows to borrow `availableToBorrow` + irm.calcBorrowRate(expectedLiquidity, availableLiquidity - amount, true); + + // it doesn't allow to borrow more + uint256 amountWithBuffer = amount > 100 ? amount * 101 / 100 : amount + 1; + vm.expectRevert(BorrowingMoreThanU2ForbiddenException.selector); + irm.calcBorrowRate(expectedLiquidity, availableLiquidity - amountWithBuffer, true); + } + } } diff --git a/contracts/test/unit/pool/PoolV3.unit.t.sol b/contracts/test/unit/pool/PoolV3.unit.t.sol index b21ef43f..f4afa9c0 100644 --- a/contracts/test/unit/pool/PoolV3.unit.t.sol +++ b/contracts/test/unit/pool/PoolV3.unit.t.sol @@ -10,7 +10,7 @@ import {MAX_WITHDRAW_FEE, RAY} from "../../../libraries/Constants.sol"; import {ICreditManagerV3} from "../../../interfaces/ICreditManagerV3.sol"; import "../../../interfaces/IExceptions.sol"; -import {ILinearInterestRateModelV3} from "../../../interfaces/ILinearInterestRateModelV3.sol"; +import {LinearInterestRateModelV3} from "../../../pool/LinearInterestRateModelV3.sol"; import {IPoolQuotaKeeperV3} from "../../../interfaces/IPoolQuotaKeeperV3.sol"; import {IPoolV3Events} from "../../../interfaces/IPoolV3.sol"; @@ -54,7 +54,7 @@ contract PoolV3UnitTest is TestHelper, IPoolV3Events, IERC4626Events { ERC20FeeMock underlying; AddressProviderV3ACLMock addressProvider; - bytes4 constant calcBorrowRateSelector = bytes4(keccak256("calcBorrowRate(uint256,uint256,bool)")); + bytes4 constant calcBorrowRateSelector = LinearInterestRateModelV3.calcBorrowRate.selector; // ----- // // SETUP // @@ -796,7 +796,7 @@ contract PoolV3UnitTest is TestHelper, IPoolV3Events, IERC4626Events { function test_U_LP_12_creditManagerBorrowable_works_as_expected() public { // for the next two cases, `irm.availableToBorrow` should not be called vm.mockCallRevert( - interestRateModel, abi.encode(ILinearInterestRateModelV3.availableToBorrow.selector), "should not be called" + interestRateModel, abi.encode(LinearInterestRateModelV3.availableToBorrow.selector), "should not be called" ); // case: total debt limit is fully used @@ -810,7 +810,7 @@ contract PoolV3UnitTest is TestHelper, IPoolV3Events, IERC4626Events { // for the next three cases, let `irm.availableToBorrow` always return 500 vm.mockCall( - interestRateModel, abi.encode(ILinearInterestRateModelV3.availableToBorrow.selector), abi.encode(500) + interestRateModel, abi.encode(LinearInterestRateModelV3.availableToBorrow.selector), abi.encode(500) ); // case: `irm.availableToBorrow` is the smallest