diff --git a/src/fixed-rate-irm/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol index b33a59c..bb1fadb 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 @@ -22,6 +24,11 @@ string constant RATE_ZERO = "rate zero"; contract FixedRateIrm is IFixedRateIrm { using MarketParamsLib for MarketParams; + /* CONSTANTS */ + + /// @inheritdoc IFixedRateIrm + uint256 public constant MAX_BORROW_RATE = 8.0 ether / uint256(365 days); + /* STORAGE */ /// @inheritdoc IFixedRateIrm @@ -33,6 +40,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/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol index 78202db..e0e6926 100644 --- a/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol +++ b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol @@ -15,11 +15,16 @@ interface IFixedRateIrm is IIrm { /* EXTERNAL */ + /// @notice Max settable borrow rate (800%). + function MAX_BORROW_RATE() external returns (uint256); + /// @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. + /// @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; } diff --git a/test/forge/FixedRateIrmTest.sol b/test/forge/FixedRateIrmTest.sol index 730360a..610896e 100644 --- a/test/forge/FixedRateIrmTest.sol +++ b/test/forge/FixedRateIrmTest.sol @@ -17,14 +17,14 @@ contract FixedRateIrmTest is Test { } function testSetBorrowRate(Id id, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); + 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); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); vm.expectEmit(true, true, true, true, address(fixedRateIrm)); emit SetBorrowRate(id, newBorrowRate); @@ -32,8 +32,9 @@ contract FixedRateIrmTest is Test { } function testSetBorrowRateAlreadySet(Id id, uint256 newBorrowRate1, uint256 newBorrowRate2) external { - vm.assume(newBorrowRate1 != 0); - vm.assume(newBorrowRate2 != 0); + 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); @@ -44,8 +45,14 @@ contract FixedRateIrmTest is Test { fixedRateIrm.setBorrowRate(id, 0); } + 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); + } + function testBorrowRate(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); } @@ -58,7 +65,7 @@ contract FixedRateIrmTest is Test { function testBorrowRateView(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) external { - vm.assume(newBorrowRate != 0); + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); }