Skip to content

Commit

Permalink
Merge pull request #359 from morpho-labs/feat/other-casting
Browse files Browse the repository at this point in the history
Other casting (v7)
  • Loading branch information
MathisGD authored Aug 18, 2023
2 parents bf53d5f + 7a47fea commit 42ed7c1
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 156 deletions.
57 changes: 26 additions & 31 deletions src/Morpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ bytes32 constant AUTHORIZATION_TYPEHASH =
contract Morpho is IMorpho {
using MathLib for uint256;
using MarketLib for Info;
using UtilsLib for uint256;
using SharesMathLib for uint256;
using SafeTransferLib for IERC20;

Expand Down Expand Up @@ -122,6 +123,7 @@ contract Morpho is IMorpho {
// Accrue interest using the previous fee set before changing it.
_accrueInterest(info, id);

// Safe "unchecked" cast.
market[id].fee = uint128(newFee);

emit EventsLib.SetFee(id, newFee);
Expand All @@ -143,6 +145,7 @@ contract Morpho is IMorpho {
require(isLltvEnabled[info.lltv], ErrorsLib.LLTV_NOT_ENABLED);
require(market[id].lastUpdate == 0, ErrorsLib.MARKET_ALREADY_CREATED);

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

Expand All @@ -165,11 +168,10 @@ contract Morpho is IMorpho {

if (assets > 0) shares = assets.toSharesDown(market[id].totalSupplyAssets, market[id].totalSupplyShares);
else assets = shares.toAssetsUp(market[id].totalSupplyAssets, market[id].totalSupplyShares);
require(assets < 2 ** 128 && shares < 2 ** 128, "too high");

user[id][onBehalf].supplyShares += shares;
market[id].totalSupplyShares += uint128(shares);
market[id].totalSupplyAssets += uint128(assets);
market[id].totalSupplyShares += shares.toUint128();
market[id].totalSupplyAssets += assets.toUint128();

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

Expand All @@ -196,11 +198,10 @@ contract Morpho is IMorpho {

if (assets > 0) shares = assets.toSharesUp(market[id].totalSupplyAssets, market[id].totalSupplyShares);
else assets = shares.toAssetsDown(market[id].totalSupplyAssets, market[id].totalSupplyShares);
require(assets < 2 ** 128 && shares < 2 ** 128, "too high");

user[id][onBehalf].supplyShares -= shares;
market[id].totalSupplyShares -= uint128(shares);
market[id].totalSupplyAssets -= uint128(assets);
market[id].totalSupplyShares -= shares.toUint128();
market[id].totalSupplyAssets -= assets.toUint128();

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

Expand Down Expand Up @@ -229,11 +230,10 @@ contract Morpho is IMorpho {

if (assets > 0) shares = assets.toSharesUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
else assets = shares.toAssetsDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
require(assets < 2 ** 128 && shares < 2 ** 128, "too high");

user[id][onBehalf].borrowShares += uint128(shares);
market[id].totalBorrowShares += uint128(shares);
market[id].totalBorrowAssets += uint128(assets);
user[id][onBehalf].borrowShares += shares.toUint128();
market[id].totalBorrowShares += shares.toUint128();
market[id].totalBorrowAssets += assets.toUint128();

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

Expand All @@ -259,11 +259,10 @@ contract Morpho is IMorpho {

if (assets > 0) shares = assets.toSharesDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
else assets = shares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
require(assets < 2 ** 128 && shares < 2 ** 128, "too high");

user[id][onBehalf].borrowShares -= uint128(shares);
market[id].totalBorrowShares -= uint128(shares);
market[id].totalBorrowAssets -= uint128(assets);
user[id][onBehalf].borrowShares -= shares.toUint128();
market[id].totalBorrowShares -= shares.toUint128();
market[id].totalBorrowAssets -= assets.toUint128();

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

Expand All @@ -282,11 +281,10 @@ contract Morpho is IMorpho {
require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
require(assets != 0, ErrorsLib.ZERO_ASSETS);
require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
require(assets < 2 ** 128, "too much");

// Don't accrue interest because it's not required and it saves gas.

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

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

Expand All @@ -306,7 +304,7 @@ contract Morpho is IMorpho {

_accrueInterest(info, id);

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

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

Expand Down Expand Up @@ -335,23 +333,21 @@ contract Morpho is IMorpho {
assetsRepaid =
seized.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp(liquidationIncentiveFactor(info.lltv));
sharesRepaid = assetsRepaid.toSharesDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
require(assetsRepaid < 2 ** 128 && sharesRepaid < 2 ** 128, "too high");

user[id][borrower].borrowShares -= uint128(sharesRepaid);
market[id].totalBorrowShares -= uint128(sharesRepaid);
market[id].totalBorrowAssets -= uint128(assetsRepaid);
user[id][borrower].borrowShares -= sharesRepaid.toUint128();
market[id].totalBorrowShares -= sharesRepaid.toUint128();
market[id].totalBorrowAssets -= assetsRepaid.toUint128();

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

// Realize the bad debt if needed.
uint256 badDebtShares;
if (user[id][borrower].collateral == 0) {
badDebtShares = user[id][borrower].borrowShares;
uint256 badDebt = badDebtShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
require(badDebt < 2 ** 128 && badDebtShares < 2 ** 128, "too high");
market[id].totalSupplyAssets -= uint128(badDebt);
market[id].totalBorrowAssets -= uint128(badDebt);
market[id].totalBorrowShares -= uint128(badDebtShares);
market[id].totalSupplyAssets -= badDebt.toUint128();
market[id].totalBorrowAssets -= badDebt.toUint128();
market[id].totalBorrowShares -= badDebtShares.toUint128();
user[id][borrower].borrowShares = 0;
}

Expand Down Expand Up @@ -434,24 +430,23 @@ contract Morpho is IMorpho {
if (marketTotalBorrow != 0) {
uint256 borrowRate = IIrm(info.irm).borrowRate(info);
uint256 interest = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed));
require(interest < 2 ** 128, "too high");
market[id].totalBorrowAssets += uint128(interest);
market[id].totalSupplyAssets += uint128(interest);
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 updated.
feeShares =
feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
require(feeShares < 2 ** 128, "too high");
user[id][feeRecipient].supplyShares += feeShares;
market[id].totalSupplyShares += uint128(feeShares);
market[id].totalSupplyShares += feeShares.toUint128();
}

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

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

Expand Down
5 changes: 5 additions & 0 deletions src/libraries/UtilsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ library UtilsLib {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}

function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max);
return uint128(x);
}
}
42 changes: 0 additions & 42 deletions src/libraries/periphery/MorphoGettersLib.sol

This file was deleted.

98 changes: 98 additions & 0 deletions src/libraries/periphery/MorphoInterestLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {Id, Info, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";

import {MathLib} from "../MathLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {MarketLib} from "../MarketLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";

/// @title MorphoInterestLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply to it.
/// The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoInterestLib {
using MarketLib for Info;
using MathLib for uint256;
using MorphoLib for IMorpho;
using SharesMathLib for uint256;

function expectedAccrueInterest(IMorpho morpho, Info memory info)
internal
view
returns (uint256 totalSupply, uint256 toralBorrow, uint256 totalSupplyShares)
{
Id id = info.id();

bytes32[] memory slots = new bytes32[](3);
slots[0] = MorphoStorageLib.marketSlot(id);
slots[1] = bytes32(uint256(MorphoStorageLib.marketSlot(id)) + 1);
slots[2] = bytes32(uint256(MorphoStorageLib.marketSlot(id)) + 2);

bytes32[] memory values = morpho.extsload(slots);
totalSupply = uint256(values[0] << 128 >> 128);
totalSupplyShares = uint256(values[0] >> 128);
toralBorrow = uint256(values[1] << 128 >> 128);
uint256 lastUpdate = uint256(values[2] << 128 >> 128);
uint256 fee = uint256(values[2] >> 128);

uint256 elapsed = block.timestamp - lastUpdate;

if (elapsed == 0) return (totalSupply, toralBorrow, totalSupplyShares);

if (toralBorrow != 0) {
uint256 borrowRate = IIrm(info.irm).borrowRateView(info);
uint256 interest = toralBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed));
toralBorrow += interest;
totalSupply += interest;

if (fee != 0) {
uint256 feeAmount = interest.wMulDown(fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated.
uint256 feeShares = feeAmount.toSharesDown(totalSupply - feeAmount, totalSupplyShares);

totalSupplyShares += feeShares;
}
}
}

function expectedTotalSupply(IMorpho morpho, Info memory info) internal view returns (uint256 totalSupply) {
(totalSupply,,) = expectedAccrueInterest(morpho, info);
}

function expectedTotalBorrow(IMorpho morpho, Info memory info) internal view returns (uint256 totalBorrow) {
(, totalBorrow,) = expectedAccrueInterest(morpho, info);
}

function expectedTotalSupplyShares(IMorpho morpho, Info memory info)
internal
view
returns (uint256 totalSupplyShares)
{
(,, totalSupplyShares) = expectedAccrueInterest(morpho, info);
}

/// @dev Warning: It does not work for `feeRecipient` because their supply shares increase is not taken into account.
function expectedSupplyBalance(IMorpho morpho, Info memory info, address user) internal view returns (uint256) {
Id id = info.id();
uint256 supplyShares = morpho.supplyShares(id, user);
(uint256 totalSupply,, uint256 totalSupplyShares) = expectedAccrueInterest(morpho, info);

return supplyShares.toAssetsDown(totalSupply, totalSupplyShares);
}

function expectedBorrowBalance(IMorpho morpho, Info memory info, address user) internal view returns (uint256) {
Id id = info.id();
uint256 borrowShares = morpho.borrowShares(id, user);
uint256 totalBorrowShares = morpho.totalBorrowShares(id);
(, uint256 totalBorrow,) = expectedAccrueInterest(morpho, info);

return borrowShares.toAssetsUp(totalBorrow, totalBorrowShares);
}
}
Loading

0 comments on commit 42ed7c1

Please sign in to comment.