Skip to content

Commit

Permalink
Merge branch 'main' of github.com:morpho-labs/morpho-blue into feat/i…
Browse files Browse the repository at this point in the history
…nvariant-tests
  • Loading branch information
Jean-Grimal committed Aug 21, 2023
2 parents b919184 + 453c185 commit 17b8df5
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 32 deletions.
4 changes: 0 additions & 4 deletions .prettierrc

This file was deleted.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,9 @@
"extends": [
"@commitlint/config-conventional"
]
},
"prettier": {
"printWidth": 120,
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
}
14 changes: 7 additions & 7 deletions src/Morpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,10 @@ contract Morpho is IMorpho {
market[id].totalSupplyShares -= shares.toUint128();
market[id].totalSupplyAssets -= assets.toUint128();

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

require(market[id].totalBorrowAssets <= market[id].totalSupplyAssets, ErrorsLib.INSUFFICIENT_LIQUIDITY);

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

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

return (assets, shares);
Expand Down Expand Up @@ -246,11 +246,11 @@ contract Morpho is IMorpho {
market[id].totalBorrowShares += shares.toUint128();
market[id].totalBorrowAssets += assets.toUint128();

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

require(_isHealthy(marketParams, id, onBehalf), ErrorsLib.INSUFFICIENT_COLLATERAL);
require(market[id].totalBorrowAssets <= market[id].totalSupplyAssets, ErrorsLib.INSUFFICIENT_LIQUIDITY);

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

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

return (assets, shares);
Expand Down Expand Up @@ -324,10 +324,10 @@ contract Morpho is IMorpho {

user[id][onBehalf].collateral -= assets.toUint128();

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

require(_isHealthy(marketParams, id, onBehalf), ErrorsLib.INSUFFICIENT_COLLATERAL);

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

IERC20(marketParams.collateralToken).safeTransfer(receiver, assets);
}

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

user[id][borrower].collateral -= seized.toUint128();

// Realize the bad debt if needed.
// Realize the bad debt if needed. Note that it saves ~3k gas to do it.
uint256 badDebtShares;
if (user[id][borrower].collateral == 0) {
badDebtShares = user[id][borrower].borrowShares;
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IMorpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct MarketParams {
uint256 lltv;
}

/// @dev Warning: For `feeRecipient, `supplyShares` does not contain the accrued shares since the last interest accrual.
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest accrual.
struct User {
uint256 supplyShares;
uint128 borrowShares;
Expand Down
54 changes: 35 additions & 19 deletions src/libraries/periphery/MorphoLib.sol
Original file line number Diff line number Diff line change
@@ -1,42 +1,58 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IMorpho, Id} from "../../Morpho.sol";
import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";

library MorphoLib {
function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256 res) {
(res,,) = morpho.user(id, user);
function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.userSupplySharesSlot(id, user));
return uint256(morpho.extSloads(slot)[0]);
}

function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256 res) {
(, res,) = morpho.user(id, user);
function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.userBorrowSharesAndCollateralSlot(id, user));
return uint128(uint256(morpho.extSloads(slot)[0]));
}

function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256 res) {
(,, res) = morpho.user(id, user);
function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.userBorrowSharesAndCollateralSlot(id, user));
return uint256(morpho.extSloads(slot)[0] >> 128);
}

function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256 res) {
(res,,,,,) = morpho.market(id);
function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}

function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256 res) {
(, res,,,,) = morpho.market(id);
function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}

function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256 res) {
(,, res,,,) = morpho.market(id);
function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}

function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256 res) {
(,,, res,,) = morpho.market(id);
function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}

function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256 res) {
(,,,, res,) = morpho.market(id);
function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}

function fee(IMorpho morpho, Id id) internal view returns (uint256 res) {
(,,,,, res) = morpho.market(id);
function fee(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}

function _array(bytes32 x) private pure returns (bytes32[] memory) {
bytes32[] memory res = new bytes32[](1);
res[0] = x;
return res;
}
}
113 changes: 113 additions & 0 deletions test/forge/periphery/MorphoLib.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {MorphoBalancesLib} from "src/libraries/periphery/MorphoBalancesLib.sol";

import "../BaseTest.sol";

contract MorphoLibTest is BaseTest {
using MathLib for uint256;
using MorphoLib for Morpho;

function _testMorphoLibCommon(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee)
private
{
// Prepare storage layout with non empty values.

amountSupplied = bound(amountSupplied, 2, MAX_TEST_AMOUNT);
amountBorrowed = bound(amountBorrowed, 1, amountSupplied);
timestamp = bound(timestamp, block.timestamp, type(uint32).max);
fee = bound(fee, 0, MAX_FEE);

// Set fee parameters.
vm.prank(OWNER);
morpho.setFee(market, fee);

// Set timestamp.
vm.warp(timestamp);

borrowableToken.setBalance(address(this), amountSupplied);
morpho.supply(market, amountSupplied, 0, address(this), hex"");

uint256 collateralPrice = IOracle(market.oracle).price();
collateralToken.setBalance(BORROWER, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice));

vm.startPrank(BORROWER);
morpho.supplyCollateral(
market, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex""
);
morpho.borrow(market, amountBorrowed, 0, BORROWER, BORROWER);
vm.stopPrank();
}

function testSupplyShares(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee) public {
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(uint256 expectedSupplyShares,,) = morpho.user(id, address(this));
assertEq(morpho.supplyShares(id, address(this)), expectedSupplyShares);
}

function testBorrowShares(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee) public {
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(, uint256 expectedBorrowShares,) = morpho.user(id, BORROWER);
assertEq(morpho.borrowShares(id, BORROWER), expectedBorrowShares);
}

function testCollateral(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee) public {
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(,, uint256 expectedCollateral) = morpho.user(id, BORROWER);
assertEq(morpho.collateral(id, BORROWER), expectedCollateral);
}

function testTotalSupplyAssets(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee)
public
{
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(uint256 expectedTotalSupplyAssets,,,,,) = morpho.market(id);
assertEq(morpho.totalSupplyAssets(id), expectedTotalSupplyAssets);
}

function testTotalSupplyShares(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee)
public
{
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(, uint256 expectedTotalSupplyShares,,,,) = morpho.market(id);
assertEq(morpho.totalSupplyShares(id), expectedTotalSupplyShares);
}

function testTotalBorrowAssets(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee)
public
{
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(,, uint256 expectedTotalBorrowAssets,,,) = morpho.market(id);
assertEq(morpho.totalBorrowAssets(id), expectedTotalBorrowAssets);
}

function testTotalBorrowShares(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee)
public
{
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(,,, uint256 expectedTotalBorrowShares,,) = morpho.market(id);
assertEq(morpho.totalBorrowShares(id), expectedTotalBorrowShares);
}

function testLastUpdate(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee) public {
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(,,,, uint256 expectedLastUpdate,) = morpho.market(id);
assertEq(morpho.lastUpdate(id), expectedLastUpdate);
}

function testFee(uint256 amountSupplied, uint256 amountBorrowed, uint256 timestamp, uint256 fee) public {
_testMorphoLibCommon(amountSupplied, amountBorrowed, timestamp, fee);

(,,,,, uint256 expectedFee) = morpho.market(id);
assertEq(morpho.fee(id), expectedFee);
}
}
2 changes: 1 addition & 1 deletion test/hardhat/Morpho.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe("Morpho", () => {
await morpho.connect(borrower).supplyCollateral(market, assets, borrower.address, "0x");
await morpho.connect(borrower).borrow(market, borrowedAmount, 0, borrower.address, user.address);

await oracle.setPrice(oraclePriceScale.div(100));
await oracle.setPrice(oraclePriceScale.div(1000));

const seized = closePositions ? assets : assets.div(2);

Expand Down

0 comments on commit 17b8df5

Please sign in to comment.