From e259e87eff19ae811e4ec8b92badabaec6e87c63 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 19 Jan 2024 00:38:06 +0100 Subject: [PATCH 01/23] feat: fixed rate --- src/FixedRateIrm.sol | 49 ++++++++++++++++++++++++++++++++ src/interfaces/IFixedRateIrm.sol | 14 +++++++++ src/libraries/ErrorsLib.sol | 6 ++++ 3 files changed, 69 insertions(+) create mode 100644 src/FixedRateIrm.sol create mode 100644 src/interfaces/IFixedRateIrm.sol diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol new file mode 100644 index 00000000..a6973184 --- /dev/null +++ b/src/FixedRateIrm.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol"; +import {IFixedRateIrm} from "./interfaces/IFixedRateIrm.sol"; + +import {ErrorsLib} from "./libraries/ErrorsLib.sol"; +import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; +import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; + +/// @title FixedRateIrm +/// @author Morpho Labs +/// @custom:contact security@morpho.org +contract FixedRateIrm is IFixedRateIrm { + using MarketParamsLib for MarketParams; + + /* EVENTS */ + + /// @notice Emitted when a borrow rate is set. + event SetBorrowRate(Id indexed id, uint256 newBorrowRate); + + /* STORAGE */ + + /// @notice Borrow rates. + mapping(Id => uint256) private _borrowRate; + + /* SETTER */ + + function setBorrowRate(Id id, uint256 newBorrowRate) external { + require(_borrowRate[id] == 0, ErrorsLib.ALREADY_SET); + require(newBorrowRate != 0, ErrorsLib.RATE_ZERO); + + _borrowRate[id] = newBorrowRate; + + emit SetBorrowRate(id, newBorrowRate); + } + + /* BORROW RATES */ + + /// @inheritdoc IIrm + function borrowRateView(MarketParams memory marketParams, Market memory) external view returns (uint256) { + return _borrowRate[marketParams.id()]; + } + + /// @inheritdoc IIrm + function borrowRate(MarketParams memory marketParams, Market memory) external view returns (uint256) { + return _borrowRate[marketParams.id()]; + } +} diff --git a/src/interfaces/IFixedRateIrm.sol b/src/interfaces/IFixedRateIrm.sol new file mode 100644 index 00000000..0468d69d --- /dev/null +++ b/src/interfaces/IFixedRateIrm.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0; + +import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; +import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; + +/// @title IAdaptiveCurveIrm +/// @author Morpho Labs +/// @custom:contact security@morpho.org +interface IFixedRateIrm is IIrm { + /// @notice Sets the borrow rate for a market. + /// @dev A rate can be set by anybody, but only once. + function setBorrowRate(Id id, uint256 newBorrowRate) external; +} diff --git a/src/libraries/ErrorsLib.sol b/src/libraries/ErrorsLib.sol index 5cb2c632..a3262674 100644 --- a/src/libraries/ErrorsLib.sol +++ b/src/libraries/ErrorsLib.sol @@ -11,4 +11,10 @@ library ErrorsLib { /// @dev Thrown when the caller is not Morpho. string internal constant NOT_MORPHO = "not Morpho"; + + /// @dev Thrown when the rate is already set for this market. + string internal constant ALREADY_SET = "already set"; + + /// @dev Thrown when trying to set the rate at zero. + string internal constant RATE_ZERO = "rate zero"; } From 169a57c6b832421a6c21e8d013da2d0a1190669c Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 25 Jan 2024 11:19:26 +0100 Subject: [PATCH 02/23] test: add testing --- src/FixedRateIrm.sol | 2 +- test/forge/FixedRateIrmTest.sol | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 test/forge/FixedRateIrmTest.sol diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index a6973184..f25f7daf 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -22,7 +22,7 @@ contract FixedRateIrm is IFixedRateIrm { /* STORAGE */ /// @notice Borrow rates. - mapping(Id => uint256) private _borrowRate; + mapping(Id => uint256) public _borrowRate; /* SETTER */ diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol new file mode 100644 index 00000000..cbee64cb --- /dev/null +++ b/test/forge/FixedRateIrmTest.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../../src/FixedRateIrm.sol"; + +import "../../lib/forge-std/src/Test.sol"; + +contract FixedRateIrmTest is Test { + using MarketParamsLib for MarketParams; + + event SetBorrowRate(Id indexed id, uint256 newBorrowRate); + + FixedRateIrm public fixedRateIrm; + + function setUp() external { + fixedRateIrm = new FixedRateIrm(); + } + + function testSetBorrowRate(Id id, uint256 newBorrowRate) external { + vm.assume(newBorrowRate != 0); + + fixedRateIrm.setBorrowRate(id, newBorrowRate); + assertEq(fixedRateIrm._borrowRate(id), newBorrowRate); + } + + function testSetBorrowRateEvent(Id id, uint256 newBorrowRate) external { + vm.assume(newBorrowRate != 0); + + vm.expectEmit(true, true, true, true, address(fixedRateIrm)); + emit SetBorrowRate(id, newBorrowRate); + fixedRateIrm.setBorrowRate(id, newBorrowRate); + } + + function testSetBorrowRateAlreadySet(Id id, uint256 newBorrowRate1, uint256 newBorrowRate2) external { + vm.assume(newBorrowRate1 != 0); + vm.assume(newBorrowRate2 != 0); + fixedRateIrm.setBorrowRate(id, newBorrowRate1); + vm.expectRevert(bytes(ErrorsLib.ALREADY_SET)); + fixedRateIrm.setBorrowRate(id, newBorrowRate2); + } + + function testSetBorrowRateRateZero(Id id) external { + vm.expectRevert(bytes(ErrorsLib.RATE_ZERO)); + fixedRateIrm.setBorrowRate(id, 0); + } + + function testBorrowRate(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { + vm.assume(newBorrowRate != 0); + fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); + assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); + } + + function testBorrowRateView(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) + external + { + vm.assume(newBorrowRate != 0); + fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); + assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); + } +} From 84f2fd21fdaadd314063bb057400bae4150d0b32 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 25 Jan 2024 11:25:46 +0100 Subject: [PATCH 03/23] docs: minor improvements --- src/FixedRateIrm.sol | 3 ++- src/interfaces/IFixedRateIrm.sol | 1 + src/libraries/ErrorsLib.sol | 2 +- test/forge/FixedRateIrmTest.sol | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index f25f7daf..a2dc61e1 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -26,8 +26,9 @@ contract FixedRateIrm is IFixedRateIrm { /* SETTER */ + /// @inheritdoc IFixedRateIrm function setBorrowRate(Id id, uint256 newBorrowRate) external { - require(_borrowRate[id] == 0, ErrorsLib.ALREADY_SET); + require(_borrowRate[id] == 0, ErrorsLib.RATE_ALREADY_SET); require(newBorrowRate != 0, ErrorsLib.RATE_ZERO); _borrowRate[id] = newBorrowRate; diff --git a/src/interfaces/IFixedRateIrm.sol b/src/interfaces/IFixedRateIrm.sol index 0468d69d..33239bcd 100644 --- a/src/interfaces/IFixedRateIrm.sol +++ b/src/interfaces/IFixedRateIrm.sol @@ -10,5 +10,6 @@ import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; interface IFixedRateIrm is IIrm { /// @notice Sets the borrow rate for a market. /// @dev A rate can be set by anybody, but only once. + /// @dev The creator of a market with this IRM would typically batch the creation with the setting of the rate. function setBorrowRate(Id id, uint256 newBorrowRate) external; } diff --git a/src/libraries/ErrorsLib.sol b/src/libraries/ErrorsLib.sol index a3262674..579edde2 100644 --- a/src/libraries/ErrorsLib.sol +++ b/src/libraries/ErrorsLib.sol @@ -13,7 +13,7 @@ library ErrorsLib { string internal constant NOT_MORPHO = "not Morpho"; /// @dev Thrown when the rate is already set for this market. - string internal constant ALREADY_SET = "already set"; + string internal constant RATE_ALREADY_SET = "rate already set"; /// @dev Thrown when trying to set the rate at zero. string internal constant RATE_ZERO = "rate zero"; diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index cbee64cb..d060afd6 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -35,7 +35,7 @@ contract FixedRateIrmTest is Test { vm.assume(newBorrowRate1 != 0); vm.assume(newBorrowRate2 != 0); fixedRateIrm.setBorrowRate(id, newBorrowRate1); - vm.expectRevert(bytes(ErrorsLib.ALREADY_SET)); + vm.expectRevert(bytes(ErrorsLib.RATE_ALREADY_SET)); fixedRateIrm.setBorrowRate(id, newBorrowRate2); } From 6ae27d92c321ab3454f62b47120746456e59ee45 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 5 Feb 2024 16:22:18 +0100 Subject: [PATCH 04/23] refactor: remove errors from lib --- src/FixedRateIrm.sol | 12 +++++++++--- src/libraries/ErrorsLib.sol | 6 ------ test/forge/FixedRateIrmTest.sol | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index a2dc61e1..1a810224 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.19; import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol"; import {IFixedRateIrm} from "./interfaces/IFixedRateIrm.sol"; -import {ErrorsLib} from "./libraries/ErrorsLib.sol"; import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; @@ -19,6 +18,13 @@ contract FixedRateIrm is IFixedRateIrm { /// @notice Emitted when a borrow rate is set. event SetBorrowRate(Id indexed id, uint256 newBorrowRate); + /* ERRORS */ + + /// @dev Thrown when the rate is already set for this market. + string public constant RATE_ALREADY_SET = "rate already set"; + /// @dev Thrown when trying to set the rate at zero. + string public constant RATE_ZERO = "rate zero"; + /* STORAGE */ /// @notice Borrow rates. @@ -28,8 +34,8 @@ contract FixedRateIrm is IFixedRateIrm { /// @inheritdoc IFixedRateIrm function setBorrowRate(Id id, uint256 newBorrowRate) external { - require(_borrowRate[id] == 0, ErrorsLib.RATE_ALREADY_SET); - require(newBorrowRate != 0, ErrorsLib.RATE_ZERO); + require(_borrowRate[id] == 0, RATE_ALREADY_SET); + require(newBorrowRate != 0, RATE_ZERO); _borrowRate[id] = newBorrowRate; diff --git a/src/libraries/ErrorsLib.sol b/src/libraries/ErrorsLib.sol index 579edde2..5cb2c632 100644 --- a/src/libraries/ErrorsLib.sol +++ b/src/libraries/ErrorsLib.sol @@ -11,10 +11,4 @@ library ErrorsLib { /// @dev Thrown when the caller is not Morpho. string internal constant NOT_MORPHO = "not Morpho"; - - /// @dev Thrown when the rate is already set for this market. - string internal constant RATE_ALREADY_SET = "rate already set"; - - /// @dev Thrown when trying to set the rate at zero. - string internal constant RATE_ZERO = "rate zero"; } diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index d060afd6..b878a413 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -35,12 +35,12 @@ contract FixedRateIrmTest is Test { vm.assume(newBorrowRate1 != 0); vm.assume(newBorrowRate2 != 0); fixedRateIrm.setBorrowRate(id, newBorrowRate1); - vm.expectRevert(bytes(ErrorsLib.RATE_ALREADY_SET)); + vm.expectRevert("rate already set"); fixedRateIrm.setBorrowRate(id, newBorrowRate2); } function testSetBorrowRateRateZero(Id id) external { - vm.expectRevert(bytes(ErrorsLib.RATE_ZERO)); + vm.expectRevert("rate zero"); fixedRateIrm.setBorrowRate(id, 0); } From 0cb5741b6c892cc30aa0b5824a077bf79a9418b1 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 5 Feb 2024 16:28:07 +0100 Subject: [PATCH 05/23] feat: revert on not yet set rate --- src/FixedRateIrm.sol | 14 ++++++++++---- test/forge/FixedRateIrmTest.sol | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index 1a810224..6ef33a72 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -20,8 +20,10 @@ contract FixedRateIrm is IFixedRateIrm { /* ERRORS */ + /// @dev Thrown when the rate is not already set for this market. + string public constant RATE_NOT_SET = "rate not set"; /// @dev Thrown when the rate is already set for this market. - string public constant RATE_ALREADY_SET = "rate already set"; + string public constant RATE_SET = "rate set"; /// @dev Thrown when trying to set the rate at zero. string public constant RATE_ZERO = "rate zero"; @@ -34,7 +36,7 @@ contract FixedRateIrm is IFixedRateIrm { /// @inheritdoc IFixedRateIrm function setBorrowRate(Id id, uint256 newBorrowRate) external { - require(_borrowRate[id] == 0, RATE_ALREADY_SET); + require(_borrowRate[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO); _borrowRate[id] = newBorrowRate; @@ -46,11 +48,15 @@ contract FixedRateIrm is IFixedRateIrm { /// @inheritdoc IIrm function borrowRateView(MarketParams memory marketParams, Market memory) external view returns (uint256) { - return _borrowRate[marketParams.id()]; + uint256 borrowRateCached = _borrowRate[marketParams.id()]; + require(borrowRateCached != 0, RATE_NOT_SET); + return borrowRateCached; } /// @inheritdoc IIrm function borrowRate(MarketParams memory marketParams, Market memory) external view returns (uint256) { - return _borrowRate[marketParams.id()]; + uint256 borrowRateCached = _borrowRate[marketParams.id()]; + require(borrowRateCached != 0, RATE_NOT_SET); + return borrowRateCached; } } diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index b878a413..81e40d49 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -35,7 +35,7 @@ contract FixedRateIrmTest is Test { vm.assume(newBorrowRate1 != 0); vm.assume(newBorrowRate2 != 0); fixedRateIrm.setBorrowRate(id, newBorrowRate1); - vm.expectRevert("rate already set"); + vm.expectRevert("rate set"); fixedRateIrm.setBorrowRate(id, newBorrowRate2); } @@ -50,6 +50,13 @@ contract FixedRateIrmTest is Test { assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); } + function testBorrowRateRateZero(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) + external + { + vm.expectRevert("rate not set"); + fixedRateIrm.borrowRate(marketParams, market); + } + function testBorrowRateView(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { @@ -57,4 +64,11 @@ contract FixedRateIrmTest is Test { fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); } + + function testBorrowRateViewRateZero(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) + external + { + vm.expectRevert("rate not set"); + fixedRateIrm.borrowRateView(marketParams, market); + } } From e382c16924f483ce51354b048cea17db0feb8379 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 6 Feb 2024 10:10:13 +0100 Subject: [PATCH 06/23] chore: various improvements --- foundry.toml | 5 ----- test/forge/FixedRateIrmTest.sol | 8 ++------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/foundry.toml b/foundry.toml index f09ac506..04766095 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,15 +15,10 @@ fail_on_revert = true [profile.default.fmt] wrap_comments = true - [profile.build] test = "/dev/null" script = "/dev/null" - -[profile.test] -via_ir = false - [profile.test.fuzz] runs = 16384 diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index 81e40d49..08ef636f 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -50,9 +50,7 @@ contract FixedRateIrmTest is Test { assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); } - function testBorrowRateRateZero(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) - external - { + function testBorrowRateRateZero(MarketParams memory marketParams, Market memory market) external { vm.expectRevert("rate not set"); fixedRateIrm.borrowRate(marketParams, market); } @@ -65,9 +63,7 @@ contract FixedRateIrmTest is Test { assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); } - function testBorrowRateViewRateZero(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) - external - { + function testBorrowRateViewRateZero(MarketParams memory marketParams, Market memory market) external { vm.expectRevert("rate not set"); fixedRateIrm.borrowRateView(marketParams, market); } From d446aedf137f7e50f2fa2f35400c7a98a15da9ad Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 8 Feb 2024 11:14:03 +0100 Subject: [PATCH 07/23] docs: improve --- src/FixedRateIrm.sol | 1 + src/interfaces/IFixedRateIrm.sol | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index 6ef33a72..eef3becd 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -54,6 +54,7 @@ contract FixedRateIrm is IFixedRateIrm { } /// @inheritdoc IIrm + /// @dev Reverts on not set rate, so the rate has to be set before the market creation. function borrowRate(MarketParams memory marketParams, Market memory) external view returns (uint256) { uint256 borrowRateCached = _borrowRate[marketParams.id()]; require(borrowRateCached != 0, RATE_NOT_SET); diff --git a/src/interfaces/IFixedRateIrm.sol b/src/interfaces/IFixedRateIrm.sol index 33239bcd..7da86b04 100644 --- a/src/interfaces/IFixedRateIrm.sol +++ b/src/interfaces/IFixedRateIrm.sol @@ -10,6 +10,6 @@ import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; interface IFixedRateIrm is IIrm { /// @notice Sets the borrow rate for a market. /// @dev A rate can be set by anybody, but only once. - /// @dev The creator of a market with this IRM would typically batch the creation with the setting of the rate. + /// @dev The creator of a market with this IRM would typically batch setting of the rate with the market creation. function setBorrowRate(Id id, uint256 newBorrowRate) external; } From 01e5d80e5e1ef30c43433cbcbbee76c603417d72 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 8 Feb 2024 11:17:07 +0100 Subject: [PATCH 08/23] chore: update compiler version --- src/FixedRateIrm.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index eef3becd..e5ecfa01 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity 0.8.24; import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol"; import {IFixedRateIrm} from "./interfaces/IFixedRateIrm.sol"; From 958fe16e6d955f3af78f60ec80595b8f5fd783ab Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 8 Feb 2024 11:23:03 +0100 Subject: [PATCH 09/23] test: fix hardhat config --- hardhat.config.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index 7ef6a904..888c4a10 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -35,6 +35,16 @@ const config: HardhatUserConfig = { viaIR: true, }, }, + { + version: "0.8.24", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + viaIR: true, + }, + }, ], }, mocha: { From 3d30b9e9daa425c91f5365c7e966fa861557863b Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 8 Feb 2024 20:30:31 +0100 Subject: [PATCH 10/23] chore: various improvements --- src/FixedRateIrm.sol | 18 +++++++++--------- test/forge/FixedRateIrmTest.sol | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index e5ecfa01..cfc17052 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -7,6 +7,15 @@ import {IFixedRateIrm} from "./interfaces/IFixedRateIrm.sol"; import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; +/* ERRORS */ + +/// @dev Thrown when the rate is not already set for this market. +string constant RATE_NOT_SET = "rate not set"; +/// @dev Thrown when the rate is already set for this market. +string constant RATE_SET = "rate set"; +/// @dev Thrown when trying to set the rate at zero. +string constant RATE_ZERO = "rate zero"; + /// @title FixedRateIrm /// @author Morpho Labs /// @custom:contact security@morpho.org @@ -18,15 +27,6 @@ contract FixedRateIrm is IFixedRateIrm { /// @notice Emitted when a borrow rate is set. event SetBorrowRate(Id indexed id, uint256 newBorrowRate); - /* ERRORS */ - - /// @dev Thrown when the rate is not already set for this market. - string public constant RATE_NOT_SET = "rate not set"; - /// @dev Thrown when the rate is already set for this market. - string public constant RATE_SET = "rate set"; - /// @dev Thrown when trying to set the rate at zero. - string public constant RATE_ZERO = "rate zero"; - /* STORAGE */ /// @notice Borrow rates. diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index 08ef636f..c3d85a33 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -35,12 +35,12 @@ contract FixedRateIrmTest is Test { vm.assume(newBorrowRate1 != 0); vm.assume(newBorrowRate2 != 0); fixedRateIrm.setBorrowRate(id, newBorrowRate1); - vm.expectRevert("rate set"); + vm.expectRevert(bytes(RATE_SET)); fixedRateIrm.setBorrowRate(id, newBorrowRate2); } function testSetBorrowRateRateZero(Id id) external { - vm.expectRevert("rate zero"); + vm.expectRevert(bytes(RATE_ZERO)); fixedRateIrm.setBorrowRate(id, 0); } @@ -50,8 +50,8 @@ contract FixedRateIrmTest is Test { assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); } - function testBorrowRateRateZero(MarketParams memory marketParams, Market memory market) external { - vm.expectRevert("rate not set"); + function testBorrowRateRateNotSet(MarketParams memory marketParams, Market memory market) external { + vm.expectRevert(bytes(RATE_NOT_SET)); fixedRateIrm.borrowRate(marketParams, market); } @@ -63,8 +63,8 @@ contract FixedRateIrmTest is Test { assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); } - function testBorrowRateViewRateZero(MarketParams memory marketParams, Market memory market) external { - vm.expectRevert("rate not set"); + function testBorrowRateViewRateNotSet(MarketParams memory marketParams, Market memory market) external { + vm.expectRevert(bytes(RATE_NOT_SET)); fixedRateIrm.borrowRateView(marketParams, market); } } From 9609f7481268dcddd140e558dfe90142e1b554c0 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 8 Feb 2024 22:08:10 +0100 Subject: [PATCH 11/23] chore: further improvements --- hardhat.config.ts | 4 ++-- src/FixedRateIrm.sol | 15 +++++++++------ src/interfaces/IFixedRateIrm.sol | 4 +--- test/forge/FixedRateIrmTest.sol | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 888c4a10..e5227301 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -30,7 +30,7 @@ const config: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 200, + runs: 999999, }, viaIR: true, }, @@ -40,7 +40,7 @@ const config: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 200, + runs: 999999, }, viaIR: true, }, diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index cfc17052..04f8114b 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -30,16 +30,19 @@ contract FixedRateIrm is IFixedRateIrm { /* STORAGE */ /// @notice Borrow rates. - mapping(Id => uint256) public _borrowRate; + mapping(Id => uint256) public borrowRateStored; /* SETTER */ - /// @inheritdoc IFixedRateIrm + /// @notice Sets the borrow rate for a market. + /// @dev A rate can be set by anybody, but only once. + /// @dev `borrowRate` reverts on rate not set, so the rate needs to be set before the market creation. + /// @dev The creator of a market with this IRM would typically batch setting of the rate with the market creation. function setBorrowRate(Id id, uint256 newBorrowRate) external { - require(_borrowRate[id] == 0, RATE_SET); + require(borrowRateStored[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO); - _borrowRate[id] = newBorrowRate; + borrowRateStored[id] = newBorrowRate; emit SetBorrowRate(id, newBorrowRate); } @@ -48,7 +51,7 @@ contract FixedRateIrm is IFixedRateIrm { /// @inheritdoc IIrm function borrowRateView(MarketParams memory marketParams, Market memory) external view returns (uint256) { - uint256 borrowRateCached = _borrowRate[marketParams.id()]; + uint256 borrowRateCached = borrowRateStored[marketParams.id()]; require(borrowRateCached != 0, RATE_NOT_SET); return borrowRateCached; } @@ -56,7 +59,7 @@ contract FixedRateIrm is IFixedRateIrm { /// @inheritdoc IIrm /// @dev Reverts on not set rate, so the rate has to be set before the market creation. function borrowRate(MarketParams memory marketParams, Market memory) external view returns (uint256) { - uint256 borrowRateCached = _borrowRate[marketParams.id()]; + uint256 borrowRateCached = borrowRateStored[marketParams.id()]; require(borrowRateCached != 0, RATE_NOT_SET); return borrowRateCached; } diff --git a/src/interfaces/IFixedRateIrm.sol b/src/interfaces/IFixedRateIrm.sol index 7da86b04..f4800e9f 100644 --- a/src/interfaces/IFixedRateIrm.sol +++ b/src/interfaces/IFixedRateIrm.sol @@ -8,8 +8,6 @@ import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; /// @author Morpho Labs /// @custom:contact security@morpho.org interface IFixedRateIrm is IIrm { - /// @notice Sets the borrow rate for a market. - /// @dev A rate can be set by anybody, but only once. - /// @dev The creator of a market with this IRM would typically batch setting of the rate with the market creation. + function borrowRateStored(Id id) external returns (uint256); function setBorrowRate(Id id, uint256 newBorrowRate) external; } diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index c3d85a33..27e823aa 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -20,7 +20,7 @@ contract FixedRateIrmTest is Test { vm.assume(newBorrowRate != 0); fixedRateIrm.setBorrowRate(id, newBorrowRate); - assertEq(fixedRateIrm._borrowRate(id), newBorrowRate); + assertEq(fixedRateIrm.borrowRateStored(id), newBorrowRate); } function testSetBorrowRateEvent(Id id, uint256 newBorrowRate) external { From f801d3e8440eabda58266c39d7d21ee9e39e986d Mon Sep 17 00:00:00 2001 From: MathisGD <74971347+MathisGD@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:28:46 +0100 Subject: [PATCH 12/23] docs: minor update Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com> --- src/FixedRateIrm.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/FixedRateIrm.sol b/src/FixedRateIrm.sol index 04f8114b..f9cb2f3a 100644 --- a/src/FixedRateIrm.sol +++ b/src/FixedRateIrm.sol @@ -37,7 +37,6 @@ contract FixedRateIrm is IFixedRateIrm { /// @notice Sets the borrow rate for a market. /// @dev A rate can be set by anybody, but only once. /// @dev `borrowRate` reverts on rate not set, so the rate needs to be set before the market creation. - /// @dev The creator of a market with this IRM would typically batch setting of the rate with the market creation. function setBorrowRate(Id id, uint256 newBorrowRate) external { require(borrowRateStored[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO); From eb954aff96e9893a98dc3eab1a6d605f044cd285 Mon Sep 17 00:00:00 2001 From: MathisGD <74971347+MathisGD@users.noreply.github.com> Date: Thu, 15 Feb 2024 08:14:58 +0100 Subject: [PATCH 13/23] docs: minor fix Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com> --- src/interfaces/IFixedRateIrm.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/IFixedRateIrm.sol b/src/interfaces/IFixedRateIrm.sol index f4800e9f..fff40a41 100644 --- a/src/interfaces/IFixedRateIrm.sol +++ b/src/interfaces/IFixedRateIrm.sol @@ -4,7 +4,7 @@ pragma solidity >=0.5.0; import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; -/// @title IAdaptiveCurveIrm +/// @title IFixedRateIrm /// @author Morpho Labs /// @custom:contact security@morpho.org interface IFixedRateIrm is IIrm { From a824ce06a53f45f12d0ffedb51abd756896b29fa Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 19 Feb 2024 11:44:24 +0100 Subject: [PATCH 14/23] refactor: repo org --- src/{ => adaptive-curve-irm}/AdaptiveCurveIrm.sol | 12 ++++++------ .../interfaces/IAdaptiveCurveIrm.sol | 4 ++-- .../libraries}/ConstantsLib.sol | 0 src/{ => adaptive-curve-irm}/libraries/ErrorsLib.sol | 0 .../libraries}/ExpLib.sol | 2 +- src/{ => adaptive-curve-irm}/libraries/MathLib.sol | 2 +- src/{ => adaptive-curve-irm}/libraries/UtilsLib.sol | 0 src/{ => fixed-rate-irm}/FixedRateIrm.sol | 6 +++--- .../interfaces/IFixedRateIrm.sol | 4 ++-- test/forge/AdaptiveCurveIrmTest.sol | 2 +- test/forge/ExpLibTest.sol | 6 +++--- test/forge/FixedRateIrmTest.sol | 2 +- test/forge/UtilsLibTest.sol | 2 +- 13 files changed, 21 insertions(+), 21 deletions(-) rename src/{ => adaptive-curve-irm}/AdaptiveCurveIrm.sol (93%) rename src/{ => adaptive-curve-irm}/interfaces/IAdaptiveCurveIrm.sol (77%) rename src/{libraries/adaptive-curve => adaptive-curve-irm/libraries}/ConstantsLib.sol (100%) rename src/{ => adaptive-curve-irm}/libraries/ErrorsLib.sol (100%) rename src/{libraries/adaptive-curve => adaptive-curve-irm/libraries}/ExpLib.sol (98%) rename src/{ => adaptive-curve-irm}/libraries/MathLib.sol (90%) rename src/{ => adaptive-curve-irm}/libraries/UtilsLib.sol (100%) rename src/{ => fixed-rate-irm}/FixedRateIrm.sol (89%) rename src/{ => fixed-rate-irm}/interfaces/IFixedRateIrm.sol (69%) diff --git a/src/AdaptiveCurveIrm.sol b/src/adaptive-curve-irm/AdaptiveCurveIrm.sol similarity index 93% rename from src/AdaptiveCurveIrm.sol rename to src/adaptive-curve-irm/AdaptiveCurveIrm.sol index f19ec822..3ecf1cb8 100644 --- a/src/AdaptiveCurveIrm.sol +++ b/src/adaptive-curve-irm/AdaptiveCurveIrm.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol"; +import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol"; import {UtilsLib} from "./libraries/UtilsLib.sol"; import {ErrorsLib} from "./libraries/ErrorsLib.sol"; -import {ExpLib} from "./libraries/adaptive-curve/ExpLib.sol"; +import {ExpLib} from "./libraries/ExpLib.sol"; import {MathLib, WAD_INT as WAD} from "./libraries/MathLib.sol"; -import {ConstantsLib} from "./libraries/adaptive-curve/ConstantsLib.sol"; -import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; -import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; -import {MathLib as MorphoMathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol"; +import {ConstantsLib} from "./libraries/ConstantsLib.sol"; +import {MarketParamsLib} from "../../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; +import {Id, MarketParams, Market} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; +import {MathLib as MorphoMathLib} from "../../lib/morpho-blue/src/libraries/MathLib.sol"; /// @title AdaptiveCurveIrm /// @author Morpho Labs diff --git a/src/interfaces/IAdaptiveCurveIrm.sol b/src/adaptive-curve-irm/interfaces/IAdaptiveCurveIrm.sol similarity index 77% rename from src/interfaces/IAdaptiveCurveIrm.sol rename to src/adaptive-curve-irm/interfaces/IAdaptiveCurveIrm.sol index 0f2659a4..087c2db8 100644 --- a/src/interfaces/IAdaptiveCurveIrm.sol +++ b/src/adaptive-curve-irm/interfaces/IAdaptiveCurveIrm.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.5.0; -import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; -import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; +import {IIrm} from "../../../lib/morpho-blue/src/interfaces/IIrm.sol"; +import {Id} from "../../../lib/morpho-blue/src/interfaces/IMorpho.sol"; /// @title IAdaptiveCurveIrm /// @author Morpho Labs diff --git a/src/libraries/adaptive-curve/ConstantsLib.sol b/src/adaptive-curve-irm/libraries/ConstantsLib.sol similarity index 100% rename from src/libraries/adaptive-curve/ConstantsLib.sol rename to src/adaptive-curve-irm/libraries/ConstantsLib.sol diff --git a/src/libraries/ErrorsLib.sol b/src/adaptive-curve-irm/libraries/ErrorsLib.sol similarity index 100% rename from src/libraries/ErrorsLib.sol rename to src/adaptive-curve-irm/libraries/ErrorsLib.sol diff --git a/src/libraries/adaptive-curve/ExpLib.sol b/src/adaptive-curve-irm/libraries/ExpLib.sol similarity index 98% rename from src/libraries/adaptive-curve/ExpLib.sol rename to src/adaptive-curve-irm/libraries/ExpLib.sol index 27110d31..e6732f54 100644 --- a/src/libraries/adaptive-curve/ExpLib.sol +++ b/src/adaptive-curve-irm/libraries/ExpLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {WAD_INT} from "../MathLib.sol"; +import {WAD_INT} from "./MathLib.sol"; /// @title ExpLib /// @author Morpho Labs diff --git a/src/libraries/MathLib.sol b/src/adaptive-curve-irm/libraries/MathLib.sol similarity index 90% rename from src/libraries/MathLib.sol rename to src/adaptive-curve-irm/libraries/MathLib.sol index f9cfe3d9..9421c780 100644 --- a/src/libraries/MathLib.sol +++ b/src/adaptive-curve-irm/libraries/MathLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {WAD} from "../../lib/morpho-blue/src/libraries/MathLib.sol"; +import {WAD} from "../../../lib/morpho-blue/src/libraries/MathLib.sol"; int256 constant WAD_INT = int256(WAD); diff --git a/src/libraries/UtilsLib.sol b/src/adaptive-curve-irm/libraries/UtilsLib.sol similarity index 100% rename from src/libraries/UtilsLib.sol rename to src/adaptive-curve-irm/libraries/UtilsLib.sol diff --git a/src/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol similarity index 89% rename from src/FixedRateIrm.sol rename to src/fixed-rate-irm/FixedRateIrm.sol index 04f8114b..cc95d46f 100644 --- a/src/FixedRateIrm.sol +++ b/src/fixed-rate-irm/FixedRateIrm.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol"; +import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; import {IFixedRateIrm} from "./interfaces/IFixedRateIrm.sol"; -import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; -import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; +import {MarketParamsLib} from "../../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; +import {Id, MarketParams, Market} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; /* ERRORS */ diff --git a/src/interfaces/IFixedRateIrm.sol b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol similarity index 69% rename from src/interfaces/IFixedRateIrm.sol rename to src/fixed-rate-irm/interfaces/IFixedRateIrm.sol index f4800e9f..8dbba4e8 100644 --- a/src/interfaces/IFixedRateIrm.sol +++ b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.5.0; -import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; -import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; +import {IIrm} from "../../../lib/morpho-blue/src/interfaces/IIrm.sol"; +import {Id} from "../../../lib/morpho-blue/src/interfaces/IMorpho.sol"; /// @title IAdaptiveCurveIrm /// @author Morpho Labs diff --git a/test/forge/AdaptiveCurveIrmTest.sol b/test/forge/AdaptiveCurveIrmTest.sol index 8d821340..8b510bc6 100644 --- a/test/forge/AdaptiveCurveIrmTest.sol +++ b/test/forge/AdaptiveCurveIrmTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../../src/AdaptiveCurveIrm.sol"; +import "../../src/adaptive-curve-irm/AdaptiveCurveIrm.sol"; import "../../lib/forge-std/src/Test.sol"; diff --git a/test/forge/ExpLibTest.sol b/test/forge/ExpLibTest.sol index ee61ce06..2e27fa14 100644 --- a/test/forge/ExpLibTest.sol +++ b/test/forge/ExpLibTest.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {MathLib, WAD_INT} from "../../src/libraries/MathLib.sol"; -import {ExpLib} from "../../src/libraries/adaptive-curve/ExpLib.sol"; +import {MathLib, WAD_INT} from "../../src/adaptive-curve-irm/libraries/MathLib.sol"; +import {ExpLib} from "../../src/adaptive-curve-irm/libraries/ExpLib.sol"; import {wadExp} from "../../lib/solmate/src/utils/SignedWadMath.sol"; -import {ConstantsLib} from "../../src/libraries/adaptive-curve/ConstantsLib.sol"; +import {ConstantsLib} from "../../src/adaptive-curve-irm/libraries/ConstantsLib.sol"; import {MathLib as MorphoMathLib} from "../../lib/morpho-blue/src/libraries/MathLib.sol"; import "../../lib/forge-std/src/Test.sol"; diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index 27e823aa..730360a1 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../../src/FixedRateIrm.sol"; +import "../../src/fixed-rate-irm/FixedRateIrm.sol"; import "../../lib/forge-std/src/Test.sol"; diff --git a/test/forge/UtilsLibTest.sol b/test/forge/UtilsLibTest.sol index 89d10824..ecde91ac 100644 --- a/test/forge/UtilsLibTest.sol +++ b/test/forge/UtilsLibTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../../src/libraries/UtilsLib.sol"; +import "../../src/adaptive-curve-irm/libraries/UtilsLib.sol"; import "../../lib/forge-std/src/Test.sol"; From 7e17db211fc96b0fcc1c9a5cfc894429ff20c250 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 19 Feb 2024 14:02:58 +0100 Subject: [PATCH 15/23] chore: revert foundry toml changes --- foundry.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/foundry.toml b/foundry.toml index 04766095..74e5acbb 100644 --- a/foundry.toml +++ b/foundry.toml @@ -22,6 +22,9 @@ script = "/dev/null" [profile.test.fuzz] runs = 16384 +[profile.test] +via_ir = false + [profile.test.invariant] runs = 32 depth = 1024 From 23259f8893af594d000aed9608463ef4d365ae0b Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 19 Feb 2024 14:07:29 +0100 Subject: [PATCH 16/23] chore: reorder toml --- foundry.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/foundry.toml b/foundry.toml index 74e5acbb..e56430d1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -19,12 +19,12 @@ wrap_comments = true test = "/dev/null" script = "/dev/null" -[profile.test.fuzz] -runs = 16384 - [profile.test] via_ir = false +[profile.test.fuzz] +runs = 16384 + [profile.test.invariant] runs = 32 depth = 1024 From 73f99750b94ca25596feb7ed33301bea64203537 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Wed, 28 Feb 2024 10:37:38 +0100 Subject: [PATCH 17/23] docs: natspec in interface --- src/fixed-rate-irm/FixedRateIrm.sol | 11 ++--------- src/fixed-rate-irm/interfaces/IFixedRateIrm.sol | 12 ++++++++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/fixed-rate-irm/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol index 1832bd75..b33a59c9 100644 --- a/src/fixed-rate-irm/FixedRateIrm.sol +++ b/src/fixed-rate-irm/FixedRateIrm.sol @@ -22,21 +22,14 @@ string constant RATE_ZERO = "rate zero"; contract FixedRateIrm is IFixedRateIrm { using MarketParamsLib for MarketParams; - /* EVENTS */ - - /// @notice Emitted when a borrow rate is set. - event SetBorrowRate(Id indexed id, uint256 newBorrowRate); - /* STORAGE */ - /// @notice Borrow rates. + /// @inheritdoc IFixedRateIrm mapping(Id => uint256) public borrowRateStored; /* SETTER */ - /// @notice Sets the borrow rate for a market. - /// @dev A rate can be set by anybody, but only once. - /// @dev `borrowRate` reverts on rate not set, so the rate needs to be set before the market creation. + /// @inheritdoc IFixedRateIrm function setBorrowRate(Id id, uint256 newBorrowRate) external { require(borrowRateStored[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO); diff --git a/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol index dacbdfc6..78202db1 100644 --- a/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol +++ b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol @@ -8,6 +8,18 @@ import {Id} from "../../../lib/morpho-blue/src/interfaces/IMorpho.sol"; /// @author Morpho Labs /// @custom:contact security@morpho.org interface IFixedRateIrm is IIrm { + /* EVENTS */ + + /// @notice Emitted when a borrow rate is set. + event SetBorrowRate(Id indexed id, uint256 newBorrowRate); + + /* EXTERNAL */ + + /// @notice Borrow rates. function borrowRateStored(Id id) external returns (uint256); + + /// @notice Sets the borrow rate for a market. + /// @dev A rate can be set by anybody, but only once. + /// @dev `borrowRate` reverts on rate not set, so the rate needs to be set before the market creation. function setBorrowRate(Id id, uint256 newBorrowRate) external; } From a0fa8f4bef176c61bee49a2052c3959f6dd0b67a Mon Sep 17 00:00:00 2001 From: MathisGD Date: Wed, 28 Feb 2024 10:47:27 +0100 Subject: [PATCH 18/23] fix: bound rate --- src/fixed-rate-irm/FixedRateIrm.sol | 8 ++++++++ test/forge/FixedRateIrmTest.sol | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/fixed-rate-irm/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol index 1832bd75..1a42f4f8 100644 --- a/src/fixed-rate-irm/FixedRateIrm.sol +++ b/src/fixed-rate-irm/FixedRateIrm.sol @@ -15,6 +15,8 @@ string constant RATE_NOT_SET = "rate not set"; string constant RATE_SET = "rate set"; /// @dev Thrown when trying to set the rate at zero. string constant RATE_ZERO = "rate zero"; +/// @dev Thrown when trying to set a rate that is too high. +string constant RATE_TOO_HIGH = "rate too high"; /// @title FixedRateIrm /// @author Morpho Labs @@ -27,6 +29,11 @@ contract FixedRateIrm is IFixedRateIrm { /// @notice Emitted when a borrow rate is set. event SetBorrowRate(Id indexed id, uint256 newBorrowRate); + /* CONSTANTS */ + + /// @notice Max settable borrow rate (800%). + uint256 public constant MAX_BORROW_RATE = 8.0 ether / uint256(365 days); + /* STORAGE */ /// @notice Borrow rates. @@ -40,6 +47,7 @@ contract FixedRateIrm is IFixedRateIrm { function setBorrowRate(Id id, uint256 newBorrowRate) external { require(borrowRateStored[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO); + require(newBorrowRate <= MAX_BORROW_RATE, RATE_TOO_HIGH); borrowRateStored[id] = newBorrowRate; diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index 730360a1..ede0e0e2 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -18,6 +18,7 @@ contract FixedRateIrmTest is Test { function testSetBorrowRate(Id id, uint256 newBorrowRate) external { vm.assume(newBorrowRate != 0); + vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(id, newBorrowRate); assertEq(fixedRateIrm.borrowRateStored(id), newBorrowRate); @@ -25,6 +26,7 @@ contract FixedRateIrmTest is Test { function testSetBorrowRateEvent(Id id, uint256 newBorrowRate) external { vm.assume(newBorrowRate != 0); + vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); vm.expectEmit(true, true, true, true, address(fixedRateIrm)); emit SetBorrowRate(id, newBorrowRate); @@ -33,7 +35,9 @@ contract FixedRateIrmTest is Test { function testSetBorrowRateAlreadySet(Id id, uint256 newBorrowRate1, uint256 newBorrowRate2) external { vm.assume(newBorrowRate1 != 0); + vm.assume(newBorrowRate1 <= fixedRateIrm.MAX_BORROW_RATE()); vm.assume(newBorrowRate2 != 0); + vm.assume(newBorrowRate2 <= fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(id, newBorrowRate1); vm.expectRevert(bytes(RATE_SET)); fixedRateIrm.setBorrowRate(id, newBorrowRate2); @@ -44,8 +48,15 @@ contract FixedRateIrmTest is Test { fixedRateIrm.setBorrowRate(id, 0); } + function testSetBorrowRateRateTooHigh(Id id, uint256 newBorrowRate) external { + vm.assume(newBorrowRate > fixedRateIrm.MAX_BORROW_RATE()); + vm.expectRevert(bytes(RATE_TOO_HIGH)); + fixedRateIrm.setBorrowRate(id, newBorrowRate); + } + function testBorrowRate(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { vm.assume(newBorrowRate != 0); + vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); } @@ -59,6 +70,7 @@ contract FixedRateIrmTest is Test { external { vm.assume(newBorrowRate != 0); + vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); } From 1af2ddb37810bbc96d6b3448de55c11c7dae0d95 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Wed, 28 Feb 2024 11:09:59 +0100 Subject: [PATCH 19/23] test: minor improvement --- test/forge/FixedRateIrmTest.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index ede0e0e2..106f2ee0 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -34,10 +34,9 @@ contract FixedRateIrmTest is Test { } function testSetBorrowRateAlreadySet(Id id, uint256 newBorrowRate1, uint256 newBorrowRate2) external { - vm.assume(newBorrowRate1 != 0); - vm.assume(newBorrowRate1 <= fixedRateIrm.MAX_BORROW_RATE()); - vm.assume(newBorrowRate2 != 0); - vm.assume(newBorrowRate2 <= fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate1 = bound(newBorrowRate1, 1, fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate2 = bound(newBorrowRate2, 1, fixedRateIrm.MAX_BORROW_RATE()); + fixedRateIrm.setBorrowRate(id, newBorrowRate1); vm.expectRevert(bytes(RATE_SET)); fixedRateIrm.setBorrowRate(id, newBorrowRate2); From 995e08f57daad164e75b0c421c30be4dbdb0b66f Mon Sep 17 00:00:00 2001 From: MathisGD Date: Wed, 28 Feb 2024 15:22:38 +0100 Subject: [PATCH 20/23] test: improve fuzzing efficiency --- test/forge/FixedRateIrmTest.sol | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index 106f2ee0..cd355528 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -17,16 +17,14 @@ contract FixedRateIrmTest is Test { } function testSetBorrowRate(Id id, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); - vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(id, newBorrowRate); assertEq(fixedRateIrm.borrowRateStored(id), newBorrowRate); } function testSetBorrowRateEvent(Id id, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); - vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); vm.expectEmit(true, true, true, true, address(fixedRateIrm)); emit SetBorrowRate(id, newBorrowRate); @@ -48,14 +46,13 @@ contract FixedRateIrmTest is Test { } function testSetBorrowRateRateTooHigh(Id id, uint256 newBorrowRate) external { - vm.assume(newBorrowRate > fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate = bound(newBorrowRate, fixedRateIrm.MAX_BORROW_RATE() + 1, type(uint256).max); vm.expectRevert(bytes(RATE_TOO_HIGH)); fixedRateIrm.setBorrowRate(id, newBorrowRate); } function testBorrowRate(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); - vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); } @@ -68,8 +65,7 @@ contract FixedRateIrmTest is Test { function testBorrowRateView(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); - vm.assume(newBorrowRate <= fixedRateIrm.MAX_BORROW_RATE()); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); } From cb9c58e87d8c91a6fcbd1c26d9ba904866602679 Mon Sep 17 00:00:00 2001 From: MathisGD <74971347+MathisGD@users.noreply.github.com> Date: Thu, 29 Feb 2024 22:03:44 +0100 Subject: [PATCH 21/23] test: better test name Co-authored-by: Merlin Egalite <44097430+MerlinEgalite@users.noreply.github.com> Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com> --- test/forge/FixedRateIrmTest.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index cd355528..610896e7 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -45,7 +45,7 @@ contract FixedRateIrmTest is Test { fixedRateIrm.setBorrowRate(id, 0); } - function testSetBorrowRateRateTooHigh(Id id, uint256 newBorrowRate) external { + function testSetBorrowRateTooHigh(Id id, uint256 newBorrowRate) external { newBorrowRate = bound(newBorrowRate, fixedRateIrm.MAX_BORROW_RATE() + 1, type(uint256).max); vm.expectRevert(bytes(RATE_TOO_HIGH)); fixedRateIrm.setBorrowRate(id, newBorrowRate); From 038844e0a841074bf4f3b87fe42807bc3ac5ed40 Mon Sep 17 00:00:00 2001 From: MathisGD <74971347+MathisGD@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:06:21 +0100 Subject: [PATCH 22/23] docs: document rate too low issue Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com> --- src/fixed-rate-irm/FixedRateIrm.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fixed-rate-irm/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol index 1a42f4f8..24b5dee6 100644 --- a/src/fixed-rate-irm/FixedRateIrm.sol +++ b/src/fixed-rate-irm/FixedRateIrm.sol @@ -44,6 +44,7 @@ contract FixedRateIrm is IFixedRateIrm { /// @notice Sets the borrow rate for a market. /// @dev A rate can be set by anybody, but only once. /// @dev `borrowRate` reverts on rate not set, so the rate needs to be set before the market creation. + /// @dev As interest are rounded down in Morpho, for markets with a low total borrow, setting a rate too low could prevent interest from accruing if interactions are frequent. function setBorrowRate(Id id, uint256 newBorrowRate) external { require(borrowRateStored[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO); From 3e435cd27f4b2b55bbfed3a970cb909646e249eb Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 1 Mar 2024 12:07:08 +0100 Subject: [PATCH 23/23] chore: fmt --- src/fixed-rate-irm/FixedRateIrm.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fixed-rate-irm/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol index 24b5dee6..5b92d70c 100644 --- a/src/fixed-rate-irm/FixedRateIrm.sol +++ b/src/fixed-rate-irm/FixedRateIrm.sol @@ -44,7 +44,8 @@ contract FixedRateIrm is IFixedRateIrm { /// @notice Sets the borrow rate for a market. /// @dev A rate can be set by anybody, but only once. /// @dev `borrowRate` reverts on rate not set, so the rate needs to be set before the market creation. - /// @dev As interest are rounded down in Morpho, for markets with a low total borrow, setting a rate too low could prevent interest from accruing if interactions are frequent. + /// @dev As interest are rounded down in Morpho, for markets with a low total borrow, setting a rate too low could + /// prevent interest from accruing if interactions are frequent. function setBorrowRate(Id id, uint256 newBorrowRate) external { require(borrowRateStored[id] == 0, RATE_SET); require(newBorrowRate != 0, RATE_ZERO);