Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stable rate borrowing #175

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
679b7c2
Feature: stable rate borrowing (#132)
Debugger022 Feb 3, 2023
d24258c
Added rebalancing conditions for the stable rate.
Debugger022 Feb 3, 2023
72f2aaf
Borrow at both rates.
Debugger022 Feb 3, 2023
81a198d
Setters for the thresholds.
Debugger022 Feb 3, 2023
4c318f8
Rebalance stable rate tests.
Debugger022 Feb 6, 2023
6609c29
Swap borrow rate mode with amount.
Debugger022 Feb 8, 2023
5a3cb09
Tests: swap borrow rate amount.
Debugger022 Feb 8, 2023
8dbf6c7
Minor changes.
Debugger022 Feb 13, 2023
532d3d7
Fix: PR comments.
Debugger022 Feb 20, 2023
86ccc84
Refactor: rebalancing condition for the stable rate.
Debugger022 Feb 21, 2023
cbcef80
Refactored: swapBorrowRateMode method.
Debugger022 Feb 21, 2023
c281ffb
Fix: PR comments.
Debugger022 Feb 23, 2023
1059cfb
Fix: PR comments.
Debugger022 Feb 23, 2023
bc81167
VEN-1123: Encapsulate the ACM check in one function.
Debugger022 Feb 24, 2023
f007edf
Refactored functions order.
Debugger022 Mar 1, 2023
21706c5
Fix: deployment script.
Debugger022 Mar 1, 2023
2eff728
fix: merge conflicts in contracts
Debugger022 Oct 3, 2023
6e73662
tests: fixed tests after merge conflicts
Debugger022 Oct 3, 2023
57cb334
tests: fixed vToken tests after merge conflicts
Debugger022 Oct 4, 2023
8fa9186
tests: fixed integration tests, deployment config, deployment scripts…
Debugger022 Oct 4, 2023
6a43338
refactor: deployment config
Debugger022 Oct 4, 2023
6e20a8e
tests: added mock contract for access control manager
Debugger022 Oct 18, 2023
0ef292c
fix: merge conflicts
Debugger022 Oct 18, 2023
6123b4e
fix: deployment config file
Debugger022 Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 13 additions & 75 deletions contracts/BaseJumpRateModelV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.13;
import { IAccessControlManagerV8 } from "@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol";

import { InterestRateModel } from "./InterestRateModel.sol";
import { BLOCKS_PER_YEAR, EXP_SCALE, MANTISSA_ONE } from "./lib/constants.sol";
import { BLOCKS_PER_YEAR, EXP_SCALE } from "./lib/constants.sol";

/**
* @title Logic for Compound's JumpRateModel Contract V2.
Expand Down Expand Up @@ -97,58 +97,6 @@ abstract contract BaseJumpRateModelV2 is InterestRateModel {
_updateJumpRateModel(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_);
}

/**
* @notice Calculates the current supply rate per block
* @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 block 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)
Expand All @@ -172,29 +120,19 @@ abstract contract BaseJumpRateModelV2 is InterestRateModel {

/**
* @notice Calculates the current borrow rate per block, 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 block as a mantissa (scaled by EXP_SCALE)
* @param utRate The utilization rate per total borrows and cash available
* @return The borrow rate percentage per block as a mantissa (scaled by BASE)
*/
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_;
function _getBorrowRate(uint256 utRate) internal view returns (uint256) {
if (utRate <= kink) {
return ((utRate * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock;
} else {
uint256 normalRate = ((kink * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock;
uint256 excessUtil;
unchecked {
excessUtil = utRate - kink;
}
return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate;
}
return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate;
}
}
4 changes: 4 additions & 0 deletions contracts/Comptroller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,10 @@ contract Comptroller is
_setMaxLoopsLimit(limit);
}

function preSwapBorrowRateModeHook(address vToken) external view override {
_checkActionPauseState(vToken, Action.SWAP_RATE_MODE);
}

/**
* @notice Determine the current account liquidity with respect to liquidation threshold requirements
* @dev The interface of this function is intentionally kept compatible with Compound and Venus Core
Expand Down
2 changes: 2 additions & 0 deletions contracts/ComptrollerInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ interface ComptrollerInterface {

function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;

function preSwapBorrowRateModeHook(address vToken) external;

function isComptroller() external view returns (bool);

/*** Liquidity/Liquidation Calculations ***/
Expand Down
3 changes: 2 additions & 1 deletion contracts/ComptrollerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ contract ComptrollerStorage {
LIQUIDATE,
TRANSFER,
ENTER_MARKET,
EXIT_MARKET
EXIT_MARKET,
SWAP_RATE_MODE
}

/**
Expand Down
6 changes: 6 additions & 0 deletions contracts/ErrorReporter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ contract TokenErrorReporter {

error LiquidateSeizeLiquidatorIsBorrower();

error SetComptrollerOwnerCheck();

error ProtocolSeizeShareTooBig();

error SetReserveFactorFreshCheck();
Expand All @@ -45,4 +47,8 @@ contract TokenErrorReporter {
error ReduceReservesCashValidation();

error SetInterestRateModelFreshCheck();
error SetStableInterestRateModelFreshCheck();

error SwapBorrowRateModeFreshnessCheck();
error SetRebalanceUtilizationRateThresholdAdminCheck();
}
128 changes: 128 additions & 0 deletions contracts/InterestRate/StableRateModel.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

/**
* @title Logic for Venus stable rate.
*/
contract StableRateModel {
uint256 private constant BASE = 1e18;

/// @notice The approximate number of blocks per year that is assumed by the interest rate model
uint256 public constant BLOCK_PER_YEAR = 2102400;

/// @notice The stable base interest rate which is the y-intercept when utilization rate is 0(also known by base_premium)
uint256 public baseRatePerBlock;

/// @notice The premium rate applicable on optimal stable loan rate(also known by stable_rate_slope)
uint256 public stableRatePremium;

/// @notice The factor to be applied to the stable rate premium before adding to the interest rate
uint256 public optimalStableLoanRatio;

/// @notice The address of the owner, i.e. the Timelock contract, which can update parameters directly
address public owner;

event NewStableInterestParams(uint256 baseRatePerBlock, uint256 stableRatePremium, uint256 optimalStableLoanRatio);

/**
* @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by BASE)
* @param stableRatePremium_ The multiplierPerBlock after hitting a specified utilization point
* @param optimalStableLoanRatio_ Optimal stable loan rate percentage.
* @param owner_ Address of the owner for this model(Governance)
*/
constructor(uint256 baseRatePerYear_, uint256 stableRatePremium_, uint256 optimalStableLoanRatio_, address owner_) {
owner = owner_;

updateStableRateModelInternal(baseRatePerYear_, stableRatePremium_, optimalStableLoanRatio_);
}

/**
* @notice Updates the parameters of the interest rate model (only callable by owner, i.e. Timelock)
* @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by BASE)
* @param stableRatePremium_ The multiplierPerBlock after hitting a specified utilization point
* @param optimalStableLoanRatio_ Optimal stable loan rate percentage.
* @custom:events Emits NewStableInterestParams, after updating the parameters
* @custom:access Only governance
*/
function updateStableRateModel(
uint256 baseRatePerYear_,
uint256 stableRatePremium_,
uint256 optimalStableLoanRatio_
) external virtual {
require(msg.sender == owner, "StableRateModel: only owner may call this function.");

updateStableRateModelInternal(baseRatePerYear_, stableRatePremium_, optimalStableLoanRatio_);
}

/**
* @notice Calculates the current borrow rate per block, with the error code expected by the market
* @param stableBorrows The amount of stable borrows in the market
* @param totalBorrows The amount of borrows in the market
* @param variableBorrowRate Current variable borrow rate per block of the protocol
* @return The borrow rate percentage per block as a mantissa (scaled by BASE)
*/
function getBorrowRate(
uint256 stableBorrows,
uint256 totalBorrows,
uint256 variableBorrowRate
) external view returns (uint256) {
uint256 loanRatio = stableLoanRatio(stableBorrows, totalBorrows);
uint256 excessLoanRatio = calculateLoanRatioDiff(loanRatio);

return (variableBorrowRate + baseRatePerBlock + ((stableRatePremium * excessLoanRatio) / BASE));
}

/**
* @notice Indicator that this is an InterestRateModel contract (for inspection)
* @return Always true
*/
function isStableRateModel() external pure virtual returns (bool) {
return true;
}

/**
* @notice Calculates the ratio of the stable borrows to total borrows
* @param stableBorrows The amount of stable borrows in the market
* @param totalBorrows The amount of total borrows in the market
* @return The stable loan rate as a mantissa between [0, BASE]
*/
function stableLoanRatio(uint256 stableBorrows, uint256 totalBorrows) public pure returns (uint256) {
// Loan ratio is 0 when there are no stable borrows
if (totalBorrows == 0) {
return 0;
}

return (stableBorrows * BASE) / totalBorrows;
}

/**
* @notice Internal function to update the parameters of the interest rate model
* @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by BASE)
* @param stableRatePremium_ The multiplierPerBlock after hitting a specified utilization point
* @param optimalStableLoanRatio_ Optimal stable loan rate percentage.
*/
function updateStableRateModelInternal(
uint256 baseRatePerYear_,
uint256 stableRatePremium_,
uint256 optimalStableLoanRatio_
) internal {
baseRatePerBlock = baseRatePerYear_ / BLOCK_PER_YEAR;
stableRatePremium = stableRatePremium_;
optimalStableLoanRatio = optimalStableLoanRatio_;

emit NewStableInterestParams(baseRatePerBlock, stableRatePremium, optimalStableLoanRatio);
}

/**
* @notice Calculates the difference of stableLoanRatio and the optimalStableLoanRatio
* @param loanRatio Stable loan ratio for stable borrows in the market
* @return The difference between the stableLoanRatio and optimal loan rate as a mantissa between [0, BASE]
*/
function calculateLoanRatioDiff(uint256 loanRatio) internal view returns (uint256) {
if (loanRatio == 0 || optimalStableLoanRatio > loanRatio) {
return 0;
}

return (loanRatio - optimalStableLoanRatio);
}
}
29 changes: 2 additions & 27 deletions contracts/InterestRateModel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,10 @@ pragma solidity 0.8.13;
abstract contract InterestRateModel {
/**
* @notice Calculates the current borrow interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @param badDebt The amount of badDebt in the market
* @param utilizationRate The utilization rate per total borrows and cash available
* @return The borrow rate per block (as a percentage, and scaled by 1e18)
*/
function getBorrowRate(
uint256 cash,
uint256 borrows,
uint256 reserves,
uint256 badDebt
) external view virtual returns (uint256);

/**
* @notice Calculates the current supply interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @param reserveFactorMantissa The current reserve factor the market has
* @param badDebt The amount of badDebt in the market
* @return The supply rate per block (as a percentage, and scaled by 1e18)
*/
function getSupplyRate(
uint256 cash,
uint256 borrows,
uint256 reserves,
uint256 reserveFactorMantissa,
uint256 badDebt
) external view virtual returns (uint256);
function getBorrowRate(uint256 utilizationRate) external view virtual returns (uint256);

/**
* @notice Indicator that this is an InterestRateModel contract (for inspection)
Expand Down
14 changes: 3 additions & 11 deletions contracts/JumpRateModelV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,10 @@ contract JumpRateModelV2 is BaseJumpRateModelV2 {

/**
* @notice Calculates the current borrow rate per block
* @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
* @param utilizationRate The utilization rate per total borrows and cash available
* @return The borrow rate percentage per block 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);
function getBorrowRate(uint256 utilizationRate) external view override returns (uint256) {
return _getBorrowRate(utilizationRate);
}
}
2 changes: 2 additions & 0 deletions contracts/Lens/PoolLens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ contract PoolLens is ExponentialNoError {
uint256 exchangeRateCurrent;
uint256 supplyRatePerBlock;
uint256 borrowRatePerBlock;
uint256 stableBorrowRatePerBlock;
uint256 reserveFactorMantissa;
uint256 supplyCaps;
uint256 borrowCaps;
Expand Down Expand Up @@ -386,6 +387,7 @@ contract PoolLens is ExponentialNoError {
exchangeRateCurrent: exchangeRateCurrent,
supplyRatePerBlock: vToken.supplyRatePerBlock(),
borrowRatePerBlock: vToken.borrowRatePerBlock(),
stableBorrowRatePerBlock: vToken.stableBorrowRatePerBlock(),
reserveFactorMantissa: vToken.reserveFactorMantissa(),
supplyCaps: comptroller.supplyCaps(address(vToken)),
borrowCaps: comptroller.borrowCaps(address(vToken)),
Expand Down
Loading
Loading