Skip to content

Commit

Permalink
Merge pull request #572 from morpho-org/cantina-review
Browse files Browse the repository at this point in the history
Cantina review
  • Loading branch information
MerlinEgalite authored Nov 12, 2023
2 parents 2680d1b + 26e4627 commit 560c176
Show file tree
Hide file tree
Showing 18 changed files with 270 additions and 190 deletions.
129 changes: 74 additions & 55 deletions src/Morpho.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {Id, IMorpho, MarketParams, Position, Market, Authorization, Signature} from "./interfaces/IMorpho.sol";
import {
Id,
IMorphoStaticTyping,
IMorphoBase,
MarketParams,
Position,
Market,
Authorization,
Signature
} from "./interfaces/IMorpho.sol";
import {
IMorphoLiquidateCallback,
IMorphoRepayCallback,
Expand All @@ -26,7 +35,7 @@ import {SafeTransferLib} from "./libraries/SafeTransferLib.sol";
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice The Morpho contract.
contract Morpho is IMorpho {
contract Morpho is IMorphoStaticTyping {
using MathLib for uint128;
using MathLib for uint256;
using UtilsLib for uint256;
Expand All @@ -36,39 +45,40 @@ contract Morpho is IMorpho {

/* IMMUTABLES */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
bytes32 public immutable DOMAIN_SEPARATOR;

/* STORAGE */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
address public owner;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
address public feeRecipient;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoStaticTyping
mapping(Id => mapping(address => Position)) public position;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoStaticTyping
mapping(Id => Market) public market;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
mapping(address => bool) public isIrmEnabled;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
mapping(uint256 => bool) public isLltvEnabled;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
mapping(address => mapping(address => bool)) public isAuthorized;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
mapping(address => uint256) public nonce;
/// @inheritdoc IMorpho
/// @inheritdoc IMorphoStaticTyping
mapping(Id => MarketParams) public idToMarketParams;

/* CONSTRUCTOR */

/// @notice Constructs the contract.
/// @param newOwner The new owner of the contract.
constructor(address newOwner) {
require(newOwner != address(0), ErrorsLib.ZERO_ADDRESS);

owner = newOwner;
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this)));
owner = newOwner;

emit EventsLib.SetOwner(newOwner);
}

/* MODIFIERS */
Expand All @@ -81,7 +91,7 @@ contract Morpho is IMorpho {

/* ONLY OWNER FUNCTIONS */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function setOwner(address newOwner) external onlyOwner {
require(newOwner != owner, ErrorsLib.ALREADY_SET);

Expand All @@ -90,7 +100,7 @@ contract Morpho is IMorpho {
emit EventsLib.SetOwner(newOwner);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function enableIrm(address irm) external onlyOwner {
require(!isIrmEnabled[irm], ErrorsLib.ALREADY_SET);

Expand All @@ -99,7 +109,7 @@ contract Morpho is IMorpho {
emit EventsLib.EnableIrm(irm);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function enableLltv(uint256 lltv) external onlyOwner {
require(!isLltvEnabled[lltv], ErrorsLib.ALREADY_SET);
require(lltv < WAD, ErrorsLib.MAX_LLTV_EXCEEDED);
Expand All @@ -109,7 +119,7 @@ contract Morpho is IMorpho {
emit EventsLib.EnableLltv(lltv);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function setFee(MarketParams memory marketParams, uint256 newFee) external onlyOwner {
Id id = marketParams.id();
require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
Expand All @@ -125,7 +135,7 @@ contract Morpho is IMorpho {
emit EventsLib.SetFee(id, newFee);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function setFeeRecipient(address newFeeRecipient) external onlyOwner {
require(newFeeRecipient != feeRecipient, ErrorsLib.ALREADY_SET);

Expand All @@ -136,7 +146,7 @@ contract Morpho is IMorpho {

/* MARKET CREATION */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function createMarket(MarketParams memory marketParams) external {
Id id = marketParams.id();
require(isIrmEnabled[marketParams.irm], ErrorsLib.IRM_NOT_ENABLED);
Expand All @@ -152,7 +162,7 @@ contract Morpho is IMorpho {

/* SUPPLY MANAGEMENT */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function supply(
MarketParams memory marketParams,
uint256 assets,
Expand Down Expand Up @@ -183,7 +193,7 @@ contract Morpho is IMorpho {
return (assets, shares);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function withdraw(
MarketParams memory marketParams,
uint256 assets,
Expand All @@ -194,8 +204,8 @@ contract Morpho is IMorpho {
Id id = marketParams.id();
require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
// No need to verify that onBehalf != address(0) thanks to the authorization check.
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
// No need to verify that onBehalf != address(0) thanks to the following authorization check.
require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);

_accrueInterest(marketParams, id);
Expand All @@ -218,7 +228,7 @@ contract Morpho is IMorpho {

/* BORROW MANAGEMENT */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function borrow(
MarketParams memory marketParams,
uint256 assets,
Expand All @@ -229,8 +239,8 @@ contract Morpho is IMorpho {
Id id = marketParams.id();
require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
// No need to verify that onBehalf != address(0) thanks to the authorization check.
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
// No need to verify that onBehalf != address(0) thanks to the following authorization check.
require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);

_accrueInterest(marketParams, id);
Expand All @@ -252,7 +262,7 @@ contract Morpho is IMorpho {
return (assets, shares);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function repay(
MarketParams memory marketParams,
uint256 assets,
Expand Down Expand Up @@ -286,7 +296,7 @@ contract Morpho is IMorpho {

/* COLLATERAL MANAGEMENT */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
external
{
Expand All @@ -306,15 +316,15 @@ contract Morpho is IMorpho {
IERC20(marketParams.collateralToken).safeTransferFrom(msg.sender, address(this), assets);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
external
{
Id id = marketParams.id();
require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
require(assets != 0, ErrorsLib.ZERO_ASSETS);
// No need to verify that onBehalf != address(0) thanks to the authorization check.
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
// No need to verify that onBehalf != address(0) thanks to the following authorization check.
require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);

_accrueInterest(marketParams, id);
Expand All @@ -330,7 +340,7 @@ contract Morpho is IMorpho {

/* LIQUIDATION */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function liquidate(
MarketParams memory marketParams,
address borrower,
Expand Down Expand Up @@ -377,9 +387,13 @@ contract Morpho is IMorpho {
uint256 badDebtShares;
if (position[id][borrower].collateral == 0) {
badDebtShares = position[id][borrower].borrowShares;
uint256 badDebt = badDebtShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
market[id].totalSupplyAssets -= badDebt.toUint128();
uint256 badDebt = UtilsLib.min(
market[id].totalBorrowAssets,
badDebtShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares)
);

market[id].totalBorrowAssets -= badDebt.toUint128();
market[id].totalSupplyAssets -= badDebt.toUint128();
market[id].totalBorrowShares -= badDebtShares.toUint128();
position[id][borrower].borrowShares = 0;
}
Expand All @@ -398,7 +412,7 @@ contract Morpho is IMorpho {

/* FLASH LOANS */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function flashLoan(address token, uint256 assets, bytes calldata data) external {
IERC20(token).safeTransfer(msg.sender, assets);

Expand All @@ -411,16 +425,16 @@ contract Morpho is IMorpho {

/* AUTHORIZATION */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function setAuthorization(address authorized, bool newIsAuthorized) external {
isAuthorized[msg.sender][authorized] = newIsAuthorized;

emit EventsLib.SetAuthorization(msg.sender, msg.sender, authorized, newIsAuthorized);
}

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function setAuthorizationWithSig(Authorization memory authorization, Signature calldata signature) external {
require(block.timestamp < authorization.deadline, ErrorsLib.SIGNATURE_EXPIRED);
require(block.timestamp <= authorization.deadline, ErrorsLib.SIGNATURE_EXPIRED);
require(authorization.nonce == nonce[authorization.authorizer]++, ErrorsLib.INVALID_NONCE);

bytes32 hashStruct = keccak256(abi.encode(AUTHORIZATION_TYPEHASH, authorization));
Expand All @@ -445,33 +459,38 @@ contract Morpho is IMorpho {

/* INTEREST MANAGEMENT */

/// @inheritdoc IMorphoBase
function accrueInterest(MarketParams memory marketParams) external {
Id id = marketParams.id();
require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);

_accrueInterest(marketParams, id);
}

/// @dev Accrues interest for the given market `marketParams`.
/// @dev Assumes that the inputs `marketParams` and `id` match.
function _accrueInterest(MarketParams memory marketParams, Id id) internal {
uint256 elapsed = block.timestamp - market[id].lastUpdate;

if (elapsed == 0) return;

if (market[id].totalBorrowAssets != 0) {
uint256 borrowRate = IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
uint256 interest = market[id].totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
market[id].totalBorrowAssets += interest.toUint128();
market[id].totalSupplyAssets += interest.toUint128();

uint256 feeShares;
if (market[id].fee != 0) {
uint256 feeAmount = interest.wMulDown(market[id].fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
// that total supply is already increased by the full interest (including the fee amount).
feeShares =
feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
position[id][feeRecipient].supplyShares += feeShares;
market[id].totalSupplyShares += feeShares.toUint128();
}

emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares);
uint256 borrowRate = IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
uint256 interest = market[id].totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
market[id].totalBorrowAssets += interest.toUint128();
market[id].totalSupplyAssets += interest.toUint128();

uint256 feeShares;
if (market[id].fee != 0) {
uint256 feeAmount = interest.wMulDown(market[id].fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
// that total supply is already increased by the full interest (including the fee amount).
feeShares = feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
position[id][feeRecipient].supplyShares += feeShares;
market[id].totalSupplyShares += feeShares.toUint128();
}

emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares);

// Safe "unchecked" cast.
market[id].lastUpdate = uint128(block.timestamp);
}
Expand Down Expand Up @@ -508,7 +527,7 @@ contract Morpho is IMorpho {

/* STORAGE VIEW */

/// @inheritdoc IMorpho
/// @inheritdoc IMorphoBase
function extSloads(bytes32[] calldata slots) external view returns (bytes32[] memory res) {
uint256 nSlots = slots.length;

Expand Down
6 changes: 2 additions & 4 deletions src/interfaces/IIrm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ import {MarketParams, Market} from "./IMorpho.sol";
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
/// @notice Returns the borrow rate of the market `marketParams`.
/// @param marketParams The MarketParams struct of the market.
/// @param market The Market struct of the market.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);

/// @notice Returns the borrow rate of the market `marketParams` without modifying any storage.
/// @param marketParams The MarketParams struct of the market.
/// @param market The Market struct of the market.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}
Loading

0 comments on commit 560c176

Please sign in to comment.