Skip to content

Commit 184fc24

Browse files
Merge pull request #640 from morpho-org/feat/skip-irm-0
feat(irm): skip address(0)
2 parents d98b26c + 5ae9ff2 commit 184fc24

File tree

9 files changed

+107
-48
lines changed

9 files changed

+107
-48
lines changed

src/Morpho.sol

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ contract Morpho is IMorphoStaticTyping {
160160
emit EventsLib.CreateMarket(id, marketParams);
161161

162162
// Call to initialize the IRM in case it is stateful.
163-
IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
163+
if (marketParams.irm != address(0)) IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
164164
}
165165

166166
/* SUPPLY MANAGEMENT */
@@ -483,25 +483,27 @@ contract Morpho is IMorphoStaticTyping {
483483
/// @dev Assumes that the inputs `marketParams` and `id` match.
484484
function _accrueInterest(MarketParams memory marketParams, Id id) internal {
485485
uint256 elapsed = block.timestamp - market[id].lastUpdate;
486-
487486
if (elapsed == 0) return;
488487

489-
uint256 borrowRate = IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
490-
uint256 interest = market[id].totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
491-
market[id].totalBorrowAssets += interest.toUint128();
492-
market[id].totalSupplyAssets += interest.toUint128();
493-
494-
uint256 feeShares;
495-
if (market[id].fee != 0) {
496-
uint256 feeAmount = interest.wMulDown(market[id].fee);
497-
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
498-
// that total supply is already increased by the full interest (including the fee amount).
499-
feeShares = feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
500-
position[id][feeRecipient].supplyShares += feeShares;
501-
market[id].totalSupplyShares += feeShares.toUint128();
502-
}
488+
if (marketParams.irm != address(0)) {
489+
uint256 borrowRate = IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
490+
uint256 interest = market[id].totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
491+
market[id].totalBorrowAssets += interest.toUint128();
492+
market[id].totalSupplyAssets += interest.toUint128();
493+
494+
uint256 feeShares;
495+
if (market[id].fee != 0) {
496+
uint256 feeAmount = interest.wMulDown(market[id].fee);
497+
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
498+
// that total supply is already increased by the full interest (including the fee amount).
499+
feeShares =
500+
feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
501+
position[id][feeRecipient].supplyShares += feeShares;
502+
market[id].totalSupplyShares += feeShares.toUint128();
503+
}
503504

504-
emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares);
505+
emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares);
506+
}
505507

506508
// Safe "unchecked" cast.
507509
market[id].lastUpdate = uint128(block.timestamp);

src/libraries/periphery/MorphoBalancesLib.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,12 @@ library MorphoBalancesLib {
3636
returns (uint256, uint256, uint256, uint256)
3737
{
3838
Id id = marketParams.id();
39-
4039
Market memory market = morpho.market(id);
4140

4241
uint256 elapsed = block.timestamp - market.lastUpdate;
4342

44-
// Skipped if elapsed == 0 or if totalBorrowAssets == 0 because interest would be null.
45-
if (elapsed != 0 && market.totalBorrowAssets != 0) {
43+
// Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
44+
if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
4645
uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
4746
uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
4847
market.totalBorrowAssets += interest.toUint128();

test/forge/BaseTest.sol

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ contract BaseTest is Test {
8282
irm = new IrmMock();
8383

8484
vm.startPrank(OWNER);
85+
morpho.enableIrm(address(0));
8586
morpho.enableIrm(address(irm));
87+
morpho.enableLltv(0);
8688
morpho.setFeeRecipient(FEE_RECIPIENT);
8789
vm.stopPrank();
8890

@@ -133,7 +135,7 @@ contract BaseTest is Test {
133135
}
134136

135137
/// @dev Bounds the fuzzing input to a realistic number of blocks.
136-
function _boundBlocks(uint256 blocks) internal view returns (uint256) {
138+
function _boundBlocks(uint256 blocks) internal pure returns (uint256) {
137139
return bound(blocks, 1, type(uint32).max);
138140
}
139141

@@ -198,7 +200,7 @@ contract BaseTest is Test {
198200
return (amountCollateral, amountBorrowed, priceCollateral);
199201
}
200202

201-
function _boundTestLltv(uint256 lltv) internal view returns (uint256) {
203+
function _boundTestLltv(uint256 lltv) internal pure returns (uint256) {
202204
return bound(lltv, MIN_TEST_LLTV, MAX_TEST_LLTV);
203205
}
204206

@@ -382,7 +384,7 @@ contract BaseTest is Test {
382384
return Math.min(MAX_LIQUIDATION_INCENTIVE_FACTOR, WAD.wDivDown(WAD - LIQUIDATION_CURSOR.wMulDown(WAD - lltv)));
383385
}
384386

385-
function _boundValidLltv(uint256 lltv) internal view returns (uint256) {
387+
function _boundValidLltv(uint256 lltv) internal pure returns (uint256) {
386388
return bound(lltv, 0, WAD - 1);
387389
}
388390

test/forge/integration/AccrueInterestIntegrationTest.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ contract AccrueInterestIntegrationTest is BaseTest {
1515
morpho.accrueInterest(marketParamsFuzz);
1616
}
1717

18+
function testAccrueInterestIrmZero(MarketParams memory marketParamsFuzz, uint256 blocks) public {
19+
marketParamsFuzz.irm = address(0);
20+
marketParamsFuzz.lltv = 0;
21+
blocks = _boundBlocks(blocks);
22+
23+
morpho.createMarket(marketParamsFuzz);
24+
25+
_forward(blocks);
26+
27+
morpho.accrueInterest(marketParamsFuzz);
28+
}
29+
1830
function testAccrueInterestNoTimeElapsed(uint256 amountSupplied, uint256 amountBorrowed) public {
1931
uint256 collateralPrice = oracle.price();
2032
uint256 amountCollateral;

test/forge/integration/CreateMarketIntegrationTest.sol

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,56 @@ contract CreateMarketIntegrationTest is BaseTest {
99
using MarketParamsLib for MarketParams;
1010

1111
function testCreateMarketWithNotEnabledIrmAndNotEnabledLltv(MarketParams memory marketParamsFuzz) public {
12-
vm.assume(marketParamsFuzz.irm != address(irm) && marketParamsFuzz.lltv != marketParams.lltv);
12+
vm.assume(!morpho.isIrmEnabled(marketParamsFuzz.irm) && !morpho.isLltvEnabled(marketParamsFuzz.lltv));
1313

14-
vm.prank(OWNER);
1514
vm.expectRevert(bytes(ErrorsLib.IRM_NOT_ENABLED));
15+
vm.prank(OWNER);
1616
morpho.createMarket(marketParamsFuzz);
1717
}
1818

1919
function testCreateMarketWithNotEnabledIrmAndEnabledLltv(MarketParams memory marketParamsFuzz) public {
20-
vm.assume(marketParamsFuzz.irm != address(irm));
20+
vm.assume(!morpho.isIrmEnabled(marketParamsFuzz.irm));
21+
2122
marketParamsFuzz.lltv = _boundValidLltv(marketParamsFuzz.lltv);
2223

2324
vm.startPrank(OWNER);
24-
if (marketParamsFuzz.lltv != marketParams.lltv) morpho.enableLltv(marketParamsFuzz.lltv);
25+
if (!morpho.isLltvEnabled(marketParamsFuzz.lltv)) morpho.enableLltv(marketParamsFuzz.lltv);
26+
vm.stopPrank();
2527

2628
vm.expectRevert(bytes(ErrorsLib.IRM_NOT_ENABLED));
29+
vm.prank(OWNER);
2730
morpho.createMarket(marketParamsFuzz);
28-
vm.stopPrank();
2931
}
3032

3133
function testCreateMarketWithEnabledIrmAndNotEnabledLltv(MarketParams memory marketParamsFuzz) public {
32-
vm.assume(marketParamsFuzz.lltv != marketParams.lltv);
34+
vm.assume(!morpho.isLltvEnabled(marketParamsFuzz.lltv));
3335

3436
vm.startPrank(OWNER);
35-
if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm);
37+
if (!morpho.isIrmEnabled(marketParamsFuzz.irm)) morpho.enableIrm(marketParamsFuzz.irm);
38+
vm.stopPrank();
3639

3740
vm.expectRevert(bytes(ErrorsLib.LLTV_NOT_ENABLED));
41+
vm.prank(OWNER);
3842
morpho.createMarket(marketParamsFuzz);
39-
vm.stopPrank();
4043
}
4144

4245
function testCreateMarketWithEnabledIrmAndLltv(MarketParams memory marketParamsFuzz) public {
4346
marketParamsFuzz.lltv = _boundValidLltv(marketParamsFuzz.lltv);
4447
Id marketParamsFuzzId = marketParamsFuzz.id();
4548

4649
vm.startPrank(OWNER);
47-
if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm);
48-
if (marketParamsFuzz.lltv != marketParams.lltv) morpho.enableLltv(marketParamsFuzz.lltv);
50+
if (!morpho.isIrmEnabled(marketParamsFuzz.irm)) morpho.enableIrm(marketParamsFuzz.irm);
51+
if (!morpho.isLltvEnabled(marketParamsFuzz.lltv)) morpho.enableLltv(marketParamsFuzz.lltv);
52+
vm.stopPrank();
4953

50-
vm.mockCall(marketParamsFuzz.irm, abi.encodeWithSelector(IIrm.borrowRate.selector), abi.encode(0));
54+
if (marketParamsFuzz.irm != address(0)) {
55+
vm.mockCall(marketParamsFuzz.irm, abi.encodeWithSelector(IIrm.borrowRate.selector), abi.encode(0));
56+
}
5157

5258
vm.expectEmit(true, true, true, true, address(morpho));
5359
emit EventsLib.CreateMarket(marketParamsFuzz.id(), marketParamsFuzz);
60+
vm.prank(OWNER);
5461
morpho.createMarket(marketParamsFuzz);
55-
vm.stopPrank();
5662

5763
assertEq(morpho.lastUpdate(marketParamsFuzzId), block.timestamp, "lastUpdate != block.timestamp");
5864
assertEq(morpho.totalSupplyAssets(marketParamsFuzzId), 0, "totalSupplyAssets != 0");
@@ -66,30 +72,37 @@ contract CreateMarketIntegrationTest is BaseTest {
6672
marketParamsFuzz.lltv = _boundValidLltv(marketParamsFuzz.lltv);
6773

6874
vm.startPrank(OWNER);
69-
if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm);
70-
if (marketParamsFuzz.lltv != marketParams.lltv) morpho.enableLltv(marketParamsFuzz.lltv);
75+
if (!morpho.isIrmEnabled(marketParamsFuzz.irm)) morpho.enableIrm(marketParamsFuzz.irm);
76+
if (!morpho.isLltvEnabled(marketParamsFuzz.lltv)) morpho.enableLltv(marketParamsFuzz.lltv);
77+
vm.stopPrank();
7178

72-
vm.mockCall(marketParamsFuzz.irm, abi.encodeWithSelector(IIrm.borrowRate.selector), abi.encode(0));
79+
if (marketParamsFuzz.irm != address(0)) {
80+
vm.mockCall(marketParamsFuzz.irm, abi.encodeWithSelector(IIrm.borrowRate.selector), abi.encode(0));
81+
}
7382

83+
vm.prank(OWNER);
7484
morpho.createMarket(marketParamsFuzz);
7585

7686
vm.expectRevert(bytes(ErrorsLib.MARKET_ALREADY_CREATED));
87+
vm.prank(OWNER);
7788
morpho.createMarket(marketParamsFuzz);
78-
vm.stopPrank();
7989
}
8090

8191
function testIdToMarketParams(MarketParams memory marketParamsFuzz) public {
8292
marketParamsFuzz.lltv = _boundValidLltv(marketParamsFuzz.lltv);
8393
Id marketParamsFuzzId = marketParamsFuzz.id();
8494

8595
vm.startPrank(OWNER);
86-
if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm);
87-
if (marketParamsFuzz.lltv != marketParams.lltv) morpho.enableLltv(marketParamsFuzz.lltv);
96+
if (!morpho.isIrmEnabled(marketParamsFuzz.irm)) morpho.enableIrm(marketParamsFuzz.irm);
97+
if (!morpho.isLltvEnabled(marketParamsFuzz.lltv)) morpho.enableLltv(marketParamsFuzz.lltv);
98+
vm.stopPrank();
8899

89-
vm.mockCall(marketParamsFuzz.irm, abi.encodeWithSelector(IIrm.borrowRate.selector), abi.encode(0));
100+
if (marketParamsFuzz.irm != address(0)) {
101+
vm.mockCall(marketParamsFuzz.irm, abi.encodeWithSelector(IIrm.borrowRate.selector), abi.encode(0));
102+
}
90103

104+
vm.prank(OWNER);
91105
morpho.createMarket(marketParamsFuzz);
92-
vm.stopPrank();
93106

94107
MarketParams memory params = morpho.idToMarketParams(marketParamsFuzzId);
95108

test/forge/integration/OnlyOwnerIntegrationTest.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ contract OnlyOwnerIntegrationTest is BaseTest {
5959
}
6060

6161
function testEnableIrm(address irmFuzz) public {
62-
vm.assume(irmFuzz != address(irm));
62+
vm.assume(!morpho.isIrmEnabled(irmFuzz));
6363

6464
vm.prank(OWNER);
6565
vm.expectEmit(true, true, true, true, address(morpho));
@@ -94,7 +94,8 @@ contract OnlyOwnerIntegrationTest is BaseTest {
9494

9595
function testEnableLltv(uint256 lltvFuzz) public {
9696
lltvFuzz = _boundValidLltv(lltvFuzz);
97-
vm.assume(lltvFuzz != marketParams.lltv);
97+
98+
vm.assume(!morpho.isLltvEnabled(lltvFuzz));
9899

99100
vm.prank(OWNER);
100101
vm.expectEmit(true, true, true, true, address(morpho));

test/hardhat/Morpho.spec.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
22
import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time";
33
import { expect } from "chai";
4-
import { AbiCoder, MaxUint256, keccak256, toBigInt } from "ethers";
4+
import { AbiCoder, MaxUint256, ZeroAddress, keccak256, toBigInt } from "ethers";
55
import hre from "hardhat";
66
import { Morpho, OracleMock, ERC20Mock, IrmMock } from "types";
77
import { MarketParamsStruct } from "types/src/Morpho";
@@ -161,6 +161,36 @@ describe("Morpho", () => {
161161
}
162162
});
163163

164+
it("should simulate gas cost [idle]", async () => {
165+
updateMarket({
166+
loanToken: await loanToken.getAddress(),
167+
collateralToken: ZeroAddress,
168+
oracle: ZeroAddress,
169+
irm: ZeroAddress,
170+
lltv: 0,
171+
});
172+
173+
await morpho.enableLltv(0);
174+
await morpho.enableIrm(ZeroAddress);
175+
await morpho.createMarket(marketParams);
176+
177+
for (let i = 0; i < suppliers.length; ++i) {
178+
logProgress("idle", i, suppliers.length);
179+
180+
const supplier = suppliers[i];
181+
182+
let assets = BigInt.WAD * toBigInt(1 + Math.floor(random() * 100));
183+
184+
await randomForwardTimestamp();
185+
186+
await morpho.connect(supplier).supply(marketParams, assets, 0, supplier.address, "0x");
187+
188+
await randomForwardTimestamp();
189+
190+
await morpho.connect(supplier).withdraw(marketParams, assets / 2n, 0, supplier.address, supplier.address);
191+
}
192+
});
193+
164194
it("should simulate gas cost [liquidations]", async () => {
165195
for (let i = 0; i < suppliers.length; ++i) {
166196
logProgress("liquidations", i, suppliers.length);

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"target": "es6",
3+
"target": "es2020",
44
"module": "nodenext",
55
"moduleResolution": "nodenext",
66
"outDir": "dist",

0 commit comments

Comments
 (0)