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

IRM refactor #54

Merged
merged 122 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
2675073
feat: refactor curve abstraction
MathisGD Oct 27, 2023
1fdf81a
Merge remote-tracking branch 'origin/main' into refactor/curve
MathisGD Oct 27, 2023
33ea93d
test: fix some tests
MathisGD Oct 28, 2023
78a4144
chore: fmt
MathisGD Oct 28, 2023
a46068f
test: passing
MathisGD Oct 28, 2023
6b78b21
chore: fmt
MathisGD Oct 28, 2023
6cc6a4c
test: improve testing
MathisGD Oct 29, 2023
6dff847
feat: bound base rate
MathisGD Oct 29, 2023
10799a8
feat: remove the exponential aspect of the curve
MathisGD Oct 30, 2023
a437d35
style: new wording
MathisGD Oct 30, 2023
95a9ef0
style: contract name
MathisGD Oct 30, 2023
180a426
chore: fmt
MathisGD Oct 30, 2023
190834c
chore: update submodules
MathisGD Oct 30, 2023
2747975
chore: revert file name change
MathisGD Oct 30, 2023
e061946
chore: revert file name change
MathisGD Oct 30, 2023
d083e58
refactor(irm): compile without IR
Rubilmax Oct 31, 2023
c68cb45
fix: bound base rate
MathisGD Oct 31, 2023
89b0133
Merge branch 'refactor/curve' into refactor/build-profile
MathisGD Oct 31, 2023
a0f0539
Merge pull request #57 from morpho-labs/refactor/build-profile
MathisGD Oct 31, 2023
60e588a
fix: check morpho zero address
MathisGD Oct 31, 2023
89a1fdc
test: improve testing
MathisGD Oct 31, 2023
8eb5285
style: base rate naming
MathisGD Oct 31, 2023
143c55c
chore: fmt
MathisGD Oct 31, 2023
9de0936
fix: minor issues
MathisGD Oct 31, 2023
6e99116
fix: minor improvements
MathisGD Nov 2, 2023
aaaa332
fix: various fixes
MathisGD Nov 4, 2023
4ee7cb9
refactor: separate first interaction case
MathisGD Nov 4, 2023
76ca930
docs: add comment on constant speed
MathisGD Nov 4, 2023
c123cfd
chore: space
MathisGD Nov 4, 2023
c3154c9
refactor(math): revert to int256
Rubilmax Nov 6, 2023
fff19be
refactor(irm): expect uint256 inputs
Rubilmax Nov 6, 2023
80e075e
chore: space
MathisGD Nov 4, 2023
1eff87c
docs: fix incorrect comment
MerlinEgalite Nov 8, 2023
50a2612
fix: return a capped value instead of overflowing
MerlinEgalite Nov 9, 2023
69fba05
docs: correct comment for tests as well
MerlinEgalite Nov 9, 2023
70f15f7
style: format
MerlinEgalite Nov 9, 2023
a2a4042
refactor: apply naming suggestion
MerlinEgalite Nov 9, 2023
54b23a5
refactor: use steeringCoeff
MathisGD Nov 9, 2023
469fd90
Merge remote-tracking branch 'origin/refactor/curve' into refactor/curve
MathisGD Nov 9, 2023
15b0f33
chore: fmt
MathisGD Nov 9, 2023
3419458
docs: comment adjustment speed non negative
MathisGD Nov 9, 2023
926cc45
Merge branch 'refactor/curve' into refactor/w-mul-down
MathisGD Nov 9, 2023
bcc4cf1
chore: fmt
MathisGD Nov 9, 2023
9614561
Merge pull request #60 from morpho-labs/refactor/w-mul-down
MathisGD Nov 9, 2023
c82fc3d
docs: improve comment return zero
MathisGD Nov 9, 2023
bd0b5cd
Merge pull request #61 from morpho-labs/fix/incorrect-comment-36
MathisGD Nov 9, 2023
222b04e
docs: minor improvements
MathisGD Nov 10, 2023
939d3d0
fix(wexp): prevent reverts
Rubilmax Nov 10, 2023
7f09d96
Merge branch 'refactor/curve' of github.com:morpho-labs/morpho-blue-p…
Rubilmax Nov 10, 2023
3c23f2a
test(foundry): fix invariant tests
Rubilmax Nov 10, 2023
3129b2b
refactor: use only int256
MathisGD Nov 10, 2023
ecad414
fix: minor improvements
MathisGD Nov 10, 2023
e26a72a
docs: non negative result
MathisGD Nov 11, 2023
370b43e
docs: avgBorrowRate non zero
MathisGD Nov 11, 2023
de8c78f
docs: document block.timestamp safe unchecked
MathisGD Nov 11, 2023
948db52
fix(wExp): update values
Rubilmax Nov 11, 2023
6717428
refactor: store int
MathisGD Nov 11, 2023
db9541b
docs: minor improvements
MathisGD Nov 11, 2023
5738df9
fix(wexp): raise upper bound to ln(max / 1e36)
Rubilmax Nov 12, 2023
ead8b28
Merge branch 'fix/exp-overflow-35' of github.com:morpho-labs/morpho-b…
Rubilmax Nov 13, 2023
f9a1bf7
test(irm): revert invariants
Rubilmax Nov 13, 2023
dee15a6
chore: update blue
MathisGD Nov 13, 2023
b209956
Merge remote-tracking branch 'origin/main' into refactor/curve
MathisGD Nov 13, 2023
8b66f45
refactor: curve function
MathisGD Nov 13, 2023
2e9953b
test: improve testing
MathisGD Nov 13, 2023
ea7a95e
Merge pull request #75 from morpho-labs/refactor/curve-function
MathisGD Nov 13, 2023
145ff39
Merge pull request #74 from morpho-labs/chore/update-blue
MathisGD Nov 13, 2023
af82fe2
docs: minor improvement
MathisGD Nov 13, 2023
98c0d61
docs: minor improvements
MathisGD Nov 13, 2023
b14c054
Merge pull request #68 from morpho-labs/test/invariant-no-revert
MathisGD Nov 13, 2023
6733f35
Merge branch 'refactor/curve' into refactor/int
MathisGD Nov 13, 2023
40f6ac0
Merge remote-tracking branch 'origin/refactor/curve' into refactor/int
MathisGD Nov 13, 2023
03b9684
Merge remote-tracking branch 'origin/refactor/curve' into fix/exp-ove…
MathisGD Nov 13, 2023
3d5e144
chore: fmt
MathisGD Nov 13, 2023
50ce806
test: add more tests
MathisGD Nov 14, 2023
9f80268
chore: update libs
MathisGD Nov 14, 2023
6ebacf0
Merge pull request #62 from morpho-labs/fix/exp-overflow-35
MathisGD Nov 14, 2023
447a232
Merge branch 'refactor/curve' into refactor/int
MathisGD Nov 14, 2023
a345815
fix: upper value
MathisGD Nov 14, 2023
62937d2
test: add exp tests
MathisGD Nov 14, 2023
006384a
docs: minor improvements
MathisGD Nov 14, 2023
8483911
Merge pull request #69 from morpho-labs/refactor/int
MathisGD Nov 14, 2023
40bf4a7
test: add APY
MathisGD Nov 14, 2023
d4219ec
Merge remote-tracking branch 'origin/test/refactor-curve' into test/r…
MathisGD Nov 14, 2023
d239efe
Merge remote-tracking branch 'origin/refactor/curve' into test/refact…
MathisGD Nov 14, 2023
a1826c9
test: minor fix
MathisGD Nov 14, 2023
77ed9c1
Merge pull request #76 from morpho-labs/test/refactor-curve
MathisGD Nov 14, 2023
1c3bedf
feat: riemann avg
MathisGD Nov 15, 2023
6e49f0e
style: minor improvements
MathisGD Nov 15, 2023
8163f2a
Merge branch 'main' into chore/merge-main
MathisGD Nov 15, 2023
d68db06
Merge pull request #84 from morpho-org/chore/merge-main
MathisGD Nov 15, 2023
fd24cc2
Merge remote-tracking branch 'origin/refactor/curve' into feat/rieman…
MathisGD Nov 15, 2023
3ea4939
docs: document riemann avg
MathisGD Nov 15, 2023
268a4f4
perf: factorize div by N
MathisGD Nov 15, 2023
2ab1a43
docs: document N_STEPS
MathisGD Nov 15, 2023
770a6fc
docs: document riemann
MathisGD Nov 16, 2023
16124b8
style: renamings
MathisGD Nov 16, 2023
5689b78
chore: fmt
MathisGD Nov 16, 2023
f1c618f
perf: use endRateAtTarget in riemann
MathisGD Nov 16, 2023
a6099fc
docs: minor improvements
MathisGD Nov 16, 2023
e0167ec
feat: left/right riemann
MathisGD Nov 16, 2023
91039d9
style: harmonize naming
MathisGD Nov 16, 2023
09c05e3
docs: remove we
MathisGD Nov 16, 2023
6682ad0
docs: minor fix
MathisGD Nov 16, 2023
500a957
feat: return early
MathisGD Nov 16, 2023
da8062f
chore: rename contract
MathisGD Nov 16, 2023
8fbaffc
docs: minor fix
MathisGD Nov 16, 2023
af5e27e
Merge branch 'feat/riemann-avg' into feat/return-early-linearAdaptati…
MathisGD Nov 16, 2023
1387436
docs: format doc
MathisGD Nov 16, 2023
0974005
Merge pull request #88 from morpho-org/feat/return-early-linearAdapta…
MathisGD Nov 16, 2023
52b9307
refactor: riemann
MathisGD Nov 16, 2023
9dac5b9
feat: trapezoidal riemann (n=2)
MathisGD Nov 16, 2023
8b81fdc
Merge pull request #89 from morpho-org/refactor/proposal-1
MathisGD Nov 16, 2023
c1633da
docs
MathisGD Nov 16, 2023
f75a09e
chore: fmt
MathisGD Nov 16, 2023
0531b5c
Merge branch 'feat/riemann-avg' into feat/trapezoidal
MathisGD Nov 16, 2023
e46719d
docs: more concise comments for trapeze N=2
QGarchery Nov 16, 2023
7667991
fix: missing bracket
QGarchery Nov 16, 2023
97794c6
Merge pull request #95 from morpho-org/docs/trapezoidal
MathisGD Nov 16, 2023
089da66
Merge pull request #91 from morpho-org/feat/trapezoidal
MerlinEgalite Nov 16, 2023
f0ebd8b
test: larger approx
MathisGD Nov 16, 2023
4637e31
Merge pull request #82 from morpho-org/feat/riemann-avg
MerlinEgalite Nov 16, 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
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ src = "src"
out = "out"
test = "test"
libs = ["lib"]
via-ir = true
MathisGD marked this conversation as resolved.
Show resolved Hide resolved

[profile.default.fuzz]
runs = 4096
Expand Down
65 changes: 26 additions & 39 deletions src/SpeedJumpIrm.sol
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
Rubilmax marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.
import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {WAD, MathLib as MorphoMathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol";

struct MarketIrm {
// Previous final borrow rate. Scaled by WAD.
uint128 prevBorrowRate;
// Previous error. Scaled by WAD.
int128 prevErr;
}
import "forge-std/console.sol";
MathisGD marked this conversation as resolved.
Show resolved Hide resolved

/// @title SpeedJumpIrm
/// @author Morpho Labs
Expand All @@ -32,7 +27,7 @@ contract SpeedJumpIrm is IIrm {
/* EVENTS */

/// @notice Emitted when a borrow rate is updated.
event BorrowRateUpdate(Id indexed id, int128 err, uint128 newBorrowRate, uint256 avgBorrowRate);
event BorrowRateUpdate(Id indexed id, uint256 avgBorrowRate, uint256 baseRate);

/* CONSTANTS */

Expand All @@ -51,12 +46,12 @@ contract SpeedJumpIrm is IIrm {
/// @notice Target utilization (scaled by WAD).
uint256 public immutable TARGET_UTILIZATION;
/// @notice Initial rate (scaled by WAD).
uint128 public immutable INITIAL_RATE;
uint256 public immutable INITIAL_BASE_RATE;

/* STORAGE */

/// @notice IRM storage for each market.
mapping(Id => MarketIrm) public marketIrm;
/// @notice Base rate of markets.
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
mapping(Id => uint256) public baseRate;

/* CONSTRUCTOR */

Expand All @@ -65,13 +60,13 @@ contract SpeedJumpIrm is IIrm {
/// @param lnJumpFactor The log of the jump factor (scaled by WAD).
/// @param speedFactor The speed factor (scaled by WAD).
/// @param targetUtilization The target utilization (scaled by WAD). Should be strictly between 0 and 1.
/// @param initialRate The initial rate (scaled by WAD).
/// @param initialBaseRate The initial rate (scaled by WAD).
constructor(
address morpho,
uint256 lnJumpFactor,
uint256 speedFactor,
uint256 targetUtilization,
uint128 initialRate
uint256 initialBaseRate
) {
require(lnJumpFactor <= uint256(type(int256).max), ErrorsLib.INPUT_TOO_LARGE);
require(speedFactor <= uint256(type(int256).max), ErrorsLib.INPUT_TOO_LARGE);
Expand All @@ -82,14 +77,14 @@ contract SpeedJumpIrm is IIrm {
LN_JUMP_FACTOR = lnJumpFactor;
SPEED_FACTOR = speedFactor;
TARGET_UTILIZATION = targetUtilization;
INITIAL_RATE = initialRate;
INITIAL_BASE_RATE = initialBaseRate;
}

/* BORROW RATES */

/// @inheritdoc IIrm
function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256) {
(,, uint256 avgBorrowRate) = _borrowRate(marketParams.id(), market);
(uint256 avgBorrowRate,) = _borrowRate(marketParams.id(), market);
return avgBorrowRate;
}

Expand All @@ -99,57 +94,49 @@ contract SpeedJumpIrm is IIrm {

Id id = marketParams.id();

(int128 err, uint128 newBorrowRate, uint256 avgBorrowRate) = _borrowRate(id, market);
(uint256 avgBorrowRate, uint256 newBaseRate) = _borrowRate(id, market);

marketIrm[id].prevErr = err;
marketIrm[id].prevBorrowRate = newBorrowRate;
baseRate[id] = newBaseRate;

emit BorrowRateUpdate(id, err, newBorrowRate, avgBorrowRate);
emit BorrowRateUpdate(id, avgBorrowRate, newBaseRate);

return avgBorrowRate;
}

/// @dev Returns err, newBorrowRate and avgBorrowRate.
function _borrowRate(Id id, Market memory market) private view returns (int128, uint128, uint128) {
function _borrowRate(Id id, Market memory market) private view returns (uint256, uint256) {
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
uint256 utilization =
market.totalSupplyAssets > 0 ? market.totalBorrowAssets.wDivDown(market.totalSupplyAssets) : 0;

uint256 errNormFactor = utilization > TARGET_UTILIZATION ? WAD - TARGET_UTILIZATION : TARGET_UTILIZATION;
// Safe "unchecked" int128 cast because |err| <= WAD.
// Safe "unchecked" int256 casts because utilization <= WAD, TARGET_UTILIZATION < WAD and errNormFactor <= WAD.
int128 err = int128((int256(utilization) - int256(TARGET_UTILIZATION)).wDivDown(int256(errNormFactor)));

MarketIrm storage irm = marketIrm[id];
if (irm.prevBorrowRate == 0) return (err, INITIAL_RATE, INITIAL_RATE);
int256 err = (int256(utilization) - int256(TARGET_UTILIZATION)).wDivDown(int256(errNormFactor));

// errDelta = err - prevErr.
// errDelta is between -1 and 1, scaled by WAD.
int256 errDelta = err - irm.prevErr;

// Safe "unchecked" cast because LN_JUMP_FACTOR <= type(int256).max.
uint256 jumpMultiplier = MathLib.wExp(errDelta.wMulDown(int256(LN_JUMP_FACTOR)));
// Safe "unchecked" cast because SPEED_FACTOR <= type(int256).max.
int256 speed = int256(SPEED_FACTOR).wMulDown(err);
uint256 elapsed = block.timestamp - market.lastUpdate;
uint256 elapsed = (baseRate[id] > 0) ? block.timestamp - market.lastUpdate : 0;
// Safe "unchecked" cast because elapsed <= block.timestamp.
int256 linearVariation = speed * int256(elapsed);
uint256 variationMultiplier = MathLib.wExp(linearVariation);

// newBorrowRate = prevBorrowRate * jumpMultiplier * variationMultiplier.
uint256 borrowRateAfterJump = irm.prevBorrowRate.wMulDown(jumpMultiplier);
uint256 newBorrowRate = borrowRateAfterJump.wMulDown(variationMultiplier);
uint256 newBaseRate = (baseRate[id] > 0) ? baseRate[id].wMulDown(variationMultiplier) : INITIAL_BASE_RATE;
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
uint256 newBorrowRate = newBaseRate.wMulDown(MathLib.wExp(err));

// Then we compute the average rate over the period (this is what Morpho needs to accrue the interest).
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
// avgBorrowRate = 1 / elapsed * ∫ borrowRateAfterJump * exp(speed * t) dt between 0 and elapsed
// = borrowRateAfterJump * (exp(linearVariation) - 1) / linearVariation
// = (newBorrowRate - borrowRateAfterJump) / linearVariation
// And avgBorrowRate ~ borrowRateAfterJump for linearVariation around zero.
uint256 avgBorrowRate;
if (linearVariation == 0) avgBorrowRate = borrowRateAfterJump;
// Safe "unchecked" cast to uint256 because linearVariation < 0 <=> newBorrowRate <= borrowRateAfterJump.
else avgBorrowRate = uint256((int256(newBorrowRate) - int256(borrowRateAfterJump)).wDivDown(linearVariation));
if (linearVariation == 0 || baseRate[id] == 0) {
avgBorrowRate = newBorrowRate;
} else {
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
// Safe "unchecked" cast to uint256 because linearVariation < 0 <=> newBorrowRate <= borrowRateAfterJump.
avgBorrowRate = uint256(
(int256(newBorrowRate) - int256(baseRate[id].wMulDown(MathLib.wExp(err)))).wDivDown(linearVariation)
);
}

// We bound both newBorrowRate and avgBorrowRate between MIN_RATE and MAX_RATE.
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
return (err, uint128(newBorrowRate.bound(MIN_RATE, MAX_RATE)), uint128(avgBorrowRate.bound(MIN_RATE, MAX_RATE)));
return (avgBorrowRate, newBaseRate);
}
}
131 changes: 65 additions & 66 deletions test/SpeedJumpIrmTest.sol
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,49 @@ contract SpeedJumpIrmTest is Test {
using MorphoMathLib for uint256;
using MarketParamsLib for MarketParams;

event BorrowRateUpdate(Id indexed id, int128 err, uint128 newBorrowRate, uint256 avgBorrowRate);
event BorrowRateUpdate(Id indexed id, uint256 avgBorrowRate, uint256 baseBorrowRate);

uint256 internal constant LN2 = 0.69314718056 ether;
uint256 internal constant TARGET_UTILIZATION = 0.8 ether;
uint256 internal constant SPEED_FACTOR = uint256(0.01 ether) / uint256(10 hours);
uint128 internal constant INITIAL_RATE = uint128(0.01 ether) / uint128(365 days);
// rate for utilization=0 and baseRate=initialBaseRate.
uint256 internal constant INITIAL_RATE = uint128(0.01 ether) / uint128(365 days);
uint256 internal constant INITIAL_BASE_RATE = uint256(INITIAL_RATE) * LN2 / 1 ether;

SpeedJumpIrm internal irm;
MarketParams internal marketParams = MarketParams(address(0), address(0), address(0), address(0), 0);

function setUp() public {
irm = new SpeedJumpIrm(address(this), LN2, SPEED_FACTOR, TARGET_UTILIZATION, INITIAL_RATE);
irm = new SpeedJumpIrm(address(this), LN2, SPEED_FACTOR, TARGET_UTILIZATION, INITIAL_BASE_RATE);
vm.warp(90 days);
}

function testFirstBorrowRateEmptyMarket() public {
Market memory market;
uint256 avgBorrowRate = irm.borrowRate(marketParams, market);
uint256 baseRate = irm.baseRate(marketParams.id());

assertEq(avgBorrowRate, INITIAL_BASE_RATE.wMulDown(MathLib.wExp(-1 ether)), "avgBorrowRate");
assertEq(baseRate, INITIAL_BASE_RATE, "baseRate");
}

function testFirstBorrowRate(Market memory market) public {
vm.assume(market.totalBorrowAssets > 0);
vm.assume(market.totalSupplyAssets >= market.totalBorrowAssets);

uint256 avgBorrowRate = irm.borrowRate(marketParams, market);
(uint256 prevBorrowRate, int256 prevErr) = irm.marketIrm(marketParams.id());
uint256 baseRate = irm.baseRate(marketParams.id());

assertEq(avgBorrowRate, INITIAL_RATE, "avgBorrowRate");
assertEq(prevBorrowRate, INITIAL_RATE, "prevBorrowRate");
assertEq(prevErr, _err(market), "prevErr");
assertEq(avgBorrowRate, INITIAL_BASE_RATE.wMulDown(MathLib.wExp(_err(market))), "avgBorrowRate");
assertEq(baseRate, INITIAL_BASE_RATE, "baseRate");
}

function testBorrowRateEventEmission(Market memory market) public {
vm.assume(market.totalBorrowAssets > 0);
vm.assume(market.totalSupplyAssets >= market.totalBorrowAssets);

vm.expectEmit(address(irm));
emit BorrowRateUpdate(marketParams.id(), int128(_err(market)), INITIAL_RATE, INITIAL_RATE);
emit BorrowRateUpdate(marketParams.id(), INITIAL_RATE, INITIAL_RATE);
irm.borrowRate(marketParams, market);
}

Expand All @@ -55,11 +65,10 @@ contract SpeedJumpIrmTest is Test {
vm.assume(market.totalSupplyAssets >= market.totalBorrowAssets);

uint256 avgBorrowRate = irm.borrowRateView(marketParams, market);
(uint256 prevBorrowRate, int256 prevErr) = irm.marketIrm(marketParams.id());
uint256 baseRate = irm.baseRate(marketParams.id());

assertEq(avgBorrowRate, INITIAL_RATE, "avgBorrowRate");
assertEq(prevBorrowRate, 0, "prevBorrowRate");
assertEq(prevErr, 0, "prevErr");
assertEq(avgBorrowRate, INITIAL_BASE_RATE.wMulDown(MathLib.wExp(_err(market))), "avgBorrowRate");
assertEq(baseRate, 0, "prevBorrowRate");
}

function testBorrowRate(Market memory market0, Market memory market1) public {
Expand All @@ -71,13 +80,12 @@ contract SpeedJumpIrmTest is Test {
vm.assume(market1.totalSupplyAssets >= market1.totalBorrowAssets);
market1.lastUpdate = uint128(bound(market1.lastUpdate, 0, block.timestamp - 1));

uint256 avgBorrowRate = irm.borrowRate(marketParams, market1);
(uint256 prevBorrowRate,) = irm.marketIrm(marketParams.id());

(uint256 expectedAvgBorrowRate, uint256 expectedPrevBorrowRate) = _expectedBorrowRates(market0, market1);
uint256 expectedBaseRate = _expectedBaseRate(marketParams.id(), market1);

assertEq(prevBorrowRate, expectedPrevBorrowRate, "prevBorrowRate");
assertEq(avgBorrowRate, expectedAvgBorrowRate, "avgBorrowRate");
assertApproxEqRel(
irm.borrowRate(marketParams, market1), _expectedAvgRate(market0, market1), 0.01 ether, "avgBorrowRate"
);
assertApproxEqRel(irm.baseRate(marketParams.id()), expectedBaseRate, 0.001 ether, "baseRate");
}

function testBorrowRateView(Market memory market0, Market memory market1) public {
Expand All @@ -89,13 +97,9 @@ contract SpeedJumpIrmTest is Test {
vm.assume(market1.totalSupplyAssets >= market1.totalBorrowAssets);
market1.lastUpdate = uint128(bound(market1.lastUpdate, 0, block.timestamp - 1));

uint256 avgBorrowRate = irm.borrowRateView(marketParams, market1);
(uint256 prevBorrowRate,) = irm.marketIrm(marketParams.id());

(uint256 expectedAvgBorrowRate,) = _expectedBorrowRates(market0, market1);

assertEq(prevBorrowRate, INITIAL_RATE, "prevBorrowRate");
assertEq(avgBorrowRate, expectedAvgBorrowRate, "avgBorrowRate");
assertApproxEqRel(
irm.borrowRateView(marketParams, market1), _expectedAvgRate(market0, market1), 0.01 ether, "avgBorrowRate"
);
}

function testBorrowRateJumpOnly(Market memory market0, Market memory market1) public {
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -107,14 +111,12 @@ contract SpeedJumpIrmTest is Test {
vm.assume(market1.totalSupplyAssets >= market1.totalBorrowAssets);
market1.lastUpdate = uint128(block.timestamp);

uint256 avgBorrowRate = irm.borrowRate(marketParams, market1);
(uint256 prevBorrowRate,) = irm.marketIrm(marketParams.id());

(uint256 expectedAvgBorrowRate, uint256 expectedPrevBorrowRate) = _expectedBorrowRates(market0, market1);

assertEq(expectedAvgBorrowRate, expectedPrevBorrowRate, "expectedAvgBorrowRate");
assertEq(avgBorrowRate, expectedAvgBorrowRate, "avgBorrowRate");
assertEq(prevBorrowRate, expectedPrevBorrowRate, "prevBorrowRate");
assertApproxEqRel(
irm.borrowRate(marketParams, market1), _expectedAvgRate(market0, market1), 0.01 ether, "avgBorrowRate"
);
assertApproxEqRel(
irm.baseRate(marketParams.id()), _expectedBaseRate(marketParams.id(), market1), 0.001 ether, "baseRate"
);
}

function testBorrowRateViewJumpOnly(Market memory market0, Market memory market1) public {
Expand All @@ -126,13 +128,9 @@ contract SpeedJumpIrmTest is Test {
vm.assume(market1.totalSupplyAssets >= market1.totalBorrowAssets);
market1.lastUpdate = uint128(block.timestamp);

uint256 avgBorrowRate = irm.borrowRateView(marketParams, market1);
(uint256 prevBorrowRate,) = irm.marketIrm(marketParams.id());

(uint256 expectedAvgBorrowRate,) = _expectedBorrowRates(market0, market1);

assertEq(prevBorrowRate, INITIAL_RATE, "prevBorrowRate");
assertEq(avgBorrowRate, expectedAvgBorrowRate, "avgBorrowRate");
assertApproxEqRel(
irm.borrowRateView(marketParams, market1), _expectedAvgRate(market0, market1), 0.01 ether, "avgBorrowRate"
);
}

function testBorrowRateSpeedOnly(Market memory market0, Market memory market1) public {
Expand All @@ -144,13 +142,12 @@ contract SpeedJumpIrmTest is Test {
market1.totalSupplyAssets = market0.totalSupplyAssets;
market1.lastUpdate = uint128(bound(market1.lastUpdate, 0, block.timestamp - 1));

uint256 avgBorrowRate = irm.borrowRate(marketParams, market1);
(uint256 prevBorrowRate,) = irm.marketIrm(marketParams.id());

(uint256 expectedAvgBorrowRate, uint256 expectedPrevBorrowRate) = _expectedBorrowRates(market0, market1);
uint256 expectedBaseRate = _expectedBaseRate(marketParams.id(), market1);

assertEq(prevBorrowRate, expectedPrevBorrowRate, "prevBorrowRate");
assertEq(avgBorrowRate, expectedAvgBorrowRate, "avgBorrowRate");
assertApproxEqRel(
irm.borrowRate(marketParams, market1), _expectedAvgRate(market0, market1), 0.01 ether, "avgBorrowRate"
);
assertApproxEqRel(irm.baseRate(marketParams.id()), expectedBaseRate, 0.001 ether, "baseRate");
}

function testBorrowRateViewSpeedOnly(Market memory market0, Market memory market1) public {
Expand All @@ -162,31 +159,35 @@ contract SpeedJumpIrmTest is Test {
market1.totalSupplyAssets = market0.totalSupplyAssets;
market1.lastUpdate = uint128(bound(market1.lastUpdate, 0, block.timestamp - 1));

uint256 avgBorrowRate = irm.borrowRateView(marketParams, market1);
(uint256 prevBorrowRate,) = irm.marketIrm(marketParams.id());
assertApproxEqRel(
irm.borrowRateView(marketParams, market1), _expectedAvgRate(market0, market1), 0.01 ether, "avgBorrowRate"
);
}

(uint256 expectedAvgBorrowRate,) = _expectedBorrowRates(market0, market1);
function _expectedBaseRate(Id id, Market memory market) internal view returns (uint256) {
uint256 baseRate = irm.baseRate(id);
console.log(irm.baseRate(id));

assertEq(prevBorrowRate, INITIAL_RATE, "prevBorrowRate");
assertEq(avgBorrowRate, expectedAvgBorrowRate, "avgBorrowRate");
int256 speed = int256(SPEED_FACTOR).wMulDown(_err(market));
uint256 elapsed = (baseRate > 0) ? block.timestamp - market.lastUpdate : 0;
int256 linearVariation = speed * int256(elapsed);
uint256 variationMultiplier = MathLib.wExp(linearVariation);
return (baseRate > 0) ? baseRate.wMulDown(variationMultiplier) : INITIAL_BASE_RATE;
}

/// @dev Returns the expected `avgBorrowRate` and `prevBorrowRate`.
function _expectedBorrowRates(Market memory market0, Market memory market1)
internal
view
returns (uint256, uint256)
{
int256 err = _err(market1);
/// @dev Returns the expected `avgBorrowRate` and `baseBorrowRate`.
function _expectedAvgRate(Market memory market0, Market memory market1) internal view returns (uint256) {
int256 prevErr = _err(market0);
int256 err = _err(market1);
int256 errDelta = err - prevErr;
uint256 elapsed = block.timestamp - market1.lastUpdate;
uint256 elapsed = block.timestamp - market0.lastUpdate;

uint256 jumpMultiplier = MathLib.wExp(errDelta.wMulDown(int256(LN2)));
int256 speed = int256(SPEED_FACTOR).wMulDown(err);
int256 speed = int256(SPEED_FACTOR).wMulDown(prevErr);
uint256 variationMultiplier = MathLib.wExp(speed * int256(elapsed));
uint256 expectedBorrowRateAfterJump = INITIAL_RATE.wMulDown(jumpMultiplier);
uint256 expectedNewBorrowRate = INITIAL_RATE.wMulDown(jumpMultiplier).wMulDown(variationMultiplier);
uint256 initialRate = INITIAL_BASE_RATE.wMulDown(MathLib.wExp(prevErr));
uint256 expectedBorrowRateAfterJump = initialRate.wMulDown(jumpMultiplier);
uint256 expectedNewBorrowRate = expectedBorrowRateAfterJump.wMulDown(variationMultiplier);

uint256 expectedAvgBorrowRate;
if (speed * int256(elapsed) == 0) {
Expand All @@ -197,13 +198,11 @@ contract SpeedJumpIrmTest is Test {
);
}

return (
expectedAvgBorrowRate.bound(irm.MIN_RATE(), irm.MAX_RATE()),
expectedNewBorrowRate.bound(irm.MIN_RATE(), irm.MAX_RATE())
);
return expectedAvgBorrowRate;
}

function _err(Market memory market) internal pure returns (int256) {
if (market.totalSupplyAssets == 0) return -1 ether;
uint256 utilization = market.totalBorrowAssets.wDivDown(market.totalSupplyAssets);

int256 err;
Expand Down
Loading