Connect to the most trusted lending network 

Access global liquidity at the best possible terms powered by open infrastructure that serves, not extracts. 

Deposits
$0
Loans
$0

Powered by Morpho

Enterprises connect with Morpho to power any lending or borrowing use case at scale.

Built for scale

Enterprise-grade infrastructure that
allows you to launch in weeks, not months

Embed custom earn products

Give your users access to yield on any asset and across any risk profile or compliance requirements.

Facilitate crypto backed loans

Plug into global liquidity, offer instant loans, and maintain full ownership over the product experience.

Become a leading curator

Curate unique strategies to streamline lending at any scale or sophistication.

Open by default,
secure by design

Transparent financial infrastructure. See it for yourself.

Morpho V1

  1// SPDX-License-Identifier: BUSL-1.1
  2pragma solidity 0.8.19;
  3
  4import {
  5    Id,
  6    IMorphoStaticTyping,
  7    IMorphoBase,
  8    MarketParams,
  9    Position,
 10    Market,
 11    Authorization,
 12    Signature
 13} from "./interfaces/IMorpho.sol";
 14import {
 15    IMorphoLiquidateCallback,
 16    IMorphoRepayCallback,
 17    IMorphoSupplyCallback,
 18    IMorphoSupplyCollateralCallback,
 19    IMorphoFlashLoanCallback
 20} from "./interfaces/IMorphoCallbacks.sol";
 21import {IIrm} from "./interfaces/IIrm.sol";
 22import {IERC20} from "./interfaces/IERC20.sol";
 23import {IOracle} from "./interfaces/IOracle.sol";
 24
 25import "./libraries/ConstantsLib.sol";
 26import {UtilsLib} from "./libraries/UtilsLib.sol";
 27import {EventsLib} from "./libraries/EventsLib.sol";
 28import {ErrorsLib} from "./libraries/ErrorsLib.sol";
 29import {MathLib, WAD} from "./libraries/MathLib.sol";
 30import {SharesMathLib} from "./libraries/SharesMathLib.sol";
 31import {MarketParamsLib} from "./libraries/MarketParamsLib.sol";
 32import {SafeTransferLib} from "./libraries/SafeTransferLib.sol";
 33
 34/// @title Morpho
 35/// @author Morpho Labs
 36/// @custom:contact security@morpho.org
 37/// @notice The Morpho contract.
 38contract Morpho is IMorphoStaticTyping {
 39    using MathLib for uint128;
 40    using MathLib for uint256;
 41    using UtilsLib for uint256;
 42    using SharesMathLib for uint256;
 43    using SafeTransferLib for IERC20;
 44    using MarketParamsLib for MarketParams;
 45
 46    /* IMMUTABLES */
 47
 48    /// @inheritdoc IMorphoBase
 49    bytes32 public immutable DOMAIN_SEPARATOR;
 50
 51    /* STORAGE */
 52
 53    /// @inheritdoc IMorphoBase
 54    address public owner;
 55    /// @inheritdoc IMorphoBase
 56    address public feeRecipient;
 57    /// @inheritdoc IMorphoStaticTyping
 58    mapping(Id => mapping(address => Position)) public position;
 59    /// @inheritdoc IMorphoStaticTyping
 60    mapping(Id => Market) public market;
 61    /// @inheritdoc IMorphoBase
 62    mapping(address => bool) public isIrmEnabled;
 63    /// @inheritdoc IMorphoBase
 64    mapping(uint256 => bool) public isLltvEnabled;
 65    /// @inheritdoc IMorphoBase
 66    mapping(address => mapping(address => bool)) public isAuthorized;
 67    /// @inheritdoc IMorphoBase
 68    mapping(address => uint256) public nonce;
 69    /// @inheritdoc IMorphoStaticTyping
 70    mapping(Id => MarketParams) public idToMarketParams;
 71
 72    /* CONSTRUCTOR */
 73
 74    /// @param newOwner The new owner of the contract.
 75    constructor(address newOwner) {
 76        require(newOwner != address(0), ErrorsLib.ZERO_ADDRESS);
 77
 78        DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this)));
 79        owner = newOwner;
 80
 81        emit EventsLib.SetOwner(newOwner);
 82    }
 83
 84    /* MODIFIERS */
 85
 86    /// @dev Reverts if the caller is not the owner.
 87    modifier onlyOwner() {
 88        require(msg.sender == owner, ErrorsLib.NOT_OWNER);
 89        _;
 90    }
 91
 92    /* ONLY OWNER FUNCTIONS */
 93
 94    /// @inheritdoc IMorphoBase
 95    function setOwner(address newOwner) external onlyOwner {
 96        require(newOwner != owner, ErrorsLib.ALREADY_SET);
 97
 98        owner = newOwner;
 99
100        emit EventsLib.SetOwner(newOwner);
101    }
102
103    /// @inheritdoc IMorphoBase
104    function enableIrm(address irm) external onlyOwner {
105        require(!isIrmEnabled[irm], ErrorsLib.ALREADY_SET);
106
107        isIrmEnabled[irm] = true;
108
109        emit EventsLib.EnableIrm(irm);
110    }
111
112    /// @inheritdoc IMorphoBase
113    function enableLltv(uint256 lltv) external onlyOwner {
114        require(!isLltvEnabled[lltv], ErrorsLib.ALREADY_SET);
115        require(lltv < WAD, ErrorsLib.MAX_LLTV_EXCEEDED);
116
117        isLltvEnabled[lltv] = true;
118
119        emit EventsLib.EnableLltv(lltv);
120    }
121
122    /// @inheritdoc IMorphoBase
123    function setFee(MarketParams memory marketParams, uint256 newFee) external onlyOwner {
124        Id id = marketParams.id();
125        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
126        require(newFee != market[id].fee, ErrorsLib.ALREADY_SET);
127        require(newFee <= MAX_FEE, ErrorsLib.MAX_FEE_EXCEEDED);
128
129        // Accrue interest using the previous fee set before changing it.
130        _accrueInterest(marketParams, id);
131
132        // Safe "unchecked" cast.
133        market[id].fee = uint128(newFee);
134
135        emit EventsLib.SetFee(id, newFee);
136    }
137
138    /// @inheritdoc IMorphoBase
139    function setFeeRecipient(address newFeeRecipient) external onlyOwner {
140        require(newFeeRecipient != feeRecipient, ErrorsLib.ALREADY_SET);
141
142        feeRecipient = newFeeRecipient;
143
144        emit EventsLib.SetFeeRecipient(newFeeRecipient);
145    }
146
147    /* MARKET CREATION */
148
149    /// @inheritdoc IMorphoBase
150    function createMarket(MarketParams memory marketParams) external {
151        Id id = marketParams.id();
152        require(isIrmEnabled[marketParams.irm], ErrorsLib.IRM_NOT_ENABLED);
153        require(isLltvEnabled[marketParams.lltv], ErrorsLib.LLTV_NOT_ENABLED);
154        require(market[id].lastUpdate == 0, ErrorsLib.MARKET_ALREADY_CREATED);
155
156        // Safe "unchecked" cast.
157        market[id].lastUpdate = uint128(block.timestamp);
158        idToMarketParams[id] = marketParams;
159
160        emit EventsLib.CreateMarket(id, marketParams);
161
162        // Call to initialize the IRM in case it is stateful.
163        if (marketParams.irm != address(0)) IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
164    }
165
166    /* SUPPLY MANAGEMENT */
167
168    /// @inheritdoc IMorphoBase
169    function supply(
170        MarketParams memory marketParams,
171        uint256 assets,
172        uint256 shares,
173        address onBehalf,
174        bytes calldata data
175    ) external returns (uint256, uint256) {
176        Id id = marketParams.id();
177        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
178        require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
179        require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
180
181        _accrueInterest(marketParams, id);
182
183        if (assets > 0) shares = assets.toSharesDown(market[id].totalSupplyAssets, market[id].totalSupplyShares);
184        else assets = shares.toAssetsUp(market[id].totalSupplyAssets, market[id].totalSupplyShares);
185
186        position[id][onBehalf].supplyShares += shares;
187        market[id].totalSupplyShares += shares.toUint128();
188        market[id].totalSupplyAssets += assets.toUint128();
189
190        emit EventsLib.Supply(id, msg.sender, onBehalf, assets, shares);
191
192        if (data.length > 0) IMorphoSupplyCallback(msg.sender).onMorphoSupply(assets, data);
193
194        IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), assets);
195
196        return (assets, shares);
197    }
198
199    /// @inheritdoc IMorphoBase
200    function withdraw(
201        MarketParams memory marketParams,
202        uint256 assets,
203        uint256 shares,
204        address onBehalf,
205        address receiver
206    ) external returns (uint256, uint256) {
207        Id id = marketParams.id();
208        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
209        require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
210        require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
211        // No need to verify that onBehalf != address(0) thanks to the following authorization check.
212        require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);
213
214        _accrueInterest(marketParams, id);
215
216        if (assets > 0) shares = assets.toSharesUp(market[id].totalSupplyAssets, market[id].totalSupplyShares);
217        else assets = shares.toAssetsDown(market[id].totalSupplyAssets, market[id].totalSupplyShares);
218
219        position[id][onBehalf].supplyShares -= shares;
220        market[id].totalSupplyShares -= shares.toUint128();
221        market[id].totalSupplyAssets -= assets.toUint128();
222
223        require(market[id].totalBorrowAssets <= market[id].totalSupplyAssets, ErrorsLib.INSUFFICIENT_LIQUIDITY);
224
225        emit EventsLib.Withdraw(id, msg.sender, onBehalf, receiver, assets, shares);
226
227        IERC20(marketParams.loanToken).safeTransfer(receiver, assets);
228
229        return (assets, shares);
230    }
231
232    /* BORROW MANAGEMENT */
233
234    /// @inheritdoc IMorphoBase
235    function borrow(
236        MarketParams memory marketParams,
237        uint256 assets,
238        uint256 shares,
239        address onBehalf,
240        address receiver
241    ) external returns (uint256, uint256) {
242        Id id = marketParams.id();
243        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
244        require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
245        require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
246        // No need to verify that onBehalf != address(0) thanks to the following authorization check.
247        require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);
248
249        _accrueInterest(marketParams, id);
250
251        if (assets > 0) shares = assets.toSharesUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
252        else assets = shares.toAssetsDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
253
254        position[id][onBehalf].borrowShares += shares.toUint128();
255        market[id].totalBorrowShares += shares.toUint128();
256        market[id].totalBorrowAssets += assets.toUint128();
257
258        require(_isHealthy(marketParams, id, onBehalf), ErrorsLib.INSUFFICIENT_COLLATERAL);
259        require(market[id].totalBorrowAssets <= market[id].totalSupplyAssets, ErrorsLib.INSUFFICIENT_LIQUIDITY);
260
261        emit EventsLib.Borrow(id, msg.sender, onBehalf, receiver, assets, shares);
262
263        IERC20(marketParams.loanToken).safeTransfer(receiver, assets);
264
265        return (assets, shares);
266    }
267
268    /// @inheritdoc IMorphoBase
269    function repay(
270        MarketParams memory marketParams,
271        uint256 assets,
272        uint256 shares,
273        address onBehalf,
274        bytes calldata data
275    ) external returns (uint256, uint256) {
276        Id id = marketParams.id();
277        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
278        require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
279        require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
280
281        _accrueInterest(marketParams, id);
282
283        if (assets > 0) shares = assets.toSharesDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
284        else assets = shares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
285
286        position[id][onBehalf].borrowShares -= shares.toUint128();
287        market[id].totalBorrowShares -= shares.toUint128();
288        market[id].totalBorrowAssets = UtilsLib.zeroFloorSub(market[id].totalBorrowAssets, assets).toUint128();
289
290        // `assets` may be greater than `totalBorrowAssets` by 1.
291        emit EventsLib.Repay(id, msg.sender, onBehalf, assets, shares);
292
293        if (data.length > 0) IMorphoRepayCallback(msg.sender).onMorphoRepay(assets, data);
294
295        IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), assets);
296
297        return (assets, shares);
298    }
299
300    /* COLLATERAL MANAGEMENT */
301
302    /// @inheritdoc IMorphoBase
303    function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
304        external
305    {
306        Id id = marketParams.id();
307        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
308        require(assets != 0, ErrorsLib.ZERO_ASSETS);
309        require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
310
311        // Don't accrue interest because it's not required and it saves gas.
312
313        position[id][onBehalf].collateral += assets.toUint128();
314
315        emit EventsLib.SupplyCollateral(id, msg.sender, onBehalf, assets);
316
317        if (data.length > 0) IMorphoSupplyCollateralCallback(msg.sender).onMorphoSupplyCollateral(assets, data);
318
319        IERC20(marketParams.collateralToken).safeTransferFrom(msg.sender, address(this), assets);
320    }
321
322    /// @inheritdoc IMorphoBase
323    function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
324        external
325    {
326        Id id = marketParams.id();
327        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
328        require(assets != 0, ErrorsLib.ZERO_ASSETS);
329        require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
330        // No need to verify that onBehalf != address(0) thanks to the following authorization check.
331        require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);
332
333        _accrueInterest(marketParams, id);
334
335        position[id][onBehalf].collateral -= assets.toUint128();
336
337        require(_isHealthy(marketParams, id, onBehalf), ErrorsLib.INSUFFICIENT_COLLATERAL);
338
339        emit EventsLib.WithdrawCollateral(id, msg.sender, onBehalf, receiver, assets);
340
341        IERC20(marketParams.collateralToken).safeTransfer(receiver, assets);
342    }
343
344    /* LIQUIDATION */
345
346    /// @inheritdoc IMorphoBase
347    function liquidate(
348        MarketParams memory marketParams,
349        address borrower,
350        uint256 seizedAssets,
351        uint256 repaidShares,
352        bytes calldata data
353    ) external returns (uint256, uint256) {
354        Id id = marketParams.id();
355        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
356        require(UtilsLib.exactlyOneZero(seizedAssets, repaidShares), ErrorsLib.INCONSISTENT_INPUT);
357
358        _accrueInterest(marketParams, id);
359
360        {
361            uint256 collateralPrice = IOracle(marketParams.oracle).price();
362
363            require(!_isHealthy(marketParams, id, borrower, collateralPrice), ErrorsLib.HEALTHY_POSITION);
364
365            // The liquidation incentive factor is min(maxLiquidationIncentiveFactor, 1/(1 - cursor*(1 - lltv))).
366            uint256 liquidationIncentiveFactor = UtilsLib.min(
367                MAX_LIQUIDATION_INCENTIVE_FACTOR,
368                WAD.wDivDown(WAD - LIQUIDATION_CURSOR.wMulDown(WAD - marketParams.lltv))
369            );
370
371            if (seizedAssets > 0) {
372                uint256 seizedAssetsQuoted = seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE);
373
374                repaidShares = seizedAssetsQuoted.wDivUp(liquidationIncentiveFactor).toSharesUp(
375                    market[id].totalBorrowAssets, market[id].totalBorrowShares
376                );
377            } else {
378                seizedAssets = repaidShares.toAssetsDown(market[id].totalBorrowAssets, market[id].totalBorrowShares)
379                    .wMulDown(liquidationIncentiveFactor).mulDivDown(ORACLE_PRICE_SCALE, collateralPrice);
380            }
381        }
382        uint256 repaidAssets = repaidShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
383
384        position[id][borrower].borrowShares -= repaidShares.toUint128();
385        market[id].totalBorrowShares -= repaidShares.toUint128();
386        market[id].totalBorrowAssets = UtilsLib.zeroFloorSub(market[id].totalBorrowAssets, repaidAssets).toUint128();
387
388        position[id][borrower].collateral -= seizedAssets.toUint128();
389
390        uint256 badDebtShares;
391        uint256 badDebtAssets;
392        if (position[id][borrower].collateral == 0) {
393            badDebtShares = position[id][borrower].borrowShares;
394            badDebtAssets = UtilsLib.min(
395                market[id].totalBorrowAssets,
396                badDebtShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares)
397            );
398
399            market[id].totalBorrowAssets -= badDebtAssets.toUint128();
400            market[id].totalSupplyAssets -= badDebtAssets.toUint128();
401            market[id].totalBorrowShares -= badDebtShares.toUint128();
402            position[id][borrower].borrowShares = 0;
403        }
404
405        // `repaidAssets` may be greater than `totalBorrowAssets` by 1.
406        emit EventsLib.Liquidate(
407            id, msg.sender, borrower, repaidAssets, repaidShares, seizedAssets, badDebtAssets, badDebtShares
408        );
409
410        IERC20(marketParams.collateralToken).safeTransfer(msg.sender, seizedAssets);
411
412        if (data.length > 0) IMorphoLiquidateCallback(msg.sender).onMorphoLiquidate(repaidAssets, data);
413
414        IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), repaidAssets);
415
416        return (seizedAssets, repaidAssets);
417    }
418
419    /* FLASH LOANS */
420
421    /// @inheritdoc IMorphoBase
422    function flashLoan(address token, uint256 assets, bytes calldata data) external {
423        require(assets != 0, ErrorsLib.ZERO_ASSETS);
424
425        emit EventsLib.FlashLoan(msg.sender, token, assets);
426
427        IERC20(token).safeTransfer(msg.sender, assets);
428
429        IMorphoFlashLoanCallback(msg.sender).onMorphoFlashLoan(assets, data);
430
431        IERC20(token).safeTransferFrom(msg.sender, address(this), assets);
432    }
433
434    /* AUTHORIZATION */
435
436    /// @inheritdoc IMorphoBase
437    function setAuthorization(address authorized, bool newIsAuthorized) external {
438        require(newIsAuthorized != isAuthorized[msg.sender][authorized], ErrorsLib.ALREADY_SET);
439
440        isAuthorized[msg.sender][authorized] = newIsAuthorized;
441
442        emit EventsLib.SetAuthorization(msg.sender, msg.sender, authorized, newIsAuthorized);
443    }
444
445    /// @inheritdoc IMorphoBase
446    function setAuthorizationWithSig(Authorization memory authorization, Signature calldata signature) external {
447        /// Do not check whether authorization is already set because the nonce increment is a desired side effect.
448        require(block.timestamp <= authorization.deadline, ErrorsLib.SIGNATURE_EXPIRED);
449        require(authorization.nonce == nonce[authorization.authorizer]++, ErrorsLib.INVALID_NONCE);
450
451        bytes32 hashStruct = keccak256(abi.encode(AUTHORIZATION_TYPEHASH, authorization));
452        bytes32 digest = keccak256(bytes.concat("\x19\x01", DOMAIN_SEPARATOR, hashStruct));
453        address signatory = ecrecover(digest, signature.v, signature.r, signature.s);
454
455        require(signatory != address(0) && authorization.authorizer == signatory, ErrorsLib.INVALID_SIGNATURE);
456
457        emit EventsLib.IncrementNonce(msg.sender, authorization.authorizer, authorization.nonce);
458
459        isAuthorized[authorization.authorizer][authorization.authorized] = authorization.isAuthorized;
460
461        emit EventsLib.SetAuthorization(
462            msg.sender, authorization.authorizer, authorization.authorized, authorization.isAuthorized
463        );
464    }
465
466    /// @dev Returns whether the sender is authorized to manage `onBehalf`'s positions.
467    function _isSenderAuthorized(address onBehalf) internal view returns (bool) {
468        return msg.sender == onBehalf || isAuthorized[onBehalf][msg.sender];
469    }
470
471    /* INTEREST MANAGEMENT */
472
473    /// @inheritdoc IMorphoBase
474    function accrueInterest(MarketParams memory marketParams) external {
475        Id id = marketParams.id();
476        require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
477
478        _accrueInterest(marketParams, id);
479    }
480
481    /// @dev Accrues interest for the given market `marketParams`.
482    /// @dev Assumes that the inputs `marketParams` and `id` match.
483    function _accrueInterest(MarketParams memory marketParams, Id id) internal {
484        uint256 elapsed = block.timestamp - market[id].lastUpdate;
485        if (elapsed == 0) return;
486
487        if (marketParams.irm != address(0)) {
488            uint256 borrowRate = IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
489            uint256 interest = market[id].totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
490            market[id].totalBorrowAssets += interest.toUint128();
491            market[id].totalSupplyAssets += interest.toUint128();
492
493            uint256 feeShares;
494            if (market[id].fee != 0) {
495                uint256 feeAmount = interest.wMulDown(market[id].fee);
496                // The fee amount is subtracted from the total supply in this calculation to compensate for the fact
497                // that total supply is already increased by the full interest (including the fee amount).
498                feeShares =
499                    feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
500                position[id][feeRecipient].supplyShares += feeShares;
501                market[id].totalSupplyShares += feeShares.toUint128();
502            }
503
504            emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares);
505        }
506
507        // Safe "unchecked" cast.
508        market[id].lastUpdate = uint128(block.timestamp);
509    }
510
511    /* HEALTH CHECK */
512
513    /// @dev Returns whether the position of `borrower` in the given market `marketParams` is healthy.
514    /// @dev Assumes that the inputs `marketParams` and `id` match.
515    function _isHealthy(MarketParams memory marketParams, Id id, address borrower) internal view returns (bool) {
516        if (position[id][borrower].borrowShares == 0) return true;
517
518        uint256 collateralPrice = IOracle(marketParams.oracle).price();
519
520        return _isHealthy(marketParams, id, borrower, collateralPrice);
521    }
522
523    /// @dev Returns whether the position of `borrower` in the given market `marketParams` with the given
524    /// `collateralPrice` is healthy.
525    /// @dev Assumes that the inputs `marketParams` and `id` match.
526    /// @dev Rounds in favor of the protocol, so one might not be able to borrow exactly `maxBorrow` but one unit less.
527    function _isHealthy(MarketParams memory marketParams, Id id, address borrower, uint256 collateralPrice)
528        internal
529        view
530        returns (bool)
531    {
532        uint256 borrowed = uint256(position[id][borrower].borrowShares).toAssetsUp(
533            market[id].totalBorrowAssets, market[id].totalBorrowShares
534        );
535        uint256 maxBorrow = uint256(position[id][borrower].collateral).mulDivDown(collateralPrice, ORACLE_PRICE_SCALE)
536            .wMulDown(marketParams.lltv);
537
538        return maxBorrow >= borrowed;
539    }
540
541    /* STORAGE VIEW */
542
543    /// @inheritdoc IMorphoBase
544    function extSloads(bytes32[] calldata slots) external view returns (bytes32[] memory res) {
545        uint256 nSlots = slots.length;
546
547        res = new bytes32[](nSlots);
548
549        for (uint256 i; i < nSlots;) {
550            bytes32 slot = slots[i++];
551
552            assembly ("memory-safe") {
553                mstore(add(res, mul(i, 32)), sload(slot))
554            }
555        }
556    }
557}
GithubGithub

Formal Verification

Built to be formally verified

Read MoreRead More

Web3Soc

Institutional-grade

Read MoreRead More

Backed by leaders reshaping finance

View More
40+ more

Unlock the potential
of onchain lending

Morpho | The most trusted lending network