From 9ea64ddd166a78b264ba8006f688880085eeed13 Mon Sep 17 00:00:00 2001 From: Geoffrey Hayes Date: Fri, 6 Dec 2019 12:31:13 -0800 Subject: [PATCH] [Release Candidate] Improve DSR Integration and Jump Rate Model This patch makes a few important changes. We first fix an optimization of CDaiDelegate that simplifies a variety of logic in certain calculations. Secondly, we fix the jump rate model to have a fixed slope past the kink, as opposed to a certain multiplier. --- contracts/CDaiDelegate.sol | 9 +- contracts/DAIInterestRateModel.sol | 40 +++++--- contracts/JumpRateModel.sol | 27 +++-- spec/scenario/MCDai.scen | 1 - test/Models/DAIInterestRateModelTest.js | 54 +++++----- test/Models/interestRateModelTest.js | 130 ++++++++++++++++++++---- test/Tokens/cTokenTest.js | 5 +- test/Utils/Compound.js | 6 +- 8 files changed, 187 insertions(+), 85 deletions(-) diff --git a/contracts/CDaiDelegate.sol b/contracts/CDaiDelegate.sol index e1bd48903..e27aa5ded 100644 --- a/contracts/CDaiDelegate.sol +++ b/contracts/CDaiDelegate.sol @@ -157,18 +157,13 @@ contract CDaiDelegate is CErc20Delegate { function doTransferOut(address payable to, uint amount) internal { DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); PotLike pot = PotLike(potAddress); - VatLike vat = VatLike(vatAddress); // Calculate the percentage decrease from the pot, and move that much out // Note: Use a slightly larger pie size to ensure that we get at least amount in the vat - uint pie = mul(add(amount, 1), RAY) / pot.chi(); + uint pie = add(mul(amount, RAY) / pot.chi(), 1); pot.exit(pie); - // Checks the actual balance of DAI in the vat after the pot exit - uint bal = vat.dai(address(this)); - - // Remove our whole balance if rounding would lead us to remove more than we have - daiJoin.exit(to, bal >= mul(amount, RAY) ? amount : bal / RAY); + daiJoin.exit(to, amount); } /*** Maker Internals ***/ diff --git a/contracts/DAIInterestRateModel.sol b/contracts/DAIInterestRateModel.sol index eb839f972..5db20504d 100644 --- a/contracts/DAIInterestRateModel.sol +++ b/contracts/DAIInterestRateModel.sol @@ -11,19 +11,29 @@ import "./SafeMath.sol"; contract DAIInterestRateModel is JumpRateModel { using SafeMath for uint; + /** + * @notice The additional margin per block separating the base borrow rate from the roof (0.05% / block) + */ + uint public constant gapPerBlock = 0.05e16 / blocksPerYear; + + /** + * @notice The assumed (1 - reserve factor) used to calculate the minimum borrow rate (reserve factor = 0.05) + */ + uint public constant assumedOneMinusReserveFactorMantissa = 0.95e18; + PotLike pot; JugLike jug; /** * @notice Construct an interest rate model - * @param _pot The approximate target base APR, as a mantissa (scaled by 1e18) - * @param _jug The rate of increase in interest rate wrt utilization (scaled by 1e18) - * @param _kink The utilization point at which an additional multiplier is applied - * @param _jump The additional multiplier to be applied to multiplierPerBlock after hitting a specified utilization point + * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point + * @param kink_ The utilization point at which the jump multiplier is applied + * @param pot_ The address of the Dai pot (where DSR is earned) + * @param jug_ The address of the Dai jug (where SF is kept) */ - constructor(address _pot, address _jug, uint _kink, uint _jump) JumpRateModel(0, 0, _kink, _jump) public { - pot = PotLike(_pot); - jug = JugLike(_jug); + constructor(uint jumpMultiplierPerYear, uint kink_, address pot_, address jug_) JumpRateModel(0, 0, jumpMultiplierPerYear, kink_) public { + pot = PotLike(pot_); + jug = JugLike(jug_); poke(); } @@ -58,18 +68,24 @@ contract DAIInterestRateModel is JumpRateModel { .mul(15); // 15 seconds per block } - /** * @notice Resets the baseRate and multiplier per block based on the stability fee and Dai savings rate */ function poke() public { (uint duty, ) = jug.ilks("ETH-A"); - uint stabilityFee = duty.add(jug.base()).sub(1e27).mul(1e18).div(1e27).mul(15); + uint stabilityFeePerBlock = duty.add(jug.base()).sub(1e27).mul(1e18).div(1e27).mul(15); + + // We ensure the minimum borrow rate >= DSR / (1 - reserve factor) + baseRatePerBlock = dsrPerBlock().mul(1e18).div(assumedOneMinusReserveFactorMantissa); - baseRatePerBlock = dsrPerBlock().mul(1e18).div(0.9e18); // ensure borrow rate is higher than savings rate - multiplierPerBlock = stabilityFee.sub(baseRatePerBlock).mul(1e18).div(kink); + // The roof borrow rate is max(base rate, stability fee) + gap, from which we derive the slope + if (baseRatePerBlock < stabilityFeePerBlock) { + multiplierPerBlock = stabilityFeePerBlock.sub(baseRatePerBlock).add(gapPerBlock).mul(1e18).div(kink); + } else { + multiplierPerBlock = gapPerBlock.mul(1e18).div(kink); + } - emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, kink, jump); + emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); } } diff --git a/contracts/JumpRateModel.sol b/contracts/JumpRateModel.sol index 2048d1682..a1ab210b7 100644 --- a/contracts/JumpRateModel.sol +++ b/contracts/JumpRateModel.sol @@ -10,7 +10,7 @@ import "./SafeMath.sol"; contract JumpRateModel is InterestRateModel { using SafeMath for uint; - event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint kink, uint jump); + event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink); /** * @notice Indicator that this is an InterestRateModel contract (for inspection) @@ -33,29 +33,29 @@ contract JumpRateModel is InterestRateModel { uint public baseRatePerBlock; /** - * @notice the utilization point at which an additional multiplier is applied - */ - uint public kink; + * @notice The multiplierPerBlock after hitting a specified utilization point + */ + uint public jumpMultiplierPerBlock; /** - * @notice the additional multiplier to be applied to multiplierPerBlock after hitting a specified utilization point - */ - uint public jump; + * @notice The utilization point at which the jump multiplier is applied + */ + uint public kink; /** * @notice Construct an interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) - * @param kink_ The utilization point at which an additional multiplier is applied - * @param jump_ The additional multiplier to be applied to multiplierPerBlock after hitting a specified utilization point + * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point + * @param kink_ The utilization point at which the jump multiplier is applied */ - constructor(uint baseRatePerYear, uint multiplierPerYear, uint kink_, uint jump_) public { + constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) public { baseRatePerBlock = baseRatePerYear.div(blocksPerYear); multiplierPerBlock = multiplierPerYear.div(blocksPerYear); + jumpMultiplierPerBlock = jumpMultiplierPerYear.div(blocksPerYear); kink = kink_; - jump = jump_; - emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, kink, jump); + emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); } /** @@ -89,8 +89,7 @@ contract JumpRateModel is InterestRateModel { } else { uint normalRate = kink.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); uint excessUtil = util.sub(kink); - uint jumpMultiplier = multiplierPerBlock.mul(jump); - return excessUtil.mul(jumpMultiplier).div(1e18).add(normalRate); + return excessUtil.mul(jumpMultiplierPerBlock).div(1e18).add(normalRate); } } diff --git a/spec/scenario/MCDai.scen b/spec/scenario/MCDai.scen index 48a801c07..7387c149b 100644 --- a/spec/scenario/MCDai.scen +++ b/spec/scenario/MCDai.scen @@ -101,7 +101,6 @@ Test "Basic mint and redeem cDAI2 (upgrade to swept DSR)" UpgradeToDSR CheckBasicMintRedeemWithDSR - Test "Basic borrow and repay cDAI2 (upgrade to swept DSR)" DeployCDAI CheckBasicMintRedeem diff --git a/test/Models/DAIInterestRateModelTest.js b/test/Models/DAIInterestRateModelTest.js index a5166033f..0c8f90642 100644 --- a/test/Models/DAIInterestRateModelTest.js +++ b/test/Models/DAIInterestRateModelTest.js @@ -5,33 +5,41 @@ const BigNum = require('bignumber.js') const { call, etherUnsigned, getContract, getContractDefaults, getTestContract } = require('../Utils/MochaTruffle'); const { getBorrowRate, getSupplyRate } = require('../Utils/Compound'); +const blocksPerYear = 2102400; const secondsPerYear = 60 * 60 * 24 * 365; function utilizationRate(cash, borrows, reserves) { return borrows ? borrows / (cash + borrows - reserves) : 0; } -function baseRoofRateFn(dsr, duty, mkrBase, kink, jump, cash, borrows, reserves) { - const stabilityFee = (duty + mkrBase - 1) * 15; +function baseRoofRateFn(dsr, duty, mkrBase, jump, kink, cash, borrows, reserves) { + const assumedOneMinusReserveFactor = 0.95; + const stabilityFeePerBlock = (duty + mkrBase - 1) * 15; const dsrPerBlock = (dsr - 1) * 15; - const base = dsrPerBlock / 0.9; - const slope = (stabilityFee - base) / kink; + const gapPerBlock = 0.0005 / blocksPerYear; + const jumpPerBlock = jump / blocksPerYear; + + let baseRatePerBlock = dsrPerBlock / assumedOneMinusReserveFactor, multiplierPerBlock; + if (baseRatePerBlock < stabilityFeePerBlock) { + multiplierPerBlock = (stabilityFeePerBlock - baseRatePerBlock + gapPerBlock) / kink; + } else { + multiplierPerBlock = gapPerBlock / kink; + } const ur = utilizationRate(cash, borrows, reserves); if (ur <= kink) { - return ur * slope + base; + return ur * multiplierPerBlock + baseRatePerBlock; } else { const excessUtil = ur - kink; - const jumpMultiplier = jump * slope; - return (excessUtil * jumpMultiplier) + (kink * slope) + base; + return (excessUtil * jumpPerBlock) + (kink * multiplierPerBlock) + baseRatePerBlock; } } -function daiSupplyRate(dsr, duty, mkrBase, kink, jump, cash, borrows, reserves, reserveFactor = 0.1) { +function daiSupplyRate(dsr, duty, mkrBase, jump, kink, cash, borrows, reserves, reserveFactor = 0.1) { const dsrPerBlock = (dsr - 1) * 15; const ur = utilizationRate(cash, borrows, reserves); - const borrowRate = baseRoofRateFn(dsr, duty, mkrBase, kink, jump, cash, borrows, reserves); + const borrowRate = baseRoofRateFn(dsr, duty, mkrBase, jump, kink, cash, borrows, reserves); const underlying = cash + borrows - reserves; const lendingSupplyRate = borrowRate * (1 - reserveFactor) * ur; @@ -66,10 +74,10 @@ contract('DAIInterestRateModel', async function (_accounts) { let model = await contract.deploy({ arguments: [ - "0xea190dbdc7adf265260ec4da6e9675fd4f5a78bb", - "0xcbb7718c9f39d05aeede1c472ca8bf804b2f1ead", + etherUnsigned(0.8e18), etherUnsigned(0.9e18), - etherUnsigned(5) + "0xea190dbdc7adf265260ec4da6e9675fd4f5a78bb", + "0xcbb7718c9f39d05aeede1c472ca8bf804b2f1ead" ] }) .send({ from: root }); @@ -129,8 +137,8 @@ contract('DAIInterestRateModel', async function (_accounts) { [0e27, 0.1e27, 0.005e27, 3e18, 500], ].map(vs => vs.map(Number)) - .forEach(([dsr, duty, base, cash, borrows, reserves = 0, kink = 0.9e18, jump = 5]) => { - it(`calculates correct borrow value for dsr=${(dsr / 1e25)}%, duty=${(duty / 1e25)}%, base=${(base / 1e25)}%, cash=${cash}, borrows=${borrows}, reserves=${reserves}`, async () => { + .forEach(([dsr, duty, base, cash, borrows, reserves = 0, jump = 0.8e18, kink = 0.9e18]) => { + it(`calculates correct borrow value for dsr=${(dsr / 1e25)}%, duty=${(duty / 1e25)}%, base=${(base / 1e25)}%, jump=${jump / 1e18}, cash=${cash}, borrows=${borrows}, reserves=${reserves}`, async () => { const [root] = _accounts; const onePlusPerSecondDsr = 1e27 + (dsr / secondsPerYear); @@ -156,14 +164,14 @@ contract('DAIInterestRateModel', async function (_accounts) { const daiIRM = await DAIInterestRateModel.deploy({ arguments: [ - pot.options.address, - jug.options.address, + etherUnsigned(jump), etherUnsigned(kink), - etherUnsigned(jump) + pot.options.address, + jug.options.address ] }).send({ from: root }); - const expected = baseRoofRateFn(onePlusPerSecondDsr / 1e27, onePlusPerSecondDuty / 1e27, perSecondBase / 1e27, kink / 1e18, jump, cash, borrows, reserves); + const expected = baseRoofRateFn(onePlusPerSecondDsr / 1e27, onePlusPerSecondDuty / 1e27, perSecondBase / 1e27, jump / 1e18, kink / 1e18, cash, borrows, reserves); assert.like( await getBorrowRate(daiIRM, cash, borrows, reserves), (x) => assert.approximately(Number(x) / 1e18, expected, 1e-8) @@ -221,7 +229,7 @@ contract('DAIInterestRateModel', async function (_accounts) { [0e27, 0.1e27, 0.005e27, 3e18, 500], ].map(vs => vs.map(Number)) - .forEach(([dsr, duty, base, cash, borrows, reserves = 0, kink = 0.9e18, jump = 5, reserveFactor = 0.1e18]) => { + .forEach(([dsr, duty, base, cash, borrows, reserves = 0, jump = 0.8e18, kink = 0.9e18, reserveFactor = 0.1e18]) => { it(`calculates correct supply value for dsr=${(dsr / 1e25)}%, duty=${(duty / 1e25)}%, base=${(base / 1e25)}%, cash=${cash}, borrows=${borrows}, reserves=${reserves}`, async () => { const [root] = _accounts; @@ -248,14 +256,14 @@ contract('DAIInterestRateModel', async function (_accounts) { const daiIRM = await DAIInterestRateModel.deploy({ arguments: [ - pot.options.address, - jug.options.address, + etherUnsigned(jump), etherUnsigned(kink), - etherUnsigned(jump) + pot.options.address, + jug.options.address ] }).send({ from: root }); - const expected = daiSupplyRate(onePlusPerSecondDsr / 1e27, onePlusPerSecondDuty / 1e27, perSecondBase / 1e27, kink / 1e18, jump, cash, borrows, reserves, reserveFactor / 1e18); + const expected = daiSupplyRate(onePlusPerSecondDsr / 1e27, onePlusPerSecondDuty / 1e27, perSecondBase / 1e27, jump / 1e18, kink / 1e18, cash, borrows, reserves, reserveFactor / 1e18); assert.like( await getSupplyRate(daiIRM, cash, borrows, reserves, reserveFactor), (x) => assert.approximately(Number(x) / 1e18, expected, 1e-8) diff --git a/test/Models/interestRateModelTest.js b/test/Models/interestRateModelTest.js index e42a2f183..bb54344d7 100644 --- a/test/Models/interestRateModelTest.js +++ b/test/Models/interestRateModelTest.js @@ -10,7 +10,7 @@ function utilizationRate(cash, borrows, reserves) { return borrows ? borrows / (cash + borrows - reserves) : 0; } -function whitePaperRateFn(base, slope, kink = 0.9, jump = 5) { +function whitePaperRateFn(base, slope, jump = 0.8, kink = 0.9) { return (cash, borrows, reserves) => { const ur = utilizationRate(cash, borrows, reserves); @@ -18,15 +18,14 @@ function whitePaperRateFn(base, slope, kink = 0.9, jump = 5) { return (ur * slope + base) / blocksPerYear; } else { const excessUtil = ur - kink; - const jumpMultiplier = jump * slope; - return ((excessUtil * jumpMultiplier) + (kink * slope) + base) / blocksPerYear; + return ((excessUtil * jump) + (kink * slope) + base) / blocksPerYear; } } } -function supplyRateFn(base, slope, kink, jump, cash, borrows, reserves, reserveFactor = 0.1) { +function supplyRateFn(base, slope, jump, kink, cash, borrows, reserves, reserveFactor = 0.1) { const ur = utilizationRate(cash, borrows, reserves); - const borrowRate = whitePaperRateFn(base, slope, kink, jump)(cash, borrows, reserves); + const borrowRate = whitePaperRateFn(base, slope, jump, kink)(cash, borrows, reserves); return borrowRate * (1 - reserveFactor) * ur; } @@ -116,25 +115,112 @@ contract('InterestRateModel', async function ([root, ...accounts]) { }); it('handles overflow utilization rate times slope', async () => { - const badModel = await makeInterestRateModel({ kind, baseRate: 0, multiplier: -1 }); + const badModel = await makeInterestRateModel({ kind, baseRate: 0, multiplier: -1, jump: -1 }); await assert.revert(getBorrowRate(badModel, 1, 1, 0), "revert SafeMath: multiplication overflow"); }); it('handles overflow utilization rate times slope + base', async () => { - const badModel = await makeInterestRateModel({ kind, baseRate: -1, multiplier: 1e48 }); + const badModel = await makeInterestRateModel({ kind, baseRate: -1, multiplier: 1e48, jump: 1e48 }); await assert.revert(getBorrowRate(badModel, 0, 1, 0), "revert SafeMath: multiplication overflow"); }); + describe('chosen points', () => { + const tests = [ + { + jump: 100, + kink: 90, + base: 10, + slope: 20, + points: [ + [0, 10], + [10, 12], + [89, 27.8], + [90, 28], + [91, 29], + [100, 38] + ] + }, + { + jump: 20, + kink: 90, + base: 10, + slope: 20, + points: [ + [0, 10], + [10, 12], + [100, 30] + ] + }, + { + jump: 0, + kink: 90, + base: 10, + slope: 20, + points: [ + [0, 10], + [10, 12], + [100, 28] + ] + }, + { + jump: 0, + kink: 110, + base: 10, + slope: 20, + points: [ + [0, 10], + [10, 12], + [100, 30] + ] + }, + { + jump: 2000, + kink: 0, + base: 10, + slope: 20, + points: [ + [0, 10], + [10, 210], + [100, 2010] + ] + } + ].forEach(({jump, kink, base, slope, points}) => { + describe(`for jump=${jump}, kink=${kink}, base=${base}, slope=${slope}`, async () => { + let jumpModel; + + before(async () => { + jumpModel = await makeInterestRateModel({ + kind: 'jump-rate', + baseRate: base / 100, + multiplier: slope / 100, + jump: jump / 100, + kink: kink / 100, + }); + }); + + points.forEach(([util, expected]) => { + it(`and util=${util}%`, async () => { + const {borrows, cash, reserves} = makeUtilization(util * 1e16); + const result = await getBorrowRate(jumpModel, cash, borrows, reserves); + const actual = Number(result) / 1e16 * blocksPerYear; + + assert.approximately(actual, expected, 1e-2); + }); + }); + }); + }); + }); + describe('ranges', () => { const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e)))); const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a); let jumps = [ 0, - 2, - 3, - 10, - 1000 + 0.02e18, + 0.03e18, + 0.10e18, + 10.0e18 ]; let kinks = [ @@ -165,18 +251,18 @@ contract('InterestRateModel', async function ([root, ...accounts]) { utils.forEach(async (util) => { it(`has correct curve for kink=${kink/1e16}%, util=${util/1e16}%`, async () => { let {borrows, cash, reserves} = makeUtilization(util); - + let calculated = borrows / (cash + borrows - reserves); const altModel = await makeInterestRateModel({ kind: 'jump-rate', baseRate: base / 1e18, multiplier: slope / 1e18, - kink: kink, - jump: jump + jump: jump / 1e18, + kink: kink }); - const expected = whitePaperRateFn(base / 1e18, slope / 1e18, kink / 1e18, jump)(cash, borrows, reserves); + const expected = whitePaperRateFn(base / 1e18, slope / 1e18, jump / 1e18, kink / 1e18)(cash, borrows, reserves); const result = await getBorrowRate(altModel, cash, borrows, reserves); assert.like( @@ -226,10 +312,10 @@ contract('InterestRateModel', async function ([root, ...accounts]) { [20.0e18, 40.0e18, 0, 0], [20.0e18, 40.0e18, 3e18, 500], ].map(vs => vs.map(Number)) - .forEach(([base, slope, cash, borrows, reserves = 0, kink = 0.9e18, jump = 5]) => { // XXX add reserves + .forEach(([base, slope, cash, borrows, reserves = 0, jump = 0.8e18, kink = 0.9e18]) => { // XXX add reserves it(`calculates correct borrow value for base=${base / 1e16}%,slope=${slope / 1e16}%, cash=${cash}, borrows=${borrows}`, async () => { - const altModel = await makeInterestRateModel({ kind: 'jump-rate', baseRate: base / 1e18, multiplier: slope / 1e18, kink: kink / 1e18, jump: jump }); - const expected = whitePaperRateFn(base / 1e18, slope / 1e18, kink / 1e18, jump)(cash, borrows, reserves); + const altModel = await makeInterestRateModel({kind: 'jump-rate', baseRate: base / 1e18, multiplier: slope / 1e18, jump: jump / 1e18, kink: kink / 1e18}); + const expected = whitePaperRateFn(base / 1e18, slope / 1e18, jump / 1e18, kink / 1e18)(cash, borrows, reserves); assert.like( await getBorrowRate(altModel, cash, borrows, reserves), (x) => assert.approximately(Number(x) / 1e18, expected, 1e-8) @@ -276,10 +362,10 @@ contract('InterestRateModel', async function ([root, ...accounts]) { [20.0e18, 40.0e18, 0, 0], [20.0e18, 40.0e18, 3e18, 500], ].map(vs => vs.map(Number)) - .forEach(([base, slope, cash, borrows, reserves = 0, kink = 0.9e18, jump = 5, reserveFactor = 0.1e18]) => { // XXX add reserves - it(`calculates correct supply value for base=${base / 1e16}%,slope=${slope / 1e16}%, cash=${cash}, borrows=${borrows}`, async () => { - const altModel = await makeInterestRateModel({ kind: 'jump-rate', baseRate: base / 1e18, multiplier: slope / 1e18, kink: kink / 1e18, jump: jump }); - const expected = supplyRateFn(base / 1e18, slope / 1e18, kink / 1e18, jump, cash, borrows, reserves, reserveFactor / 1e18); + .forEach(([base, slope, cash, borrows, reserves = 0, jump = slope * 5, kink = 0.9e18, reserveFactor = 0.1e18]) => { // XXX add reserves + it(`calculates correct supply value for base=${base / 1e16}%, slope=${slope / 1e16}%, jump=${jump / 1e16}, cash=${cash}, borrows=${borrows}`, async () => { + const altModel = await makeInterestRateModel({kind: 'jump-rate', baseRate: base / 1e18, multiplier: slope / 1e18, jump: jump / 1e18, kink: kink / 1e18}); + const expected = supplyRateFn(base / 1e18, slope / 1e18, jump / 1e18, kink / 1e18, cash, borrows, reserves, reserveFactor / 1e18); assert.like( await getSupplyRate(altModel, cash, borrows, reserves, reserveFactor), (x) => assert.approximately(Number(x) / 1e18, expected, 1e-8) diff --git a/test/Tokens/cTokenTest.js b/test/Tokens/cTokenTest.js index 4c2f37832..f49f8d81f 100644 --- a/test/Tokens/cTokenTest.js +++ b/test/Tokens/cTokenTest.js @@ -79,14 +79,13 @@ contract('CToken', function ([root, admin, ...accounts]) { const baseRate = 0.05; const multiplier = 0.45; const kink = 0.95; - const jump = 5; + const jump = 5 * multiplier; const cToken = await makeCToken({ supportMarket: true, interestRateModelOpts: { kind: 'jump-rate', baseRate, multiplier, kink, jump } }); await send(cToken, 'harnessSetReserveFactorFresh', [etherMantissa(.01)]); await send(cToken, 'harnessExchangeRateDetails', [1, 1, 0]); await send(cToken, 'harnessSetExchangeRate', [etherMantissa(1)]); // Full utilization (Over the kink so jump is included), 1% reserves - const additionalJump = multiplier * jump * .05; - const borrowRate = (kink * multiplier) + baseRate + additionalJump; + const borrowRate = baseRate + multiplier * kink + jump * .05; const expectedSuplyRate = borrowRate * .99; const perBlock = await call(cToken, 'supplyRatePerBlock'); diff --git a/test/Utils/Compound.js b/test/Utils/Compound.js index fd6cc1580..e3f56cccb 100644 --- a/test/Utils/Compound.js +++ b/test/Utils/Compound.js @@ -173,9 +173,9 @@ async function makeInterestRateModel(opts = {}) { const InterestRateModel = getTestContract('JumpRateModel'); const baseRate = etherMantissa(dfn(opts.baseRate, 0)); const multiplier = etherMantissa(dfn(opts.multiplier, 1e-18)); - const kink = etherMantissa(dfn(opts.kink, 0.95e18)); - const jump = etherUnsigned(dfn(opts.jump, 5)); - const interestRateModel = await InterestRateModel.deploy({ arguments: [baseRate, multiplier, kink, jump] }).send({ from: root }); + const jump = etherMantissa(dfn(opts.jump, 0)); + const kink = etherMantissa(dfn(opts.kink, 0)); + const interestRateModel = await InterestRateModel.deploy({ arguments: [baseRate, multiplier, jump, kink] }).send({ from: root }); return interestRateModel; } }