diff --git a/foundry.toml b/foundry.toml index f09ac506..e56430d1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,12 +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 diff --git a/hardhat.config.ts b/hardhat.config.ts index 7ef6a904..e5227301 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -30,7 +30,17 @@ const config: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 200, + runs: 999999, + }, + viaIR: true, + }, + }, + { + version: "0.8.24", + settings: { + optimizer: { + enabled: true, + runs: 999999, }, viaIR: true, }, 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/fixed-rate-irm/FixedRateIrm.sol b/src/fixed-rate-irm/FixedRateIrm.sol new file mode 100644 index 00000000..bb1fadbb --- /dev/null +++ b/src/fixed-rate-irm/FixedRateIrm.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +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"; + +/* 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"; +/// @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 +/// @custom:contact security@morpho.org +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 + mapping(Id => uint256) public borrowRateStored; + + /* SETTER */ + + /// @inheritdoc 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; + + emit SetBorrowRate(id, newBorrowRate); + } + + /* BORROW RATES */ + + /// @inheritdoc IIrm + function borrowRateView(MarketParams memory marketParams, Market memory) external view returns (uint256) { + uint256 borrowRateCached = borrowRateStored[marketParams.id()]; + require(borrowRateCached != 0, RATE_NOT_SET); + return borrowRateCached; + } + + /// @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 = borrowRateStored[marketParams.id()]; + require(borrowRateCached != 0, RATE_NOT_SET); + return borrowRateCached; + } +} diff --git a/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol new file mode 100644 index 00000000..e0e6926c --- /dev/null +++ b/src/fixed-rate-irm/interfaces/IFixedRateIrm.sol @@ -0,0 +1,30 @@ +// 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 IFixedRateIrm +/// @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 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/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 new file mode 100644 index 00000000..610896e7 --- /dev/null +++ b/test/forge/FixedRateIrmTest.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../../src/fixed-rate-irm/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 { + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); + + fixedRateIrm.setBorrowRate(id, newBorrowRate); + assertEq(fixedRateIrm.borrowRateStored(id), newBorrowRate); + } + + function testSetBorrowRateEvent(Id id, uint256 newBorrowRate) external { + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); + + 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 { + 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); + } + + function testSetBorrowRateRateZero(Id id) external { + vm.expectRevert(bytes(RATE_ZERO)); + 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 { + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); + fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); + assertEq(fixedRateIrm.borrowRate(marketParams, market), newBorrowRate); + } + + function testBorrowRateRateNotSet(MarketParams memory marketParams, Market memory market) external { + vm.expectRevert(bytes(RATE_NOT_SET)); + fixedRateIrm.borrowRate(marketParams, market); + } + + function testBorrowRateView(MarketParams memory marketParams, Market memory market, uint256 newBorrowRate) + external + { + newBorrowRate = bound(newBorrowRate, 1, fixedRateIrm.MAX_BORROW_RATE()); + fixedRateIrm.setBorrowRate(marketParams.id(), newBorrowRate); + assertEq(fixedRateIrm.borrowRateView(marketParams, market), newBorrowRate); + } + + function testBorrowRateViewRateNotSet(MarketParams memory marketParams, Market memory market) external { + vm.expectRevert(bytes(RATE_NOT_SET)); + fixedRateIrm.borrowRateView(marketParams, market); + } +} 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";