Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into certora/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
QGarchery committed Oct 12, 2023
2 parents 6ee4bf0 + bb0499b commit d8d1fc2
Show file tree
Hide file tree
Showing 60 changed files with 1,369 additions and 1,367 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- type: "slow"
fuzz-runs: 10000
max-test-rejects: 500000
invariant-runs: 64
invariant-runs: 48
invariant-depth: 2048
- type: "fast"
fuzz-runs: 256
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
47 changes: 39 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,43 @@
# Morpho Blue

## Licensing
Morpho Blue is a noncustodial lending protocol implemented for the Ethereum Virtual Machine.
Morpho Blue offers a new trustless primitive with increased efficiency and flexibility compared to existing lending platforms.
It provides permissionless risk management and permissionless market creation with oracle agnostic pricing.
It also enables higher collateralization factors, improved interest rates, and lower gas consumption.
The protocol is designed to be a simple, immutable, and governance-minimized base layer that allows for a wide variety of other layers to be built on top.
Morpho Blue also offers a convenient developer experience with a singleton implementation, callbacks, free flash loans, and account management features.

The primary license for Morpho Blue is the Business Source License 1.1 (`BUSL-1.1`), see [`LICENSE`](./LICENSE). However, some files are dual licensed under `GPL-2.0-or-later`.
## Whitepaper

All files in the following folders can also be licensed under `GPL-2.0-or-later` (as indicated in their SPDX headers):
- `src/interfaces`, see [`src/interfaces/LICENSE`](./src/interfaces/LICENSE)
- `src/libraries`, see [`src/libraries/LICENSE`](./src/libaries/LICENSE)
- `src/mocks`, see [`src/mocks/LICENSE`](./src/mocks/LICENSE)
- `test`, see [`test/LICENSE`](./test/LICENSE)
- `certora`, see [`certora/LICENSE`](./certora/LICENSE)
The protocol is described in detail in the [Morpho Blue Whitepaper](./morpho-blue-whitepaper.pdf).

## Repository Structure

[`Morpho.sol`](./src/Morpho.sol) contains most of the source code of the core contract of Morpho Blue.
It solely relies on internal libraries in the [`src/libraries`](./src/libraries) subdirectory.

Libraries in the [`src/libraries/periphery`](./src/libraries/periphery) directory are not used by Morpho Blue.
They are useful helpers that integrators can reuse or adapt to their own needs.

The [`src/mocks`](./src/mocks) directory contains contracts designed exclusively for testing.

You'll find relevant comments in [`IMorpho.sol`](./src/interfaces/IMorpho.sol), notably a list of requirements about market dependencies.

## Getting Started

Install dependencies: `yarn`

Run forge tests: `yarn test:forge`

Run hardhat tests: `yarn test:hardhat`

You will find other useful commands in the [`package.json`](./package.json) file.

## Audits

All audits are stored in the [audits](./audits/)' folder.

## Licences

The primary license for Morpho Blue is the Business Source License 1.1 (`BUSL-1.1`), see [`LICENSE`](./LICENSE).
However, all files in the following folders can also be licensed under `GPL-2.0-or-later` (as indicated in their SPDX headers): [`src/interfaces`](./src/interfaces), [`src/libraries`](./src/libraries), [`src/mocks`](./src/mocks), [`test`](./test), [`certora`](./certora).
Binary file not shown.
2 changes: 0 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ wrap_comments = true
[profile.build]
test = "/dev/null"
script = "/dev/null"
force = true


[profile.test]
via-ir = false
extra_output_files = []


# See more config options https://github.com/foundry-rs/foundry/tree/master/crates/config
8 changes: 2 additions & 6 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,9 @@ const config: HardhatUserConfig = {
chainId: 1,
gasPrice: 0,
initialBaseFeePerGas: 0,
allowBlocksWithSameTimestamp: true,
accounts: {
count: 252,
},
mining: {
mempool: {
order: "fifo",
},
count: 202, // must be even
},
},
},
Expand Down
1 change: 0 additions & 1 deletion lib/openzeppelin-contracts
Submodule openzeppelin-contracts deleted from fd81a9
Binary file added morpho-blue-whitepaper.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"postinstall": "husky install && forge install",
"build:forge": "FOUNDRY_PROFILE=build forge build",
"build:hardhat": "npx hardhat compile --force",
"build:hardhat": "npx hardhat compile",
"test:forge": "FOUNDRY_PROFILE=test forge test",
"test:forge:invariant": "FOUNDRY_MATCH_CONTRACT=InvariantTest yarn test:forge",
"test:forge:integration": "FOUNDRY_MATCH_CONTRACT=IntegrationTest yarn test:forge",
Expand Down
4 changes: 1 addition & 3 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/
@morpho-blue/=./
33 changes: 18 additions & 15 deletions src/Morpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {SafeTransferLib} from "./libraries/SafeTransferLib.sol";

/// @title Morpho
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @custom:contact security@morpho.org
/// @notice The Morpho contract.
contract Morpho is IMorpho {
using MathLib for uint128;
Expand Down Expand Up @@ -62,7 +62,7 @@ contract Morpho is IMorpho {

/* CONSTRUCTOR */

/// @notice Initializes the contract.
/// @notice Constructs the contract.
/// @param newOwner The new owner of the contract.
constructor(address newOwner) {
require(newOwner != address(0), ErrorsLib.ZERO_ADDRESS);
Expand All @@ -73,7 +73,7 @@ contract Morpho is IMorpho {

/* MODIFIERS */

/// @notice Reverts if the caller is not the owner.
/// @dev Reverts if the caller is not the owner.
modifier onlyOwner() {
require(msg.sender == owner, ErrorsLib.NOT_OWNER);
_;
Expand All @@ -96,7 +96,7 @@ contract Morpho is IMorpho {

isIrmEnabled[irm] = true;

emit EventsLib.EnableIrm(address(irm));
emit EventsLib.EnableIrm(irm);
}

/// @inheritdoc IMorpho
Expand Down Expand Up @@ -178,7 +178,7 @@ contract Morpho is IMorpho {

if (data.length > 0) IMorphoSupplyCallback(msg.sender).onMorphoSupply(assets, data);

IERC20(marketParams.borrowableToken).safeTransferFrom(msg.sender, address(this), assets);
IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), assets);

return (assets, shares);
}
Expand Down Expand Up @@ -211,7 +211,7 @@ contract Morpho is IMorpho {

emit EventsLib.Withdraw(id, msg.sender, onBehalf, receiver, assets, shares);

IERC20(marketParams.borrowableToken).safeTransfer(receiver, assets);
IERC20(marketParams.loanToken).safeTransfer(receiver, assets);

return (assets, shares);
}
Expand Down Expand Up @@ -247,7 +247,7 @@ contract Morpho is IMorpho {

emit EventsLib.Borrow(id, msg.sender, onBehalf, receiver, assets, shares);

IERC20(marketParams.borrowableToken).safeTransfer(receiver, assets);
IERC20(marketParams.loanToken).safeTransfer(receiver, assets);

return (assets, shares);
}
Expand Down Expand Up @@ -279,7 +279,7 @@ contract Morpho is IMorpho {

if (data.length > 0) IMorphoRepayCallback(msg.sender).onMorphoRepay(assets, data);

IERC20(marketParams.borrowableToken).safeTransferFrom(msg.sender, address(this), assets);
IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), assets);

return (assets, shares);
}
Expand Down Expand Up @@ -350,18 +350,20 @@ contract Morpho is IMorpho {

uint256 repaidAssets;
{
// The liquidation incentive factor is min(maxIncentiveFactor, 1/(1 - cursor*(1 - lltv))).
uint256 incentiveFactor = UtilsLib.min(
// The liquidation incentive factor is min(maxLiquidationIncentiveFactor, 1/(1 - cursor*(1 - lltv))).
uint256 liquidationIncentiveFactor = UtilsLib.min(
MAX_LIQUIDATION_INCENTIVE_FACTOR,
WAD.wDivDown(WAD - LIQUIDATION_CURSOR.wMulDown(WAD - marketParams.lltv))
);

if (seizedAssets > 0) {
repaidAssets = seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp(incentiveFactor);
repaidAssets =
seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp(liquidationIncentiveFactor);
repaidShares = repaidAssets.toSharesDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
} else {
repaidAssets = repaidShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
seizedAssets = repaidAssets.wMulDown(incentiveFactor).mulDivDown(ORACLE_PRICE_SCALE, collateralPrice);
seizedAssets =
repaidAssets.wMulDown(liquidationIncentiveFactor).mulDivDown(ORACLE_PRICE_SCALE, collateralPrice);
}
}

Expand Down Expand Up @@ -389,7 +391,7 @@ contract Morpho is IMorpho {

if (data.length > 0) IMorphoLiquidateCallback(msg.sender).onMorphoLiquidate(repaidAssets, data);

IERC20(marketParams.borrowableToken).safeTransferFrom(msg.sender, address(this), repaidAssets);
IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), repaidAssets);

return (seizedAssets, repaidAssets);
}
Expand Down Expand Up @@ -436,6 +438,7 @@ contract Morpho is IMorpho {
);
}

/// @dev Returns whether the sender is authorized to manage `onBehalf`'s positions.
function _isSenderAuthorized(address onBehalf) internal view returns (bool) {
return msg.sender == onBehalf || isAuthorized[onBehalf][msg.sender];
}
Expand Down Expand Up @@ -486,9 +489,9 @@ contract Morpho is IMorpho {
}

/// @dev Returns whether the position of `borrower` in the given market `marketParams` with the given
/// `collateralPrice`
/// is healthy.
/// `collateralPrice` is healthy.
/// @dev Assumes that the inputs `marketParams` and `id` match.
/// @dev Rounds in favor of the protocol, so one might not be able to borrow exactly `maxBorrow` but one unit less.
function _isHealthy(MarketParams memory marketParams, Id id, address borrower, uint256 collateralPrice)
internal
view
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/IERC20.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.2;
pragma solidity >=0.5.0;

/// @title IERC20
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @custom:contact security@morpho.org
/// @dev Empty because we only call library functions. It prevents calling transfer (transferFrom) instead of
/// safeTransfer (safeTransferFrom).
interface IERC20 {}
6 changes: 3 additions & 3 deletions src/interfaces/IIrm.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.2;
pragma solidity >=0.5.0;

import {MarketParams, Market} from "./IMorpho.sol";

/// @title IIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @notice Interface that IRMs used by Morpho must implement.
/// @custom:contact security@morpho.org
/// @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.
Expand Down
12 changes: 7 additions & 5 deletions src/interfaces/IMorpho.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.8;
pragma solidity >=0.5.0;

type Id is bytes32;

struct MarketParams {
address borrowableToken;
address loanToken;
address collateralToken;
address oracle;
address irm;
Expand All @@ -21,7 +21,7 @@ struct Position {

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additionnal shares accrued by `feeRecipient` since the last
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
uint128 totalSupplyAssets;
Expand All @@ -48,10 +48,12 @@ struct Signature {

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @custom:contact security@morpho.org
/// @notice Interface of Morpho.
interface IMorpho {
/// @notice The EIP-712 domain separator.
/// @dev Warning: In case of a hardfork, every EIP-712 signed message based on this domain separator can be reused
/// on the forked chain because the domain separator would be the same.
function DOMAIN_SEPARATOR() external view returns (bytes32);

/// @notice The owner of the contract.
Expand Down Expand Up @@ -102,7 +104,7 @@ interface IMorpho {
function idToMarketParams(Id id)
external
view
returns (address borrowableToken, address collateralToken, address oracle, address irm, uint256 lltv);
returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);

/// @notice Sets `newOwner` as owner of the contract.
/// @dev Warning: No two-step transfer ownership.
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IMorphoCallbacks.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.2;
pragma solidity >=0.5.0;

/// @title IMorphoLiquidateCallback
/// @notice Interface that liquidators willing to use `liquidate`'s callback must implement.
Expand Down
10 changes: 5 additions & 5 deletions src/interfaces/IOracle.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.2;
pragma solidity >=0.5.0;

/// @title IOracle
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @custom:contact security@morpho.org
/// @notice Interface that oracles used by Morpho must implement.
interface IOracle {
/// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of borrowable token, scaled by 1e36.
/// @dev It corresponds to the price of 10**(collateral decimals) assets of collateral token quoted in
/// 10**(borrowable decimals) assets of borrowable token with `36 + borrowable decimals - collateral decimals`
/// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36.
/// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in
/// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals`
/// decimals of precision.
function price() external view returns (uint256);
}
16 changes: 11 additions & 5 deletions src/libraries/ErrorsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @custom:contact security@morpho.org
/// @notice Library exposing error messages.
library ErrorsLib {
/// @notice Thrown when the caller is not the owner.
Expand Down Expand Up @@ -60,11 +60,17 @@ library ErrorsLib {
/// @notice Thrown when the nonce is invalid.
string internal constant INVALID_NONCE = "invalid nonce";

/// @notice Thrown when a token transfer has failed.
string internal constant TRANSFER_FAILED = "transfer failed";
/// @notice Thrown when a token transfer reverted.
string internal constant TRANSFER_REVERTED = "transfer reverted";

/// @notice Thrown when a token transferFrom has failed.
string internal constant TRANSFER_FROM_FAILED = "transferFrom failed";
/// @notice Thrown when a token transfer returned false.
string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false";

/// @notice Thrown when a token transferFrom reverted.
string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";

/// @notice Thrown when a token transferFrom returned false
string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false";

/// @notice Thrown when the maximum uint128 is exceeded.
string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
Expand Down
2 changes: 1 addition & 1 deletion src/libraries/EventsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Id, MarketParams} from "../interfaces/IMorpho.sol";

/// @title EventsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @custom:contact security@morpho.org
/// @notice Library exposing events.
library EventsLib {
/// @notice Emitted when setting a new owner.
Expand Down
Loading

0 comments on commit d8d1fc2

Please sign in to comment.