Skip to content

Commit

Permalink
fix: VEN-1487
Browse files Browse the repository at this point in the history
  • Loading branch information
defcon022 committed May 29, 2023
1 parent aa96690 commit 9d200e7
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 25 deletions.
24 changes: 15 additions & 9 deletions contracts/BaseJumpRateModelV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,38 +102,42 @@ abstract contract BaseJumpRateModelV2 is InterestRateModel {
* @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 reserveFactorMantissa,
uint256 badDebt
) public view virtual override returns (uint256) {
uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa;
uint256 borrowRate = _getBorrowRate(cash, borrows, reserves);
uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt);
uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE;
return (utilizationRate(cash, borrows, reserves) * rateToPool) / EXP_SCALE;
return (utilizationRate(cash, borrows, reserves, badDebt) * rateToPool) / EXP_SCALE;
}

/**
* @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`
* @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 reserves,
uint256 badDebt
) public pure returns (uint256) {
// Utilization rate is 0 when there are no borrows
if (borrows == 0) {
if ((borrows + badDebt) == 0) {
return 0;
}

uint256 rate = (borrows * EXP_SCALE) / (cash + borrows - reserves);
uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves);

if (rate > EXP_SCALE) {
rate = EXP_SCALE;
Expand Down Expand Up @@ -168,14 +172,16 @@ abstract contract BaseJumpRateModelV2 is InterestRateModel {
* @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)
*/
function _getBorrowRate(
uint256 cash,
uint256 borrows,
uint256 reserves
uint256 reserves,
uint256 badDebt
) internal view returns (uint256) {
uint256 util = utilizationRate(cash, borrows, reserves);
uint256 util = utilizationRate(cash, borrows, reserves, badDebt);
uint256 kink_ = kink;

if (util <= kink_) {
Expand Down
8 changes: 6 additions & 2 deletions contracts/InterestRateModel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ abstract contract InterestRateModel {
* @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
* @return The borrow rate per block (as a percentage, and scaled by 1e18)
*/
function getBorrowRate(
uint256 cash,
uint256 borrows,
uint256 reserves
uint256 reserves,
uint256 badDebt
) external view virtual returns (uint256);

/**
Expand All @@ -25,13 +27,15 @@ abstract contract InterestRateModel {
* @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 reserveFactorMantissa,
uint256 badDebt
) external view virtual returns (uint256);

/**
Expand Down
6 changes: 4 additions & 2 deletions contracts/JumpRateModelV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ contract JumpRateModelV2 is BaseJumpRateModelV2 {
* @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 1e18)
*/
function getBorrowRate(
uint256 cash,
uint256 borrows,
uint256 reserves
uint256 reserves,
uint256 badDebt
) external view override returns (uint256) {
return _getBorrowRate(cash, borrows, reserves);
return _getBorrowRate(cash, borrows, reserves, badDebt);
}
}
13 changes: 10 additions & 3 deletions contracts/VToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -654,15 +654,22 @@ contract VToken is
* @return rate The borrow interest rate per block, scaled by 1e18
*/
function borrowRatePerBlock() external view override returns (uint256) {
return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves);
return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);
}

/**
* @notice Returns the current per-block supply interest rate for this v
* @return rate The supply interest rate per block, scaled by 1e18
*/
function supplyRatePerBlock() external view override returns (uint256) {
return interestRateModel.getSupplyRate(_getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa);
return
interestRateModel.getSupplyRate(
_getCashPrior(),
totalBorrows,
totalReserves,
reserveFactorMantissa,
badDebt
);
}

/**
Expand Down Expand Up @@ -717,7 +724,7 @@ contract VToken is
uint256 borrowIndexPrior = borrowIndex;

/* Calculate the current borrow interest rate */
uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);
require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, "borrow rate is absurdly high");

/* Calculate the number of blocks elapsed since the last accrual */
Expand Down
24 changes: 15 additions & 9 deletions contracts/WhitePaperInterestRateModel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ contract WhitePaperInterestRateModel is InterestRateModel {
* @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)
*/
function getBorrowRate(
uint256 cash,
uint256 borrows,
uint256 reserves
uint256 reserves,
uint256 badDebt
) public view override returns (uint256) {
uint256 ur = utilizationRate(cash, borrows, reserves);
uint256 ur = utilizationRate(cash, borrows, reserves, badDebt);
return ((ur * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock;
}

Expand All @@ -56,38 +58,42 @@ contract WhitePaperInterestRateModel is InterestRateModel {
* @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 reserveFactorMantissa,
uint256 badDebt
) public view override returns (uint256) {
uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa;
uint256 borrowRate = getBorrowRate(cash, borrows, reserves);
uint256 borrowRate = getBorrowRate(cash, borrows, reserves, badDebt);
uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE;
return (utilizationRate(cash, borrows, reserves) * rateToPool) / EXP_SCALE;
return (utilizationRate(cash, borrows, reserves, badDebt) * rateToPool) / EXP_SCALE;
}

/**
* @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`
* @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 reserves,
uint256 badDebt
) public pure returns (uint256) {
// Utilization rate is 0 when there are no borrows
if (borrows == 0) {
if ((borrows + badDebt) == 0) {
return 0;
}

uint256 rate = (borrows * EXP_SCALE) / (cash + borrows - reserves);
uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves);

if (rate > EXP_SCALE) {
rate = EXP_SCALE;
Expand Down
106 changes: 106 additions & 0 deletions tests/hardhat/JumpRateModelV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { FakeContract, smock } from "@defi-wonderland/smock";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import chai from "chai";
import { ethers } from "hardhat";

import { convertToUnit } from "../../helpers/utils";
import { AccessControlManager, JumpRateModelV2 } from "../../typechain";

const { expect } = chai;
chai.use(smock.matchers);

describe("Jump rate model tests", () => {
let jumpRateModel: JumpRateModelV2;
let accessControlManager: FakeContract<AccessControlManager>;

const kink = convertToUnit(8, 17);

const fixture = async () => {
accessControlManager = await smock.fake<AccessControlManager>("AccessControlManager");
accessControlManager.isAllowedToCall.returns(true);

const JumpRateModelFactory = await ethers.getContractFactory("JumpRateModelV2");
jumpRateModel = await JumpRateModelFactory.deploy(
convertToUnit(2, 12),
convertToUnit(4, 14),
convertToUnit(2, 18),
kink,
accessControlManager.address,
);
await jumpRateModel.deployed();
};

before(async () => {
await loadFixture(fixture);
});

it("Update jump rate model", async () => {
expect(await jumpRateModel.baseRatePerBlock()).equal("190258");
expect(await jumpRateModel.multiplierPerBlock()).equal("47564687");
expect(await jumpRateModel.jumpMultiplierPerBlock()).equal("190258751902");
expect(await jumpRateModel.kink()).equal(kink);

await jumpRateModel.updateJumpRateModel(convertToUnit(3, 12), convertToUnit(5, 14), convertToUnit(2.2, 18), kink);

expect(await jumpRateModel.baseRatePerBlock()).equal("285388");
expect(await jumpRateModel.multiplierPerBlock()).equal("59455859");
expect(await jumpRateModel.jumpMultiplierPerBlock()).equal("209284627092");
expect(await jumpRateModel.kink()).equal(kink);
});

it("Utilization rate: borrows and badDebt is zero", async () => {
expect(
await jumpRateModel.utilizationRate(
convertToUnit(10, 19),
convertToUnit(0, 17),
convertToUnit(1, 19),
convertToUnit(0, 19),
),
).equal(0);
});

it("Utilization rate", async () => {
expect(
await jumpRateModel.utilizationRate(
convertToUnit(10, 19),
convertToUnit(4, 19),
convertToUnit(2, 19),
convertToUnit(1, 19),
),
).equal("384615384615384615");
});

it("Borrow Rate: below kink utilization", async () => {
expect(
await jumpRateModel.getBorrowRate(
convertToUnit(10, 19),
convertToUnit(4, 19),
convertToUnit(2, 19),
convertToUnit(1, 19),
),
).equal("23153026");
});

it("Borrow Rate: above kink utilization", async () => {
expect(
await jumpRateModel.getBorrowRate(
convertToUnit(6, 19),
convertToUnit(16, 19),
convertToUnit(2, 19),
convertToUnit(1, 19),
),
).equal("2041036999");
});

it("Supply Rate", async () => {
expect(
await jumpRateModel.getSupplyRate(
convertToUnit(10, 19),
convertToUnit(4, 19),
convertToUnit(2, 19),
convertToUnit(1, 17),
convertToUnit(1, 19),
),
).equal("8014508");
});
});
Loading

0 comments on commit 9d200e7

Please sign in to comment.