From 9a10ba031d82156a0efa7a9a8195d122f9e844e1 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 26 Jun 2024 12:56:25 +0530 Subject: [PATCH 01/19] fix: removed update and acm --- contracts/TwoKinksInterestRateModel.sol | 184 ++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 contracts/TwoKinksInterestRateModel.sol diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol new file mode 100644 index 00000000..8a6447d6 --- /dev/null +++ b/contracts/TwoKinksInterestRateModel.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { TimeManagerV8 } from "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol"; +import { InterestRateModel } from "./InterestRateModel.sol"; +import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; + +/** + * @title JumpRateModelV2 + * @author Compound (modified by Dharma Labs, Arr00 and Venus) + * @notice An interest rate model with a steep increase after a certain utilization threshold called **kink** is reached. + * The parameters of this interest rate model can be adjusted by the owner. Version 2 modifies Version 1 by enabling updateable parameters + */ +contract TwoKindsInterestRateModel is InterestRateModel, TimeManagerV8 { + /** + * @notice The multiplier of utilization rate per block or second that gives the slope of the interest rate + */ + uint256 public multiplierPerBlock; + + /** + * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 + */ + uint256 public baseRatePerBlock; + + /** + * @notice The multiplier per block or second after hitting a specified utilization point + */ + uint256 public jumpMultiplierPerBlock; + + /** + * @notice The utilization point at which the jump multiplier is applied + */ + uint256 public kink; + + event NewInterestParams( + uint256 baseRatePerBlockOrTimestamp, + uint256 multiplierPerBlockOrTimestamp, + uint256 jumpMultiplierPerBlockOrTimestamp, + uint256 kink + ); + + /** + * @notice Thrown when the action is prohibited by AccessControlManager + */ + error Unauthorized(address sender, address calledContract, string methodSignature); + + /** + * @notice Construct an interest rate model + * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) + * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) + * @param jumpMultiplierPerYear_ The multiplier after hitting a specified utilization point + * @param kink_ The utilization point at which the jump multiplier is applied + * @param timeBased_ A boolean indicating whether the contract is based on time or block. + * @param blocksPerYear_ The number of blocks per year + */ + constructor( + uint256 baseRatePerYear_, + uint256 multiplierPerYear_, + uint256 jumpMultiplierPerYear_, + uint256 kink_, + bool timeBased_, + uint256 blocksPerYear_ + ) TimeManagerV8(timeBased_, blocksPerYear_) { + _updateJumpRateModel(baseRatePerYear_, multiplierPerYear_, jumpMultiplierPerYear_, kink_); + } + + /** + * @notice Calculates the current borrow rate per slot (block or second) + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param badDebt The amount of badDebt in the market + * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18) + */ + function getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 badDebt + ) external view override returns (uint256) { + return _getBorrowRate(cash, borrows, reserves, badDebt); + } + + /** + * @notice Calculates the current supply rate per slot (block or second) + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param reserveFactorMantissa The current reserve factor for the market + * @param badDebt The amount of badDebt in the market + * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) + */ + function getSupplyRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 reserveFactorMantissa, + uint256 badDebt + ) public view virtual override returns (uint256) { + uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa; + uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt); + uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE; + uint256 incomeToDistribute = borrows * rateToPool; + uint256 supply = cash + borrows + badDebt - reserves; + return incomeToDistribute / supply; + } + + /** + * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)` + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market (currently unused) + * @param badDebt The amount of badDebt in the market + * @return The utilization rate as a mantissa between [0, MANTISSA_ONE] + */ + function utilizationRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 badDebt + ) public pure returns (uint256) { + // Utilization rate is 0 when there are no borrows and badDebt + if ((borrows + badDebt) == 0) { + return 0; + } + + uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves); + + if (rate > EXP_SCALE) { + rate = EXP_SCALE; + } + + return rate; + } + + /** + * @notice Internal function to update the parameters of the interest rate model + * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by EXP_SCALE) + * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) + * @param jumpMultiplierPerYear The multiplierPerBlockOrTimestamp after hitting a specified utilization point + * @param kink_ The utilization point at which the jump multiplier is applied + */ + function _updateJumpRateModel( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink_ + ) internal { + baseRatePerBlock = baseRatePerYear / blocksOrSecondsPerYear; + multiplierPerBlock = multiplierPerYear / blocksOrSecondsPerYear; + jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksOrSecondsPerYear; + kink = kink_; + + emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); + } + + /** + * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param badDebt The amount of badDebt in the market + * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) + */ + function _getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 badDebt + ) internal view returns (uint256) { + uint256 util = utilizationRate(cash, borrows, reserves, badDebt); + uint256 kink_ = kink; + + if (util <= kink_) { + return ((util * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; + } + uint256 normalRate = ((kink_ * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; + uint256 excessUtil; + unchecked { + excessUtil = util - kink_; + } + return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate; + } +} From b0242c7bf86e2f29a4f17b8d255c894a266035f4 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 26 Jun 2024 13:19:11 +0530 Subject: [PATCH 02/19] fix: updated vars --- contracts/TwoKinksInterestRateModel.sol | 303 ++++++++++++++---------- 1 file changed, 178 insertions(+), 125 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 8a6447d6..267fd09a 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -6,179 +6,232 @@ import { InterestRateModel } from "./InterestRateModel.sol"; import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; /** - * @title JumpRateModelV2 - * @author Compound (modified by Dharma Labs, Arr00 and Venus) + * @title TwoKinksInterestRateModel + * @author Venus * @notice An interest rate model with a steep increase after a certain utilization threshold called **kink** is reached. * The parameters of this interest rate model can be adjusted by the owner. Version 2 modifies Version 1 by enabling updateable parameters */ -contract TwoKindsInterestRateModel is InterestRateModel, TimeManagerV8 { +contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { + + ////////////////////// SLOPE 1 ////////////////////// + /** - * @notice The multiplier of utilization rate per block or second that gives the slope of the interest rate + * @notice The multiplier of utilization rate per block or second that gives the slope 1 of the interest rate */ - uint256 public multiplierPerBlock; + uint256 public immutable multiplierPerBlockOrTimestamp; /** * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 */ - uint256 public baseRatePerBlock; + uint256 public immutable baseRatePerBlockOrTimestamp; + + ////////////////////// SLOPE 2 ////////////////////// + + /** + * @notice The utilization point at which the multiplier2 is applied + */ + uint256 public immutable kink1; + + /** + * @notice The multiplier of utilization rate per block or second that gives the slope 2 of the interest rate + */ + uint256 public immutable multiplier2PerBlockOrTimestamp; /** - * @notice The multiplier per block or second after hitting a specified utilization point + * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits kink1 */ - uint256 public jumpMultiplierPerBlock; + uint256 public immutable baseRate2PerBlockOrTimestamp; + + ////////////////////// SLOPE 3 ////////////////////// /** * @notice The utilization point at which the jump multiplier is applied */ - uint256 public kink; + uint256 public immutable kink2; + + + /** + * @notice The multiplier per block or second after hitting kink2 + */ + uint256 public jumpMultiplierPerBlockOrTimestamp; + event NewInterestParams( uint256 baseRatePerBlockOrTimestamp, uint256 multiplierPerBlockOrTimestamp, - uint256 jumpMultiplierPerBlockOrTimestamp, - uint256 kink + uint256 kink1, + uint256 multiplier2PerBlockOrTimestamp, + uint256 baseRate2PerBlockOrTimestamp, + uint256 kink2, + uint256 jumpMultiplierPerBlockOrTimestamp ); - /** - * @notice Thrown when the action is prohibited by AccessControlManager - */ - error Unauthorized(address sender, address calledContract, string methodSignature); - /** * @notice Construct an interest rate model * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) - * @param jumpMultiplierPerYear_ The multiplier after hitting a specified utilization point - * @param kink_ The utilization point at which the jump multiplier is applied + * @param kink1_ The utilization point at which the multiplier2 is applied + * @param multiplier2PerYear_ The rate of increase in interest rate wrt utilization after hitting kink1 (scaled by EXP_SCALE) + * @param baseRate2PerYear_ The approximate target base APR after hitting kink1, as a mantissa (scaled by EXP_SCALE) + * @param kink2_ The utilization point at which the jump multiplier is applied + * @param jumpMultiplierPerYear_ The multiplier after hitting kink2 * @param timeBased_ A boolean indicating whether the contract is based on time or block. * @param blocksPerYear_ The number of blocks per year */ constructor( uint256 baseRatePerYear_, uint256 multiplierPerYear_, + uint256 kink1_, + uint256 multiplier2PerYear_, + uint256 baseRate2PerYear_, + uint256 kink2_, uint256 jumpMultiplierPerYear_, - uint256 kink_, bool timeBased_, uint256 blocksPerYear_ ) TimeManagerV8(timeBased_, blocksPerYear_) { - _updateJumpRateModel(baseRatePerYear_, multiplierPerYear_, jumpMultiplierPerYear_, kink_); + _updateJumpRateModel( + baseRatePerYear_, + multiplierPerYear_, + kink1_, + multiplier2PerYear_, + baseRate2PerYear_, + kink2_, + jumpMultiplierPerYear_ + ); } - /** - * @notice Calculates the current borrow rate per slot (block or second) - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market - * @param badDebt The amount of badDebt in the market - * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18) - */ - function getBorrowRate( - uint256 cash, - uint256 borrows, - uint256 reserves, - uint256 badDebt - ) external view override returns (uint256) { - return _getBorrowRate(cash, borrows, reserves, badDebt); - } - - /** - * @notice Calculates the current supply rate per slot (block or second) - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market - * @param reserveFactorMantissa The current reserve factor for the market - * @param badDebt The amount of badDebt in the market - * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) - */ - function getSupplyRate( - uint256 cash, - uint256 borrows, - uint256 reserves, - uint256 reserveFactorMantissa, - uint256 badDebt - ) public view virtual override returns (uint256) { - uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa; - uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt); - uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE; - uint256 incomeToDistribute = borrows * rateToPool; - uint256 supply = cash + borrows + badDebt - reserves; - return incomeToDistribute / supply; - } - - /** - * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)` - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market (currently unused) - * @param badDebt The amount of badDebt in the market - * @return The utilization rate as a mantissa between [0, MANTISSA_ONE] - */ - function utilizationRate( - uint256 cash, - uint256 borrows, - uint256 reserves, - uint256 badDebt - ) public pure returns (uint256) { - // Utilization rate is 0 when there are no borrows and badDebt - if ((borrows + badDebt) == 0) { - return 0; - } - - uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves); - - if (rate > EXP_SCALE) { - rate = EXP_SCALE; - } - - return rate; - } + // /** + // * @notice Calculates the current borrow rate per slot (block or second) + // * @param cash The amount of cash in the market + // * @param borrows The amount of borrows in the market + // * @param reserves The amount of reserves in the market + // * @param badDebt The amount of badDebt in the market + // * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18) + // */ + // function getBorrowRate( + // uint256 cash, + // uint256 borrows, + // uint256 reserves, + // uint256 badDebt + // ) external view override returns (uint256) { + // return _getBorrowRate(cash, borrows, reserves, badDebt); + // } + + // /** + // * @notice Calculates the current supply rate per slot (block or second) + // * @param cash The amount of cash in the market + // * @param borrows The amount of borrows in the market + // * @param reserves The amount of reserves in the market + // * @param reserveFactorMantissa The current reserve factor for the market + // * @param badDebt The amount of badDebt in the market + // * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) + // */ + // function getSupplyRate( + // uint256 cash, + // uint256 borrows, + // uint256 reserves, + // uint256 reserveFactorMantissa, + // uint256 badDebt + // ) public view virtual override returns (uint256) { + // uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa; + // uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt); + // uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE; + // uint256 incomeToDistribute = borrows * rateToPool; + // uint256 supply = cash + borrows + badDebt - reserves; + // return incomeToDistribute / supply; + // } + + // /** + // * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)` + // * @param cash The amount of cash in the market + // * @param borrows The amount of borrows in the market + // * @param reserves The amount of reserves in the market (currently unused) + // * @param badDebt The amount of badDebt in the market + // * @return The utilization rate as a mantissa between [0, MANTISSA_ONE] + // */ + // function utilizationRate( + // uint256 cash, + // uint256 borrows, + // uint256 reserves, + // uint256 badDebt + // ) public pure returns (uint256) { + // // Utilization rate is 0 when there are no borrows and badDebt + // if ((borrows + badDebt) == 0) { + // return 0; + // } + + // uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves); + + // if (rate > EXP_SCALE) { + // rate = EXP_SCALE; + // } + + // return rate; + // } /** * @notice Internal function to update the parameters of the interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by EXP_SCALE) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) - * @param jumpMultiplierPerYear The multiplierPerBlockOrTimestamp after hitting a specified utilization point - * @param kink_ The utilization point at which the jump multiplier is applied + * @param kink1_ The utilization point at which the multiplier2 is applied + * @param multiplier2PerYear The rate of increase in interest rate wrt utilization after hitting kink1 (scaled by EXP_SCALE) + * @param baseRate2PerYear The approximate target base APR after hitting kink1, as a mantissa (scaled by EXP_SCALE) + * @param kink2_ The utilization point at which the jump multiplier is applied + * @param jumpMultiplierPerYear The multiplier after hitting kink2 */ function _updateJumpRateModel( uint256 baseRatePerYear, uint256 multiplierPerYear, - uint256 jumpMultiplierPerYear, - uint256 kink_ + uint256 kink1_, + uint256 multiplier2PerYear, + uint256 baseRate2PerYear, + uint256 kink2_, + uint256 jumpMultiplierPerYear ) internal { - baseRatePerBlock = baseRatePerYear / blocksOrSecondsPerYear; - multiplierPerBlock = multiplierPerYear / blocksOrSecondsPerYear; - jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksOrSecondsPerYear; - kink = kink_; - - emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); + baseRatePerBlockOrTimestamp = baseRatePerYear / blocksOrSecondsPerYear; + multiplierPerBlockOrTimestamp = multiplierPerYear / blocksOrSecondsPerYear; + kink1 = kink1_; + multiplier2PerBlockOrTimestamp = multiplier2PerYear / blocksOrSecondsPerYear; + baseRate2PerBlockOrTimestamp = baseRate2PerYear / blocksOrSecondsPerYear; + kink2 = kink2_; + jumpMultiplierPerBlockOrTimestamp = jumpMultiplierPerYear / blocksOrSecondsPerYear; + + emit NewInterestParams( + baseRatePerBlockOrTimestamp, + multiplierPerBlockOrTimestamp, + kink1, + multiplier2PerBlockOrTimestamp, + baseRate2PerBlockOrTimestamp, + kink2, + jumpMultiplierPerBlockOrTimestamp + ); } - /** - * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market - * @param badDebt The amount of badDebt in the market - * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) - */ - function _getBorrowRate( - uint256 cash, - uint256 borrows, - uint256 reserves, - uint256 badDebt - ) internal view returns (uint256) { - uint256 util = utilizationRate(cash, borrows, reserves, badDebt); - uint256 kink_ = kink; - - if (util <= kink_) { - return ((util * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; - } - uint256 normalRate = ((kink_ * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; - uint256 excessUtil; - unchecked { - excessUtil = util - kink_; - } - return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate; - } + // /** + // * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market + // * @param cash The amount of cash in the market + // * @param borrows The amount of borrows in the market + // * @param reserves The amount of reserves in the market + // * @param badDebt The amount of badDebt in the market + // * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) + // */ + // function _getBorrowRate( + // uint256 cash, + // uint256 borrows, + // uint256 reserves, + // uint256 badDebt + // ) internal view returns (uint256) { + // uint256 util = utilizationRate(cash, borrows, reserves, badDebt); + // uint256 kink_ = kink; + + // if (util <= kink_) { + // return ((util * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; + // } + // uint256 normalRate = ((kink_ * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; + // uint256 excessUtil; + // unchecked { + // excessUtil = util - kink_; + // } + // return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate; + // } } From 3225de5968fd4f2dbe7e9ae070e74e7576d9147c Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 26 Jun 2024 13:52:10 +0530 Subject: [PATCH 03/19] fix: updated borrow rate --- contracts/TwoKinksInterestRateModel.sol | 237 +++++++++++------------- 1 file changed, 105 insertions(+), 132 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 267fd09a..10c135e7 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -54,18 +54,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { * @notice The multiplier per block or second after hitting kink2 */ uint256 public jumpMultiplierPerBlockOrTimestamp; - - event NewInterestParams( - uint256 baseRatePerBlockOrTimestamp, - uint256 multiplierPerBlockOrTimestamp, - uint256 kink1, - uint256 multiplier2PerBlockOrTimestamp, - uint256 baseRate2PerBlockOrTimestamp, - uint256 kink2, - uint256 jumpMultiplierPerBlockOrTimestamp - ); - /** * @notice Construct an interest rate model * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) @@ -89,85 +78,83 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { bool timeBased_, uint256 blocksPerYear_ ) TimeManagerV8(timeBased_, blocksPerYear_) { - _updateJumpRateModel( - baseRatePerYear_, - multiplierPerYear_, - kink1_, - multiplier2PerYear_, - baseRate2PerYear_, - kink2_, - jumpMultiplierPerYear_ - ); + baseRatePerBlockOrTimestamp = baseRatePerYear_ / blocksOrSecondsPerYear; + multiplierPerBlockOrTimestamp = multiplierPerYear_ / blocksOrSecondsPerYear; + kink1 = kink1_; + multiplier2PerBlockOrTimestamp = multiplier2PerYear_ / blocksOrSecondsPerYear; + baseRate2PerBlockOrTimestamp = baseRate2PerYear_ / blocksOrSecondsPerYear; + kink2 = kink2_; + jumpMultiplierPerBlockOrTimestamp = jumpMultiplierPerYear_ / blocksOrSecondsPerYear; + } + + /** + * @notice Calculates the current borrow rate per slot (block or second) + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param badDebt The amount of badDebt in the market + * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18) + */ + function getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 badDebt + ) external view override returns (uint256) { + return _getBorrowRate(cash, borrows, reserves, badDebt); + } + + /** + * @notice Calculates the current supply rate per slot (block or second) + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param reserveFactorMantissa The current reserve factor for the market + * @param badDebt The amount of badDebt in the market + * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) + */ + function getSupplyRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 reserveFactorMantissa, + uint256 badDebt + ) public view virtual override returns (uint256) { + uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa; + uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt); + uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE; + uint256 incomeToDistribute = borrows * rateToPool; + uint256 supply = cash + borrows + badDebt - reserves; + return incomeToDistribute / supply; } - // /** - // * @notice Calculates the current borrow rate per slot (block or second) - // * @param cash The amount of cash in the market - // * @param borrows The amount of borrows in the market - // * @param reserves The amount of reserves in the market - // * @param badDebt The amount of badDebt in the market - // * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18) - // */ - // function getBorrowRate( - // uint256 cash, - // uint256 borrows, - // uint256 reserves, - // uint256 badDebt - // ) external view override returns (uint256) { - // return _getBorrowRate(cash, borrows, reserves, badDebt); - // } - - // /** - // * @notice Calculates the current supply rate per slot (block or second) - // * @param cash The amount of cash in the market - // * @param borrows The amount of borrows in the market - // * @param reserves The amount of reserves in the market - // * @param reserveFactorMantissa The current reserve factor for the market - // * @param badDebt The amount of badDebt in the market - // * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) - // */ - // function getSupplyRate( - // uint256 cash, - // uint256 borrows, - // uint256 reserves, - // uint256 reserveFactorMantissa, - // uint256 badDebt - // ) public view virtual override returns (uint256) { - // uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa; - // uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt); - // uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE; - // uint256 incomeToDistribute = borrows * rateToPool; - // uint256 supply = cash + borrows + badDebt - reserves; - // return incomeToDistribute / supply; - // } - - // /** - // * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)` - // * @param cash The amount of cash in the market - // * @param borrows The amount of borrows in the market - // * @param reserves The amount of reserves in the market (currently unused) - // * @param badDebt The amount of badDebt in the market - // * @return The utilization rate as a mantissa between [0, MANTISSA_ONE] - // */ - // function utilizationRate( - // uint256 cash, - // uint256 borrows, - // uint256 reserves, - // uint256 badDebt - // ) public pure returns (uint256) { - // // Utilization rate is 0 when there are no borrows and badDebt - // if ((borrows + badDebt) == 0) { - // return 0; - // } - - // uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves); - - // if (rate > EXP_SCALE) { - // rate = EXP_SCALE; - // } - - // return rate; - // } + /** + * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)` + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market (currently unused) + * @param badDebt The amount of badDebt in the market + * @return The utilization rate as a mantissa between [0, MANTISSA_ONE] + */ + function utilizationRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 badDebt + ) public pure returns (uint256) { + // Utilization rate is 0 when there are no borrows and badDebt + if ((borrows + badDebt) == 0) { + return 0; + } + + uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves); + + if (rate > EXP_SCALE) { + rate = EXP_SCALE; + } + + return rate; + } /** * @notice Internal function to update the parameters of the interest rate model @@ -188,50 +175,36 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { uint256 kink2_, uint256 jumpMultiplierPerYear ) internal { - baseRatePerBlockOrTimestamp = baseRatePerYear / blocksOrSecondsPerYear; - multiplierPerBlockOrTimestamp = multiplierPerYear / blocksOrSecondsPerYear; - kink1 = kink1_; - multiplier2PerBlockOrTimestamp = multiplier2PerYear / blocksOrSecondsPerYear; - baseRate2PerBlockOrTimestamp = baseRate2PerYear / blocksOrSecondsPerYear; - kink2 = kink2_; - jumpMultiplierPerBlockOrTimestamp = jumpMultiplierPerYear / blocksOrSecondsPerYear; - - emit NewInterestParams( - baseRatePerBlockOrTimestamp, - multiplierPerBlockOrTimestamp, - kink1, - multiplier2PerBlockOrTimestamp, - baseRate2PerBlockOrTimestamp, - kink2, - jumpMultiplierPerBlockOrTimestamp - ); + } - // /** - // * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market - // * @param cash The amount of cash in the market - // * @param borrows The amount of borrows in the market - // * @param reserves The amount of reserves in the market - // * @param badDebt The amount of badDebt in the market - // * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) - // */ - // function _getBorrowRate( - // uint256 cash, - // uint256 borrows, - // uint256 reserves, - // uint256 badDebt - // ) internal view returns (uint256) { - // uint256 util = utilizationRate(cash, borrows, reserves, badDebt); - // uint256 kink_ = kink; - - // if (util <= kink_) { - // return ((util * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; - // } - // uint256 normalRate = ((kink_ * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock; - // uint256 excessUtil; - // unchecked { - // excessUtil = util - kink_; - // } - // return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate; - // } + /** + * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param badDebt The amount of badDebt in the market + * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) + */ + function _getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 badDebt + ) internal view returns (uint256) { + uint256 util = utilizationRate(cash, borrows, reserves, badDebt); + + if (util < kink1) { + return ((util * multiplierPerBlockOrTimestamp) / EXP_SCALE) + baseRatePerBlockOrTimestamp; + } else if (util < kink2) { + uint256 rate1 = (((kink1 * multiplierPerBlockOrTimestamp) / EXP_SCALE) + baseRatePerBlockOrTimestamp); + uint256 rate2 = (((util - kink1) * multiplier2PerBlockOrTimestamp) / EXP_SCALE) + baseRate2PerBlockOrTimestamp; + return rate1 + rate2; + } else { + uint256 rate1 = (((kink1 * multiplierPerBlockOrTimestamp) / EXP_SCALE) + baseRatePerBlockOrTimestamp); + uint256 rate2 = (((kink2 - kink1) * multiplier2PerBlockOrTimestamp) / EXP_SCALE) + baseRate2PerBlockOrTimestamp; + uint256 rate3 = (((util - kink2) * jumpMultiplierPerBlockOrTimestamp) / EXP_SCALE); + return rate1 + rate2 + rate3; + } + } } From 147e6d0ff168dc193049840aced2188b48887187 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 26 Jun 2024 13:53:23 +0530 Subject: [PATCH 04/19] fix: remove unused func --- contracts/TwoKinksInterestRateModel.sol | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 10c135e7..2f66790b 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -156,28 +156,6 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { return rate; } - /** - * @notice Internal function to update the parameters of the interest rate model - * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by EXP_SCALE) - * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) - * @param kink1_ The utilization point at which the multiplier2 is applied - * @param multiplier2PerYear The rate of increase in interest rate wrt utilization after hitting kink1 (scaled by EXP_SCALE) - * @param baseRate2PerYear The approximate target base APR after hitting kink1, as a mantissa (scaled by EXP_SCALE) - * @param kink2_ The utilization point at which the jump multiplier is applied - * @param jumpMultiplierPerYear The multiplier after hitting kink2 - */ - function _updateJumpRateModel( - uint256 baseRatePerYear, - uint256 multiplierPerYear, - uint256 kink1_, - uint256 multiplier2PerYear, - uint256 baseRate2PerYear, - uint256 kink2_, - uint256 jumpMultiplierPerYear - ) internal { - - } - /** * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market * @param cash The amount of cash in the market From 8492092f6276bcc46671e6bf23fdb42085142416 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 26 Jun 2024 15:11:37 +0530 Subject: [PATCH 05/19] fix: updated comment --- contracts/TwoKinksInterestRateModel.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 2f66790b..69f09ff4 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -8,8 +8,7 @@ import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; /** * @title TwoKinksInterestRateModel * @author Venus - * @notice An interest rate model with a steep increase after a certain utilization threshold called **kink** is reached. - * The parameters of this interest rate model can be adjusted by the owner. Version 2 modifies Version 1 by enabling updateable parameters + * @notice An interest rate model with two different steep increase each after a certain utilization threshold called **kink** is reached. */ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { From 5e7f6e5617af70b3a0793754f2c9ff9d0f77f31e Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 26 Jun 2024 15:27:37 +0530 Subject: [PATCH 06/19] fix: negative multiplier --- contracts/TwoKinksInterestRateModel.sol | 100 ++++++++++++++---------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 69f09ff4..b18a85ad 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -7,83 +7,82 @@ import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; /** * @title TwoKinksInterestRateModel - * @author Venus + * @author Venus * @notice An interest rate model with two different steep increase each after a certain utilization threshold called **kink** is reached. */ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { - ////////////////////// SLOPE 1 ////////////////////// /** * @notice The multiplier of utilization rate per block or second that gives the slope 1 of the interest rate */ - uint256 public immutable multiplierPerBlockOrTimestamp; + int256 public immutable MULTIPLIER_PER_BLOCK_OR_TIMESTAMP; /** * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 */ - uint256 public immutable baseRatePerBlockOrTimestamp; + int256 public immutable BASE_RATE_PER_BLOCK_OR_TIMESTAMP; ////////////////////// SLOPE 2 ////////////////////// /** * @notice The utilization point at which the multiplier2 is applied */ - uint256 public immutable kink1; - + int256 public immutable KINK_1; + /** * @notice The multiplier of utilization rate per block or second that gives the slope 2 of the interest rate */ - uint256 public immutable multiplier2PerBlockOrTimestamp; + int256 public immutable MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP; /** - * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits kink1 + * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits KINK_1 */ - uint256 public immutable baseRate2PerBlockOrTimestamp; + int256 public immutable BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP; ////////////////////// SLOPE 3 ////////////////////// /** * @notice The utilization point at which the jump multiplier is applied */ - uint256 public immutable kink2; - + int256 public immutable KINK_2; /** - * @notice The multiplier per block or second after hitting kink2 + * @notice The multiplier per block or second after hitting KINK_2 */ - uint256 public jumpMultiplierPerBlockOrTimestamp; - + int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_TIMESTAMP; + /** * @notice Construct an interest rate model * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) * @param kink1_ The utilization point at which the multiplier2 is applied - * @param multiplier2PerYear_ The rate of increase in interest rate wrt utilization after hitting kink1 (scaled by EXP_SCALE) - * @param baseRate2PerYear_ The approximate target base APR after hitting kink1, as a mantissa (scaled by EXP_SCALE) + * @param multiplier2PerYear_ The rate of increase in interest rate wrt utilization after hitting KINK_1 (scaled by EXP_SCALE) + * @param baseRate2PerYear_ The approximate target base APR after hitting KINK_1, as a mantissa (scaled by EXP_SCALE) * @param kink2_ The utilization point at which the jump multiplier is applied - * @param jumpMultiplierPerYear_ The multiplier after hitting kink2 + * @param jumpMultiplierPerYear_ The multiplier after hitting KINK_2 * @param timeBased_ A boolean indicating whether the contract is based on time or block. * @param blocksPerYear_ The number of blocks per year */ constructor( - uint256 baseRatePerYear_, - uint256 multiplierPerYear_, - uint256 kink1_, - uint256 multiplier2PerYear_, - uint256 baseRate2PerYear_, - uint256 kink2_, - uint256 jumpMultiplierPerYear_, + int256 baseRatePerYear_, + int256 multiplierPerYear_, + int256 kink1_, + int256 multiplier2PerYear_, + int256 baseRate2PerYear_, + int256 kink2_, + int256 jumpMultiplierPerYear_, bool timeBased_, uint256 blocksPerYear_ ) TimeManagerV8(timeBased_, blocksPerYear_) { - baseRatePerBlockOrTimestamp = baseRatePerYear_ / blocksOrSecondsPerYear; - multiplierPerBlockOrTimestamp = multiplierPerYear_ / blocksOrSecondsPerYear; - kink1 = kink1_; - multiplier2PerBlockOrTimestamp = multiplier2PerYear_ / blocksOrSecondsPerYear; - baseRate2PerBlockOrTimestamp = baseRate2PerYear_ / blocksOrSecondsPerYear; - kink2 = kink2_; - jumpMultiplierPerBlockOrTimestamp = jumpMultiplierPerYear_ / blocksOrSecondsPerYear; + int256 blocksOrSecondsPerYear_ = int256(blocksOrSecondsPerYear); + BASE_RATE_PER_BLOCK_OR_TIMESTAMP = baseRatePerYear_ / blocksOrSecondsPerYear_; + MULTIPLIER_PER_BLOCK_OR_TIMESTAMP = multiplierPerYear_ / blocksOrSecondsPerYear_; + KINK_1 = kink1_; + MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP = multiplier2PerYear_ / blocksOrSecondsPerYear_; + BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP = baseRate2PerYear_ / blocksOrSecondsPerYear_; + KINK_2 = kink2_; + JUMP_MULTIPLIER_PER_BLOCK_OR_TIMESTAMP = jumpMultiplierPerYear_ / blocksOrSecondsPerYear_; } /** @@ -169,19 +168,34 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { uint256 reserves, uint256 badDebt ) internal view returns (uint256) { - uint256 util = utilizationRate(cash, borrows, reserves, badDebt); - - if (util < kink1) { - return ((util * multiplierPerBlockOrTimestamp) / EXP_SCALE) + baseRatePerBlockOrTimestamp; - } else if (util < kink2) { - uint256 rate1 = (((kink1 * multiplierPerBlockOrTimestamp) / EXP_SCALE) + baseRatePerBlockOrTimestamp); - uint256 rate2 = (((util - kink1) * multiplier2PerBlockOrTimestamp) / EXP_SCALE) + baseRate2PerBlockOrTimestamp; - return rate1 + rate2; + int256 util = int256(utilizationRate(cash, borrows, reserves, badDebt)); + int256 expScale = int256(EXP_SCALE); + + if (util < KINK_1) { + return _max(0, ((util * MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale) + BASE_RATE_PER_BLOCK_OR_TIMESTAMP); + } else if (util < KINK_2) { + int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale) + + BASE_RATE_PER_BLOCK_OR_TIMESTAMP); + int256 rate2 = (((util - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP) / expScale) + + BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP; + return _max(0, rate1 + rate2); } else { - uint256 rate1 = (((kink1 * multiplierPerBlockOrTimestamp) / EXP_SCALE) + baseRatePerBlockOrTimestamp); - uint256 rate2 = (((kink2 - kink1) * multiplier2PerBlockOrTimestamp) / EXP_SCALE) + baseRate2PerBlockOrTimestamp; - uint256 rate3 = (((util - kink2) * jumpMultiplierPerBlockOrTimestamp) / EXP_SCALE); - return rate1 + rate2 + rate3; + int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale) + + BASE_RATE_PER_BLOCK_OR_TIMESTAMP); + int256 rate2 = (((KINK_2 - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP) / expScale) + + BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP; + int256 rate3 = (((util - KINK_2) * JUMP_MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale); + return _max(0, rate1 + rate2 + rate3); } } + + /** + * @notice Returns the larger of two numbers + * @param a The first number + * @param b The second number + * @return The larger of the two numbers + */ + function _max(int256 a, int256 b) internal pure returns (uint256) { + return uint256(a > b ? a : b); + } } From 852760c73b9fb4a6e85490937696a05efe06c43e Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 27 Jun 2024 13:51:25 +0530 Subject: [PATCH 07/19] fix: below kink1 test --- tests/hardhat/TwoKinksInterestRateModel.ts | 132 +++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 tests/hardhat/TwoKinksInterestRateModel.ts diff --git a/tests/hardhat/TwoKinksInterestRateModel.ts b/tests/hardhat/TwoKinksInterestRateModel.ts new file mode 100644 index 00000000..91e6edf6 --- /dev/null +++ b/tests/hardhat/TwoKinksInterestRateModel.ts @@ -0,0 +1,132 @@ +import { FakeContract, smock } from "@defi-wonderland/smock"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import BigNumber from "bignumber.js"; +import chai from "chai"; +import { ethers } from "hardhat"; + +import { BSC_BLOCKS_PER_YEAR } from "../../helpers/deploymentConfig"; +import { convertToUnit } from "../../helpers/utils"; +import { TwoKinksInterestRateModel } from "../../typechain"; +import { getDescription } from "./util/descriptionHelpers"; +import { parseEther } from "ethers/lib/utils"; + +const { expect } = chai; +chai.use(smock.matchers); + +for (const isTimeBased of [false, true]) { + let twoKinksInterestRateModel: TwoKinksInterestRateModel; + + const baseRatePerYear = convertToUnit(0.2, 16); + const multiplierPerYear = convertToUnit(0.225, 18); + const kink1 = convertToUnit(0.2, 18); + const multiplier2PerYear = convertToUnit(0.625, 18); + const baseRate2PerYear = convertToUnit(0.6, 16); + const kink2 = convertToUnit(0.8, 18); + const jumpMultiplierPerYear = convertToUnit(6.8, 18); + + const cash = convertToUnit(10, 19); + const borrows = convertToUnit(4, 19); + const reserves = convertToUnit(2, 19); + const badDebt = convertToUnit(1, 19); + const expScale = convertToUnit(1, 18); + + const description = getDescription(isTimeBased); + let slotsPerYear = isTimeBased ? 0 : BSC_BLOCKS_PER_YEAR; + + describe(`${description}Two Kinks Interest Rate Model Tests`, async () => { + const fixture = async () => { + const TwoKinksInterestRateModelFactory = await ethers.getContractFactory("TwoKinksInterestRateModel"); + + twoKinksInterestRateModel = await TwoKinksInterestRateModelFactory.deploy( + baseRatePerYear, + multiplierPerYear, + kink1, + multiplier2PerYear, + baseRate2PerYear, + kink2, + jumpMultiplierPerYear, + isTimeBased, + slotsPerYear, + ); + await twoKinksInterestRateModel.deployed(); + }; + + before(async () => { + await loadFixture(fixture); + slotsPerYear = (await twoKinksInterestRateModel.blocksOrSecondsPerYear()).toNumber(); + }); + + it("Utilization rate: borrows and badDebt is zero", async () => { + expect(await twoKinksInterestRateModel.utilizationRate(cash, 0, reserves, 0)).equal(0); + }); + + it("Should return correct number of blocks", async () => { + expect(await twoKinksInterestRateModel.blocksOrSecondsPerYear()).to.equal(slotsPerYear); + }); + + it("Utilization rate", async () => { + const utilizationRate = new BigNumber(Number(borrows) + Number(badDebt)) + .multipliedBy(expScale) + .dividedBy(Number(cash) + Number(borrows) + Number(badDebt) - Number(reserves)) + .toFixed(0); + + expect(await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt)).equal(utilizationRate); + }); + + it("Borrow Rate: below kink1 utilization", async () => { + const cash = convertToUnit(12, 19); + const borrows = convertToUnit(1, 19); + const reserves = convertToUnit(2, 19); + const badDebt = convertToUnit(1, 19); + + const multiplierPerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_TIMESTAMP()).toString(); + const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_TIMESTAMP()).toString(); + const utilizationRate = (await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt)).toString(); + + const value = new BigNumber(utilizationRate) + .multipliedBy(multiplierPerBlockOrTimestamp) + .dividedBy(expScale) + .toFixed(0); + + expect(await twoKinksInterestRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).equal( + Number(value) + Number(baseRatePerBlockOrTimestamp), + ); + }); + + // it("Borrow Rate: above kink utilization", async () => { + // const multiplierPerBlockOrTimestamp = (await jumpRateModel.multiplierPerBlock()).toString(); + // const jumpMultiplierPerBlockOrTimestamp = (await jumpRateModel.jumpMultiplierPerBlock()).toString(); + // const baseRatePerBlockOrTimestamp = (await jumpRateModel.baseRatePerBlock()).toString(); + // const utilizationRate = ( + // await jumpRateModel.utilizationRate(convertToUnit(6, 19), convertToUnit(16, 19), reserves, badDebt) + // ).toString(); + + // const value = new BigNumber(kink).multipliedBy(multiplierPerBlockOrTimestamp).dividedBy(expScale).toFixed(0); + + // const normalRate = Number(value) + Number(baseRatePerBlockOrTimestamp); + // const excessUtil = Number(utilizationRate) - Number(kink); + + // const jumpValue = new BigNumber(excessUtil) + // .multipliedBy(jumpMultiplierPerBlockOrTimestamp) + // .dividedBy(expScale) + // .toFixed(0); + + // expect(await jumpRateModel.getBorrowRate(convertToUnit(6, 19), convertToUnit(16, 19), reserves, badDebt)).equal( + // Number(jumpValue) + Number(normalRate), + // ); + // }); + + // it("Supply Rate", async () => { + // const reserveMantissa = convertToUnit(1, 17); + // const oneMinusReserveFactor = Number(expScale) - Number(reserveMantissa); + // const borrowRate = (await jumpRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).toString(); + // const rateToPool = new BigNumber(borrowRate).multipliedBy(oneMinusReserveFactor).dividedBy(expScale).toFixed(0); + // const rate = new BigNumber(borrows) + // .multipliedBy(expScale) + // .dividedBy(Number(cash) + Number(borrows) + Number(badDebt) - Number(reserves)); + // const supplyRate = new BigNumber(rateToPool).multipliedBy(rate).dividedBy(expScale).toFixed(0); + + // expect(await jumpRateModel.getSupplyRate(cash, borrows, reserves, reserveMantissa, badDebt)).equal(supplyRate); + // }); + }); +} From 5d01bb0dc462b6dc2c68ed090b8d99687bba850a Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 27 Jun 2024 14:18:06 +0530 Subject: [PATCH 08/19] fix: test above kink1 and below kink2 --- contracts/TwoKinksInterestRateModel.sol | 42 ++++++------ tests/hardhat/TwoKinksInterestRateModel.ts | 80 ++++++++++++---------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index b18a85ad..dc426db9 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.25; import { TimeManagerV8 } from "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol"; import { InterestRateModel } from "./InterestRateModel.sol"; import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; +import "hardhat/console.sol"; /** * @title TwoKinksInterestRateModel @@ -16,12 +17,12 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { /** * @notice The multiplier of utilization rate per block or second that gives the slope 1 of the interest rate */ - int256 public immutable MULTIPLIER_PER_BLOCK_OR_TIMESTAMP; + int256 public immutable MULTIPLIER_PER_BLOCK_OR_SECOND; /** * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 */ - int256 public immutable BASE_RATE_PER_BLOCK_OR_TIMESTAMP; + int256 public immutable BASE_RATE_PER_BLOCK_OR_SECOND; ////////////////////// SLOPE 2 ////////////////////// @@ -33,12 +34,12 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { /** * @notice The multiplier of utilization rate per block or second that gives the slope 2 of the interest rate */ - int256 public immutable MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP; + int256 public immutable MULTIPLIER_2_PER_BLOCK_OR_SECOND; /** * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits KINK_1 */ - int256 public immutable BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP; + int256 public immutable BASE_RATE_2_PER_BLOCK_OR_SECOND; ////////////////////// SLOPE 3 ////////////////////// @@ -50,7 +51,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { /** * @notice The multiplier per block or second after hitting KINK_2 */ - int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_TIMESTAMP; + int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND; /** * @notice Construct an interest rate model @@ -76,13 +77,13 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { uint256 blocksPerYear_ ) TimeManagerV8(timeBased_, blocksPerYear_) { int256 blocksOrSecondsPerYear_ = int256(blocksOrSecondsPerYear); - BASE_RATE_PER_BLOCK_OR_TIMESTAMP = baseRatePerYear_ / blocksOrSecondsPerYear_; - MULTIPLIER_PER_BLOCK_OR_TIMESTAMP = multiplierPerYear_ / blocksOrSecondsPerYear_; + BASE_RATE_PER_BLOCK_OR_SECOND = baseRatePerYear_ / blocksOrSecondsPerYear_; + MULTIPLIER_PER_BLOCK_OR_SECOND = multiplierPerYear_ / blocksOrSecondsPerYear_; KINK_1 = kink1_; - MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP = multiplier2PerYear_ / blocksOrSecondsPerYear_; - BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP = baseRate2PerYear_ / blocksOrSecondsPerYear_; + MULTIPLIER_2_PER_BLOCK_OR_SECOND = multiplier2PerYear_ / blocksOrSecondsPerYear_; + BASE_RATE_2_PER_BLOCK_OR_SECOND = baseRate2PerYear_ / blocksOrSecondsPerYear_; KINK_2 = kink2_; - JUMP_MULTIPLIER_PER_BLOCK_OR_TIMESTAMP = jumpMultiplierPerYear_ / blocksOrSecondsPerYear_; + JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND = jumpMultiplierPerYear_ / blocksOrSecondsPerYear_; } /** @@ -172,19 +173,20 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 expScale = int256(EXP_SCALE); if (util < KINK_1) { - return _max(0, ((util * MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale) + BASE_RATE_PER_BLOCK_OR_TIMESTAMP); + return _max(0, ((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); } else if (util < KINK_2) { - int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale) + - BASE_RATE_PER_BLOCK_OR_TIMESTAMP); - int256 rate2 = (((util - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP) / expScale) + - BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP; + int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + + BASE_RATE_PER_BLOCK_OR_SECOND); + int256 rate2 = (((util - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + + BASE_RATE_2_PER_BLOCK_OR_SECOND; + return _max(0, rate1 + rate2); } else { - int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale) + - BASE_RATE_PER_BLOCK_OR_TIMESTAMP); - int256 rate2 = (((KINK_2 - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_TIMESTAMP) / expScale) + - BASE_RATE_2_PER_BLOCK_OR_TIMESTAMP; - int256 rate3 = (((util - KINK_2) * JUMP_MULTIPLIER_PER_BLOCK_OR_TIMESTAMP) / expScale); + int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + + BASE_RATE_PER_BLOCK_OR_SECOND); + int256 rate2 = (((KINK_2 - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + + BASE_RATE_2_PER_BLOCK_OR_SECOND; + int256 rate3 = (((util - KINK_2) * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); return _max(0, rate1 + rate2 + rate3); } } diff --git a/tests/hardhat/TwoKinksInterestRateModel.ts b/tests/hardhat/TwoKinksInterestRateModel.ts index 91e6edf6..a24d7164 100644 --- a/tests/hardhat/TwoKinksInterestRateModel.ts +++ b/tests/hardhat/TwoKinksInterestRateModel.ts @@ -1,7 +1,7 @@ import { FakeContract, smock } from "@defi-wonderland/smock"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import BigNumber from "bignumber.js"; -import chai from "chai"; +import chai, { util } from "chai"; import { ethers } from "hardhat"; import { BSC_BLOCKS_PER_YEAR } from "../../helpers/deploymentConfig"; @@ -79,10 +79,12 @@ for (const isTimeBased of [false, true]) { const reserves = convertToUnit(2, 19); const badDebt = convertToUnit(1, 19); - const multiplierPerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_TIMESTAMP()).toString(); - const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_TIMESTAMP()).toString(); + const multiplierPerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND()).toString(); + const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_SECOND()).toString(); const utilizationRate = (await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt)).toString(); + expect((new BigNumber(utilizationRate)).toNumber()).to.be.lt((new BigNumber(kink1)).toNumber()) + const value = new BigNumber(utilizationRate) .multipliedBy(multiplierPerBlockOrTimestamp) .dividedBy(expScale) @@ -93,40 +95,42 @@ for (const isTimeBased of [false, true]) { ); }); - // it("Borrow Rate: above kink utilization", async () => { - // const multiplierPerBlockOrTimestamp = (await jumpRateModel.multiplierPerBlock()).toString(); - // const jumpMultiplierPerBlockOrTimestamp = (await jumpRateModel.jumpMultiplierPerBlock()).toString(); - // const baseRatePerBlockOrTimestamp = (await jumpRateModel.baseRatePerBlock()).toString(); - // const utilizationRate = ( - // await jumpRateModel.utilizationRate(convertToUnit(6, 19), convertToUnit(16, 19), reserves, badDebt) - // ).toString(); - - // const value = new BigNumber(kink).multipliedBy(multiplierPerBlockOrTimestamp).dividedBy(expScale).toFixed(0); - - // const normalRate = Number(value) + Number(baseRatePerBlockOrTimestamp); - // const excessUtil = Number(utilizationRate) - Number(kink); - - // const jumpValue = new BigNumber(excessUtil) - // .multipliedBy(jumpMultiplierPerBlockOrTimestamp) - // .dividedBy(expScale) - // .toFixed(0); - - // expect(await jumpRateModel.getBorrowRate(convertToUnit(6, 19), convertToUnit(16, 19), reserves, badDebt)).equal( - // Number(jumpValue) + Number(normalRate), - // ); - // }); - - // it("Supply Rate", async () => { - // const reserveMantissa = convertToUnit(1, 17); - // const oneMinusReserveFactor = Number(expScale) - Number(reserveMantissa); - // const borrowRate = (await jumpRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).toString(); - // const rateToPool = new BigNumber(borrowRate).multipliedBy(oneMinusReserveFactor).dividedBy(expScale).toFixed(0); - // const rate = new BigNumber(borrows) - // .multipliedBy(expScale) - // .dividedBy(Number(cash) + Number(borrows) + Number(badDebt) - Number(reserves)); - // const supplyRate = new BigNumber(rateToPool).multipliedBy(rate).dividedBy(expScale).toFixed(0); - - // expect(await jumpRateModel.getSupplyRate(cash, borrows, reserves, reserveMantissa, badDebt)).equal(supplyRate); - // }); + it("Borrow Rate: above kink1 and below kink2 utilization", async () => { + const cash = convertToUnit(12, 19); + const borrows = convertToUnit(3, 19); + const reserves = convertToUnit(1, 19); + const badDebt = convertToUnit(1, 19); + + const multiplierPerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND()).toString(); + const multiplier2PerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_2_PER_BLOCK_OR_SECOND()).toString(); + const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_SECOND()).toString(); + const baseRate2PerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_2_PER_BLOCK_OR_SECOND()).toString(); + const utilizationRate = ( + await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt) + ).toString(); + + expect((new BigNumber(utilizationRate)).toNumber()).to.be.gt((new BigNumber(kink1)).toNumber()) + expect((new BigNumber(utilizationRate)).toNumber()).to.be.lt((new BigNumber(kink2)).toNumber()) + + const rate1 = new BigNumber(kink1).multipliedBy(multiplierPerBlockOrTimestamp).dividedBy(expScale).plus(baseRatePerBlockOrTimestamp).toFixed(0); + const rate2 = new BigNumber((new BigNumber(utilizationRate)).minus(kink1)).multipliedBy(multiplier2PerBlockOrTimestamp).dividedBy(expScale).plus(baseRate2PerBlockOrTimestamp).toFixed(0); + + expect(await twoKinksInterestRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).equal( + Number(rate1) + Number(rate2), + ); + }); + + it("Supply Rate", async () => { + const reserveMantissa = convertToUnit(1, 17); + const oneMinusReserveFactor = Number(expScale) - Number(reserveMantissa); + const borrowRate = (await twoKinksInterestRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).toString(); + const rateToPool = new BigNumber(borrowRate).multipliedBy(oneMinusReserveFactor).dividedBy(expScale).toFixed(0); + const rate = new BigNumber(borrows) + .multipliedBy(expScale) + .dividedBy(Number(cash) + Number(borrows) + Number(badDebt) - Number(reserves)); + const supplyRate = new BigNumber(rateToPool).multipliedBy(rate).dividedBy(expScale).toFixed(0); + + expect(await twoKinksInterestRateModel.getSupplyRate(cash, borrows, reserves, reserveMantissa, badDebt)).equal(supplyRate); + }); }); } From 43bf1e896dfe9775b31baaa6fb838a1a7f231292 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 27 Jun 2024 15:30:19 +0530 Subject: [PATCH 09/19] fix: added tests for above kink2 utilization rate --- contracts/TwoKinksInterestRateModel.sol | 10 +-- tests/hardhat/TwoKinksInterestRateModel.ts | 96 ++++++++++++++++++---- 2 files changed, 84 insertions(+), 22 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index dc426db9..e38f2c8c 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.25; import { TimeManagerV8 } from "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol"; import { InterestRateModel } from "./InterestRateModel.sol"; import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; -import "hardhat/console.sol"; /** * @title TwoKinksInterestRateModel @@ -175,18 +174,17 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { if (util < KINK_1) { return _max(0, ((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); } else if (util < KINK_2) { - int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + - BASE_RATE_PER_BLOCK_OR_SECOND); + int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 rate2 = (((util - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; - + return _max(0, rate1 + rate2); } else { - int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + - BASE_RATE_PER_BLOCK_OR_SECOND); + int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 rate2 = (((KINK_2 - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; int256 rate3 = (((util - KINK_2) * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); + return _max(0, rate1 + rate2 + rate3); } } diff --git a/tests/hardhat/TwoKinksInterestRateModel.ts b/tests/hardhat/TwoKinksInterestRateModel.ts index a24d7164..88a633f6 100644 --- a/tests/hardhat/TwoKinksInterestRateModel.ts +++ b/tests/hardhat/TwoKinksInterestRateModel.ts @@ -1,14 +1,13 @@ -import { FakeContract, smock } from "@defi-wonderland/smock"; +import { smock } from "@defi-wonderland/smock"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import BigNumber from "bignumber.js"; -import chai, { util } from "chai"; +import chai from "chai"; import { ethers } from "hardhat"; import { BSC_BLOCKS_PER_YEAR } from "../../helpers/deploymentConfig"; import { convertToUnit } from "../../helpers/utils"; import { TwoKinksInterestRateModel } from "../../typechain"; import { getDescription } from "./util/descriptionHelpers"; -import { parseEther } from "ethers/lib/utils"; const { expect } = chai; chai.use(smock.matchers); @@ -78,12 +77,16 @@ for (const isTimeBased of [false, true]) { const borrows = convertToUnit(1, 19); const reserves = convertToUnit(2, 19); const badDebt = convertToUnit(1, 19); - - const multiplierPerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND()).toString(); + + const multiplierPerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND() + ).toString(); const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_SECOND()).toString(); - const utilizationRate = (await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt)).toString(); + const utilizationRate = ( + await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt) + ).toString(); - expect((new BigNumber(utilizationRate)).toNumber()).to.be.lt((new BigNumber(kink1)).toNumber()) + expect(new BigNumber(utilizationRate).toNumber()).to.be.lt(new BigNumber(kink1).toNumber()); const value = new BigNumber(utilizationRate) .multipliedBy(multiplierPerBlockOrTimestamp) @@ -101,25 +104,84 @@ for (const isTimeBased of [false, true]) { const reserves = convertToUnit(1, 19); const badDebt = convertToUnit(1, 19); - const multiplierPerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND()).toString(); - const multiplier2PerBlockOrTimestamp = (await twoKinksInterestRateModel.MULTIPLIER_2_PER_BLOCK_OR_SECOND()).toString(); + const multiplierPerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND() + ).toString(); + const multiplier2PerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_2_PER_BLOCK_OR_SECOND() + ).toString(); const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_SECOND()).toString(); - const baseRate2PerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_2_PER_BLOCK_OR_SECOND()).toString(); + const baseRate2PerBlockOrTimestamp = ( + await twoKinksInterestRateModel.BASE_RATE_2_PER_BLOCK_OR_SECOND() + ).toString(); const utilizationRate = ( await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt) ).toString(); - expect((new BigNumber(utilizationRate)).toNumber()).to.be.gt((new BigNumber(kink1)).toNumber()) - expect((new BigNumber(utilizationRate)).toNumber()).to.be.lt((new BigNumber(kink2)).toNumber()) + expect(new BigNumber(utilizationRate).toNumber()).to.be.gt(new BigNumber(kink1).toNumber()); + expect(new BigNumber(utilizationRate).toNumber()).to.be.lt(new BigNumber(kink2).toNumber()); + + const rate1 = new BigNumber(kink1) + .multipliedBy(multiplierPerBlockOrTimestamp) + .dividedBy(expScale) + .plus(baseRatePerBlockOrTimestamp) + .toFixed(0); + const rate2 = new BigNumber(new BigNumber(utilizationRate).minus(kink1)) + .multipliedBy(multiplier2PerBlockOrTimestamp) + .dividedBy(expScale) + .plus(baseRate2PerBlockOrTimestamp) + .toFixed(0); - const rate1 = new BigNumber(kink1).multipliedBy(multiplierPerBlockOrTimestamp).dividedBy(expScale).plus(baseRatePerBlockOrTimestamp).toFixed(0); - const rate2 = new BigNumber((new BigNumber(utilizationRate)).minus(kink1)).multipliedBy(multiplier2PerBlockOrTimestamp).dividedBy(expScale).plus(baseRate2PerBlockOrTimestamp).toFixed(0); - expect(await twoKinksInterestRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).equal( Number(rate1) + Number(rate2), ); }); + it("Borrow Rate: above kink2 utilization", async () => { + const cash = convertToUnit(12, 19); + const borrows = convertToUnit(21, 19); + const reserves = convertToUnit(1, 19); + const badDebt = convertToUnit(24, 19); + + const multiplierPerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND() + ).toString(); + const multiplier2PerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_2_PER_BLOCK_OR_SECOND() + ).toString(); + const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_SECOND()).toString(); + const baseRate2PerBlockOrTimestamp = ( + await twoKinksInterestRateModel.BASE_RATE_2_PER_BLOCK_OR_SECOND() + ).toString(); + const jumpMultiplierPerBlockOrTimestamp = ( + await twoKinksInterestRateModel.JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND() + ).toString(); + const utilizationRate = ( + await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt) + ).toString(); + + expect(new BigNumber(utilizationRate).toNumber()).to.be.gt(new BigNumber(kink2).toNumber()); + + const rate1 = new BigNumber(kink1) + .multipliedBy(multiplierPerBlockOrTimestamp) + .dividedBy(expScale) + .plus(baseRatePerBlockOrTimestamp) + .toFixed(0); + const rate2 = new BigNumber(new BigNumber(kink2).minus(kink1)) + .multipliedBy(multiplier2PerBlockOrTimestamp) + .dividedBy(expScale) + .plus(baseRate2PerBlockOrTimestamp) + .toFixed(0); + const rate3 = new BigNumber(new BigNumber(utilizationRate).minus(kink2)) + .multipliedBy(jumpMultiplierPerBlockOrTimestamp) + .dividedBy(expScale) + .toFixed(0); + + expect(await twoKinksInterestRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).equal( + new BigNumber(rate1).plus(rate2).plus(rate3).toString(), + ); + }); + it("Supply Rate", async () => { const reserveMantissa = convertToUnit(1, 17); const oneMinusReserveFactor = Number(expScale) - Number(reserveMantissa); @@ -130,7 +192,9 @@ for (const isTimeBased of [false, true]) { .dividedBy(Number(cash) + Number(borrows) + Number(badDebt) - Number(reserves)); const supplyRate = new BigNumber(rateToPool).multipliedBy(rate).dividedBy(expScale).toFixed(0); - expect(await twoKinksInterestRateModel.getSupplyRate(cash, borrows, reserves, reserveMantissa, badDebt)).equal(supplyRate); + expect(await twoKinksInterestRateModel.getSupplyRate(cash, borrows, reserves, reserveMantissa, badDebt)).equal( + supplyRate, + ); }); }); } From 686e1702e15a83b17ef4c3b4640990c117cf34bf Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 27 Jun 2024 15:34:28 +0530 Subject: [PATCH 10/19] fix: added checks for negative value --- contracts/TwoKinksInterestRateModel.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index e38f2c8c..7783f814 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -52,6 +52,11 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { */ int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND; + /** + * @notice Thrown when a negative value is not allowed + */ + error NegativeValueNotAllowed(); + /** * @notice Construct an interest rate model * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) @@ -75,6 +80,10 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { bool timeBased_, uint256 blocksPerYear_ ) TimeManagerV8(timeBased_, blocksPerYear_) { + if (baseRatePerYear_ < 0 || baseRate2PerYear_ < 0 || kink1_ < 0 || kink2_ < 0) { + revert NegativeValueNotAllowed(); + } + int256 blocksOrSecondsPerYear_ = int256(blocksOrSecondsPerYear); BASE_RATE_PER_BLOCK_OR_SECOND = baseRatePerYear_ / blocksOrSecondsPerYear_; MULTIPLIER_PER_BLOCK_OR_SECOND = multiplierPerYear_ / blocksOrSecondsPerYear_; From 5a93f4d1b4d23d93b33b027f3aa72bd47e05d987 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 27 Jun 2024 15:51:44 +0530 Subject: [PATCH 11/19] fix: added test for negative values --- tests/hardhat/TwoKinksInterestRateModel.ts | 71 +++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/tests/hardhat/TwoKinksInterestRateModel.ts b/tests/hardhat/TwoKinksInterestRateModel.ts index 88a633f6..8ca066a6 100644 --- a/tests/hardhat/TwoKinksInterestRateModel.ts +++ b/tests/hardhat/TwoKinksInterestRateModel.ts @@ -30,7 +30,7 @@ for (const isTimeBased of [false, true]) { const expScale = convertToUnit(1, 18); const description = getDescription(isTimeBased); - let slotsPerYear = isTimeBased ? 0 : BSC_BLOCKS_PER_YEAR; + const slotsPerYear = isTimeBased ? 0 : BSC_BLOCKS_PER_YEAR; describe(`${description}Two Kinks Interest Rate Model Tests`, async () => { const fixture = async () => { @@ -52,7 +52,6 @@ for (const isTimeBased of [false, true]) { before(async () => { await loadFixture(fixture); - slotsPerYear = (await twoKinksInterestRateModel.blocksOrSecondsPerYear()).toNumber(); }); it("Utilization rate: borrows and badDebt is zero", async () => { @@ -60,6 +59,7 @@ for (const isTimeBased of [false, true]) { }); it("Should return correct number of blocks", async () => { + const slotsPerYear = (await twoKinksInterestRateModel.blocksOrSecondsPerYear()).toNumber(); expect(await twoKinksInterestRateModel.blocksOrSecondsPerYear()).to.equal(slotsPerYear); }); @@ -182,6 +182,73 @@ for (const isTimeBased of [false, true]) { ); }); + it("Borrow Rate: above kink2 utilization and negative multipliers", async () => { + const multiplierPerYear = convertToUnit(-0.225, 18); + const multiplier2PerYear = convertToUnit(-0.625, 18); + const jumpMultiplierPerYear = convertToUnit(-6.8, 18); + + const TwoKinksInterestRateModelFactory = await ethers.getContractFactory("TwoKinksInterestRateModel"); + + twoKinksInterestRateModel = await TwoKinksInterestRateModelFactory.deploy( + baseRatePerYear, + multiplierPerYear, + kink1, + multiplier2PerYear, + baseRate2PerYear, + kink2, + jumpMultiplierPerYear, + isTimeBased, + slotsPerYear, + ); + await twoKinksInterestRateModel.deployed(); + + const cash = convertToUnit(12, 19); + const borrows = convertToUnit(21, 19); + const reserves = convertToUnit(1, 19); + const badDebt = convertToUnit(24, 19); + + const multiplierPerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_PER_BLOCK_OR_SECOND() + ).toString(); + const multiplier2PerBlockOrTimestamp = ( + await twoKinksInterestRateModel.MULTIPLIER_2_PER_BLOCK_OR_SECOND() + ).toString(); + const baseRatePerBlockOrTimestamp = (await twoKinksInterestRateModel.BASE_RATE_PER_BLOCK_OR_SECOND()).toString(); + const baseRate2PerBlockOrTimestamp = ( + await twoKinksInterestRateModel.BASE_RATE_2_PER_BLOCK_OR_SECOND() + ).toString(); + const jumpMultiplierPerBlockOrTimestamp = ( + await twoKinksInterestRateModel.JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND() + ).toString(); + const utilizationRate = ( + await twoKinksInterestRateModel.utilizationRate(cash, borrows, reserves, badDebt) + ).toString(); + + expect(new BigNumber(utilizationRate).toNumber()).to.be.gt(new BigNumber(kink2).toNumber()); + + const rate1 = new BigNumber(kink1) + .multipliedBy(multiplierPerBlockOrTimestamp) + .dividedBy(expScale) + .plus(baseRatePerBlockOrTimestamp) + .toFixed(0); + const rate2 = new BigNumber(new BigNumber(kink2).minus(kink1)) + .multipliedBy(multiplier2PerBlockOrTimestamp) + .dividedBy(expScale) + .plus(baseRate2PerBlockOrTimestamp) + .toFixed(0); + const rate3 = new BigNumber(new BigNumber(utilizationRate).minus(kink2)) + .multipliedBy(jumpMultiplierPerBlockOrTimestamp) + .dividedBy(expScale) + .toFixed(0); + + let finalRate = new BigNumber(rate1).plus(rate2).plus(rate3).toNumber(); + if (finalRate < 0) { + finalRate = 0; + } + + expect(await twoKinksInterestRateModel.getBorrowRate(cash, borrows, reserves, badDebt)).equal(finalRate); + }); + it("Supply Rate", async () => { const reserveMantissa = convertToUnit(1, 17); const oneMinusReserveFactor = Number(expScale) - Number(reserveMantissa); From ece33796e826a15b70df06a5a1d2385e80feaa92 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 25 Jul 2024 18:09:48 +0530 Subject: [PATCH 12/19] fix: vpb-03 --- contracts/TwoKinksInterestRateModel.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 7783f814..db7a7515 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -57,6 +57,11 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { */ error NegativeValueNotAllowed(); + /** + * @notice Thrown when the kink points are not in the correct order + */ + error InvalidKink(); + /** * @notice Construct an interest rate model * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) @@ -84,6 +89,10 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { revert NegativeValueNotAllowed(); } + if (kink2_ <= kink1_ || kink1_ <= 0) { + revert InvalidKink(); + } + int256 blocksOrSecondsPerYear_ = int256(blocksOrSecondsPerYear); BASE_RATE_PER_BLOCK_OR_SECOND = baseRatePerYear_ / blocksOrSecondsPerYear_; MULTIPLIER_PER_BLOCK_OR_SECOND = multiplierPerYear_ / blocksOrSecondsPerYear_; From dd52dc9e40ddd60af5246a0eb65c52cbf27ce7fd Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Thu, 25 Jul 2024 18:12:24 +0530 Subject: [PATCH 13/19] fix: vpb-05 --- contracts/TwoKinksInterestRateModel.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index db7a7515..75e70bd4 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -8,7 +8,7 @@ import { EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol"; /** * @title TwoKinksInterestRateModel * @author Venus - * @notice An interest rate model with two different steep increase each after a certain utilization threshold called **kink** is reached. + * @notice An interest rate model with two different slope increase or decrease each after a certain utilization threshold called **kink** is reached. */ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { ////////////////////// SLOPE 1 ////////////////////// @@ -48,7 +48,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 public immutable KINK_2; /** - * @notice The multiplier per block or second after hitting KINK_2 + * @notice The multiplier of utilization rate per block or second that gives the slope 3 of interest rate */ int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND; @@ -65,10 +65,10 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { /** * @notice Construct an interest rate model * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE) - * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE) + * @param multiplierPerYear_ The rate of increase or decrease in interest rate wrt utilization (scaled by EXP_SCALE) * @param kink1_ The utilization point at which the multiplier2 is applied - * @param multiplier2PerYear_ The rate of increase in interest rate wrt utilization after hitting KINK_1 (scaled by EXP_SCALE) - * @param baseRate2PerYear_ The approximate target base APR after hitting KINK_1, as a mantissa (scaled by EXP_SCALE) + * @param multiplier2PerYear_ The rate of increase or decrease in interest rate wrt utilization after hitting KINK_1 (scaled by EXP_SCALE) + * @param baseRate2PerYear_ The additonal base APR after hitting KINK_1, as a mantissa (scaled by EXP_SCALE) * @param kink2_ The utilization point at which the jump multiplier is applied * @param jumpMultiplierPerYear_ The multiplier after hitting KINK_2 * @param timeBased_ A boolean indicating whether the contract is based on time or block. @@ -109,7 +109,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @param badDebt The amount of badDebt in the market - * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18) + * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE) */ function getBorrowRate( uint256 cash, From 00fdcdbbe86608a8c8d4783a4818c0c94e2c7bf8 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Tue, 30 Jul 2024 11:28:47 +0530 Subject: [PATCH 14/19] fix: vpb-02 --- contracts/TwoKinksInterestRateModel.sol | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 75e70bd4..9d80c4f8 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -193,15 +193,27 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { return _max(0, ((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); } else if (util < KINK_2) { int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); - int256 rate2 = (((util - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + + int256 slope2Util; + unchecked { + slope2Util = util - KINK_1; + } + int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; return _max(0, rate1 + rate2); } else { int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); - int256 rate2 = (((KINK_2 - KINK_1) * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + + int256 slope2Util; + unchecked { + slope2Util = KINK_2 - KINK_1; + } + int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; - int256 rate3 = (((util - KINK_2) * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); + int256 slope3Util; + unchecked { + slope3Util = util - KINK_2; + } + int256 rate3 = ((slope3Util * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); return _max(0, rate1 + rate2 + rate3); } From 66662d1338ad5e46dbb0c2090296d4b705b1d7d2 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Tue, 30 Jul 2024 11:30:27 +0530 Subject: [PATCH 15/19] fix: vpb-01 --- contracts/TwoKinksInterestRateModel.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 9d80c4f8..d1b88ca4 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -190,7 +190,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 expScale = int256(EXP_SCALE); if (util < KINK_1) { - return _max(0, ((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); + return _max(((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); } else if (util < KINK_2) { int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 slope2Util; @@ -200,7 +200,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; - return _max(0, rate1 + rate2); + return _max(rate1 + rate2); } else { int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 slope2Util; @@ -215,17 +215,17 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { } int256 rate3 = ((slope3Util * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); - return _max(0, rate1 + rate2 + rate3); + return _max(rate1 + rate2 + rate3); } } /** - * @notice Returns the larger of two numbers - * @param a The first number - * @param b The second number - * @return The larger of the two numbers + * @notice Returns 0 if number is less than 0, otherwise returns the input + * @param number The first number + * @return The maximum of 0 and input number */ - function _max(int256 a, int256 b) internal pure returns (uint256) { - return uint256(a > b ? a : b); + function _max(int256 number) internal pure returns (uint256) { + int256 zero; + return uint256(number > zero ? number : zero); } } From 455533e0eb2213fc8ec29a983389868e25f42038 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Tue, 30 Jul 2024 11:35:26 +0530 Subject: [PATCH 16/19] fix: vpb-01 --- contracts/TwoKinksInterestRateModel.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index d1b88ca4..7394a2ac 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -190,7 +190,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 expScale = int256(EXP_SCALE); if (util < KINK_1) { - return _max(((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); + return _minCap(((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); } else if (util < KINK_2) { int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 slope2Util; @@ -200,7 +200,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; - return _max(rate1 + rate2); + return _minCap(rate1 + rate2); } else { int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 slope2Util; @@ -215,7 +215,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { } int256 rate3 = ((slope3Util * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); - return _max(rate1 + rate2 + rate3); + return _minCap(rate1 + rate2 + rate3); } } @@ -224,7 +224,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { * @param number The first number * @return The maximum of 0 and input number */ - function _max(int256 number) internal pure returns (uint256) { + function _minCap(int256 number) internal pure returns (uint256) { int256 zero; return uint256(number > zero ? number : zero); } From 37ffc7963b209a61d2afc92bd061398268f163ae Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Wed, 31 Jul 2024 15:59:29 +0530 Subject: [PATCH 17/19] fix: vpb-03 alleviation --- contracts/TwoKinksInterestRateModel.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 7394a2ac..4586bd31 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -85,7 +85,7 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { bool timeBased_, uint256 blocksPerYear_ ) TimeManagerV8(timeBased_, blocksPerYear_) { - if (baseRatePerYear_ < 0 || baseRate2PerYear_ < 0 || kink1_ < 0 || kink2_ < 0) { + if (baseRatePerYear_ < 0 || baseRate2PerYear_ < 0) { revert NegativeValueNotAllowed(); } From 499a6bb3f52ef6df174f1e5f4bec51455801227f Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Fri, 16 Aug 2024 13:51:16 +0530 Subject: [PATCH 18/19] fix: ven-s2 --- contracts/TwoKinksInterestRateModel.sol | 31 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index 4586bd31..ed23c85e 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -40,6 +40,11 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { */ int256 public immutable BASE_RATE_2_PER_BLOCK_OR_SECOND; + /** + * @notice The maximum kink interest rate + */ + int256 public immutable RATE_1; + ////////////////////// SLOPE 3 ////////////////////// /** @@ -52,6 +57,11 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { */ int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND; + /** + * @notice The maximum kink interest rate + */ + int256 public immutable RATE_2; + /** * @notice Thrown when a negative value is not allowed */ @@ -101,6 +111,15 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { BASE_RATE_2_PER_BLOCK_OR_SECOND = baseRate2PerYear_ / blocksOrSecondsPerYear_; KINK_2 = kink2_; JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND = jumpMultiplierPerYear_ / blocksOrSecondsPerYear_; + + int256 expScale = int256(EXP_SCALE); + RATE_1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); + + int256 slope2Util; + unchecked { + slope2Util = KINK_2 - KINK_1; + } + RATE_2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; } /** @@ -192,7 +211,6 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { if (util < KINK_1) { return _minCap(((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); } else if (util < KINK_2) { - int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); int256 slope2Util; unchecked { slope2Util = util - KINK_1; @@ -200,22 +218,15 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND; - return _minCap(rate1 + rate2); + return _minCap(RATE_1 + rate2); } else { - int256 rate1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND); - int256 slope2Util; - unchecked { - slope2Util = KINK_2 - KINK_1; - } - int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + - BASE_RATE_2_PER_BLOCK_OR_SECOND; int256 slope3Util; unchecked { slope3Util = util - KINK_2; } int256 rate3 = ((slope3Util * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale); - return _minCap(rate1 + rate2 + rate3); + return _minCap(RATE_1 + RATE_2 + rate3); } } From 2044826425fbf80d1e6877741c260c49707a68f6 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Fri, 16 Aug 2024 13:55:32 +0530 Subject: [PATCH 19/19] fix: ven-s3 --- contracts/TwoKinksInterestRateModel.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/TwoKinksInterestRateModel.sol b/contracts/TwoKinksInterestRateModel.sol index ed23c85e..acde2543 100644 --- a/contracts/TwoKinksInterestRateModel.sol +++ b/contracts/TwoKinksInterestRateModel.sol @@ -14,12 +14,12 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { ////////////////////// SLOPE 1 ////////////////////// /** - * @notice The multiplier of utilization rate per block or second that gives the slope 1 of the interest rate + * @notice The multiplier of utilization rate per block or second that gives the slope 1 of the interest rate scaled by EXP_SCALE */ int256 public immutable MULTIPLIER_PER_BLOCK_OR_SECOND; /** - * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 + * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 scaled by EXP_SCALE */ int256 public immutable BASE_RATE_PER_BLOCK_OR_SECOND; @@ -31,17 +31,17 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 public immutable KINK_1; /** - * @notice The multiplier of utilization rate per block or second that gives the slope 2 of the interest rate + * @notice The multiplier of utilization rate per block or second that gives the slope 2 of the interest rate scaled by EXP_SCALE */ int256 public immutable MULTIPLIER_2_PER_BLOCK_OR_SECOND; /** - * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits KINK_1 + * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits KINK_1 scaled by EXP_SCALE */ int256 public immutable BASE_RATE_2_PER_BLOCK_OR_SECOND; /** - * @notice The maximum kink interest rate + * @notice The maximum kink interest rate scaled by EXP_SCALE */ int256 public immutable RATE_1; @@ -53,12 +53,12 @@ contract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 { int256 public immutable KINK_2; /** - * @notice The multiplier of utilization rate per block or second that gives the slope 3 of interest rate + * @notice The multiplier of utilization rate per block or second that gives the slope 3 of interest rate scaled by EXP_SCALE */ int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND; /** - * @notice The maximum kink interest rate + * @notice The maximum kink interest rate scaled by EXP_SCALE */ int256 public immutable RATE_2;