8000 Refactor: use morpho utils by Rubilmax · Pull Request #90 · morpho-org/morpho-blue · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Refactor: use morpho utils #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
[submodule "lib/morpho-utils"]
path = lib/morpho-utils
url = https://github.com/morpho-dao/morpho-utils
1 change: 1 addition & 0 deletions lib/morpho-utils
Submodule morpho-utils added at 74f194
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
solmate/=lib/solmate/src/
morpho-utils/=lib/morpho-utils/src/
35 changes: 19 additions & 16 deletions src/Blue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {IIrm} from "src/interfaces/IIrm.sol";
import {IERC20} from "src/interfaces/IERC20.sol";
import {IOracle} from "src/interfaces/IOracle.sol";

import {MathLib} from "src/libraries/MathLib.sol";
import {WadRayMath} from "morpho-utils/math/WadRayMath.sol";
import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol";

uint256 constant WAD = 1e18;
Expand All @@ -31,7 +31,7 @@ function toId(Market calldata market) pure returns (Id) {
}

contract Blue {
using MathLib for uint256;
using WadRayMath for uint256;
using SafeTransferLib for IERC20;

// Storage.
Expand Down Expand Up @@ -111,7 +111,7 @@ contract Blue {
supplyShare[id][msg.sender] = WAD;
totalSupplyShares[id] = WAD;
} else {
uint256 shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]);
uint256 shares = amount.wadDivDown(totalSupply[id]).wadMulDown(totalSupplyShares[id]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering... isn't this basically equivalent to amount.mulDivDown(totalSupplyShares, totalSupply)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you change the order @Rubilmax ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MerlinEgalite discussed here: #90 (comment)

supplyShare[id][msg.sender] += shares;
totalSupplyShares[id] += shares;
}
Expand All @@ -128,7 +128,7 @@ contract Blue {

accrueInterests(market, id);

uint256 shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]);
uint256 shares = amount.wadDivUp(totalSupply[id]).wadMulUp(totalSupplyShares[id]);
supplyShare[id][msg.sender] -= shares;
totalSupplyShares[id] -= shares;

Expand All @@ -152,7 +152,7 @@ contract Blue {
borrowShare[id][msg.sender] = WAD;
totalBorrowS 8000 hares[id] = WAD;
} else {
uint256 shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]);
uint256 shares = amount.wadDivUp(totalBorrow[id]).wadMulUp(totalBorrowShares[id]);
borrowShare[id][msg.sender] += shares;
totalBorrowShares[id] += shares;
}
Expand All @@ -172,7 +172,7 @@ contract Blue {

accrueInterests(market, id);

uint256 shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]);
uint256 shares = amount.wadDivDown(totalBorrow[id]).wadMulDown(totalBorrowShares[id]); // TODO: totalBorrow[id] > 0 because ???
borrowShare[id][msg.sender] -= shares;
totalBorrowShares[id] -= shares;

Expand Down Expand Up @@ -222,10 +222,11 @@ contract Blue {
require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position");

// The liquidation incentive is 1 + ALPHA * (1 / LLTV - 1).
uint256 incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lltv) - WAD);
uint256 repaid =
seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price());
uint256 repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]);
uint256 incentive = WAD + ALPHA.wadMulDown(WAD.wadDivDown(market.lltv) - WAD);
uint256 repaid = seized.wadMulUp(market.collateralOracle.price()).wadDivUp(incentive).wadDivUp(
market.borrowableOracle.price()
);
uint256 repaidShares = repaid.wadDivDown(totalBorrow[id]).wadMulDown(totalBorrowShares[id]);

borrowShare[id][borrower] -= repaidShares;
totalBorrowShares[id] -= repaidShares;
Expand All @@ -235,7 +236,7 @@ contract Blue {

// Realize the bad debt if needed.
if (collateral[id][borrower] == 0) {
totalSupply[id] -= borrowShare[id][borrower].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]);
totalSupply[id] -= borrowShare[id][borrower].wadDivUp(totalBorrowShares[id]).wadMulUp(totalBorrow[id]);
totalBorrowShares[id] -= borrowShare[id][borrower];
borrowShare[id][borrower] = 0;
}
Expand All @@ -251,7 +252,7 @@ contract Blue {

if (marketTotalBorrow != 0) {
uint256 borrowRate = market.irm.borrowRate(market);
uint256 accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]);
uint256 accruedInterests = marketTotalBorrow.wadMulDown(borrowRate * (block.timestamp - lastUpdate[id]));
totalBorrow[id] = marketTotalBorrow + accruedInterests;
totalSupply[id] += accruedInterests;
}
Expand All @@ -264,10 +265,12 @@ contract Blue {
function isHealthy(Market calldata market, Id id, address user) private view returns (bool) {
uint256 borrowShares = borrowShare[id][user];
if (borrowShares == 0) return true;

// totalBorrowShares[id] > 0 when borrowShares > 0.
uint256 borrowValue =
borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price());
uint256 collateralValue = collateral[id][user].wMul(market.collateralOracle.price());
return collateralValue.wMul(market.lltv) >= borrowValue;
uint256 borrowValue = borrowShares.wadDivUp(totalBorrowShares[id]).wadMulUp(totalBorrow[id]).wadMulUp(
market.borrowableOracle.price()
);
uint256 collateralValue = collateral[id][user].wadMulDown(market.collateralOracle.price());
return collateralValue.wadMulDown(market.lltv) >= borrowValue;
}
}
21 changes: 0 additions & 21 deletions src/libraries/MathLib.sol

This file was deleted.

6 changes: 3 additions & 3 deletions src/mocks/IrmMock.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

import {MathLib} from "src/libraries/MathLib.sol";
import {WadRayMath} from "morpho-utils/math/WadRayMath.sol";

import "src/Blue.sol";

contract IrmMock is IIrm {
using MathLib for uint256;
using WadRayMath for uint256;

Blue public immutable blue;

Expand All @@ -16,7 +16,7 @@ contract IrmMock is IIrm {

function borrowRate(Market calldata market) external view returns (uint256) {
Id id = Id.wrap(keccak256(abi.encode(market)));
uint256 utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id));
uint256 utilization = blue.totalBorrow(id).wadDivDown(blue.totalSupply(id));

// Divide by the number of seconds in a year.
// This is a very simple model (to refine later) where x% utilization corresponds to x% APR.
Expand Down
52 changes: 29 additions & 23 deletions test/forge/Blue.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity 0.8.20;
import {IERC20} from "src/interfaces/IERC20.sol";
import {IOracle} from "src/interfaces/IOracle.sol";

import {WadRayMath} from "morpho-utils/math/WadRayMath.sol";

import "forge-std/Test.sol";
import "forge-std/console.sol";

Expand All @@ -13,7 +15,7 @@ import {OracleMock as Oracle} from "src/mocks/OracleMock.sol";
import {IrmMock as Irm} from "src/mocks/IrmMock.sol";

contract BlueTest is Test {
using MathLib for uint256;
using WadRayMath for uint256;

address private constant BORROWER = address(1234);
address private constant LIQUIDATOR = address(5678);
Expand Down Expand Up @@ -76,25 +78,27 @@ contract BlueTest is Test {
// To move to a test utils file later.

function netWorth(address user) internal view returns (uint256) {
uint256 collateralAssetValue = collateralAsset.balanceOf(user).wMul(collateralOracle.price());
uint256 borrowableAssetValue = borrowableAsset.balanceOf(user).wMul(borrowableOracle.price());
uint256 collateralAssetValue = collateralAsset.balanceOf(user).wadMulDown(collateralOracle.price());
uint256 borrowableAssetValue = borrowableAsset.balanceOf(user).wadMulDown(borrowableOracle.price());
return collateralAssetValue + borrowableAssetValue;
}

function supplyBalance(address user) internal view returns (uint256) {
uint256 supplyShares = blue.supplyShare(id, user);
if (supplyShares == 0) return 0;

uint256 totalShares = blue.totalSupplyShares(id);
uint256 totalSupply = blue.totalSupply(id);
return supplyShares.wMul(totalSupply).wDiv(totalShares);
return supplyShares.wadMulDown(totalSupply).wadDivDown(totalShares);
}

function borrowBalance(address user) internal view returns (uint256) {
uint256 borrowerShares = blue.borrowShare(id, user);
if (borrowerShares == 0) return 0;

uint256 totalShares = blue.totalBorrowShares(id);
uint256 totalBorrow = blue.totalBorrow(id);
return borrowerShares.wMul(totalBorrow).wDiv(totalShares);
return borrowerShares.wadMulUp(totalBorrow).wadDivUp(totalShares);
}

// Invariants
Expand Down Expand Up @@ -292,9 +296,9 @@ contract BlueTest is Test {
vm.prank(BORROWER);
blue.supplyCollateral(market, amountCollateral);

uint256 collateralValue = amountCollateral.wMul(priceCollateral);
uint256 borrowValue = amountBorrowed.wMul(priceBorrowable);
if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(LLTV))) {
uint256 collateralValue = amountCollateral.wadMulDown(priceCollateral);
uint256 borrowValue = amountBorrowed.wadMulUp(priceBorrowable);
if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wadMulDown(LLTV))) {
vm.prank(BORROWER);
blue.borrow(market, amountBorrowed);
} else {
Expand Down Expand Up @@ -360,10 +364,10 @@ contract BlueTest is Test {
amountLent = bound(amountLent, 1000, 2 ** 64);

uint256 amountCollateral = amountLent;
uint256 borrowingPower = amountCollateral.wMul(LLTV);
uint256 amountBorrowed = borrowingPower.wMul(0.8e18);
uint256 toSeize = amountCollateral.wMul(LLTV);
uint256 incentive = WAD + ALPHA.wMul(WAD.wDiv(LLTV) - WAD);
uint256 borrowingPower = amountCollateral.wadMulDown(LLTV);
uint256 amountBorrowed = borrowingPower.wadMulDown(0.8e18);
uint256 toSeize = amountCollateral.wadMulDown(LLTV);
uint256 incentive = WAD + ALPHA.wadMulDown(WAD.wadDivDown(LLTV) - WAD);

borrowableAsset.setBalance(address(this), amountLent);
collateralAsset.setBalance(BORROWER, amountCollateral);
Expand All @@ -389,9 +393,10 @@ contract BlueTest is Test {

uint256 liquidatorNetWorthAfter = netWorth(LIQUIDATOR);

uint256 expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price());
uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price())
- expectedRepaid.wMul(borrowableOracle.price());
uint256 expectedRepaid =
toSeize.wadMulUp(collateralOracle.price()).wadDivUp(incentive).wadDivUp(borrowableOracle.price());
uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wadMulDown(collateralOracle.price())
- expectedRepaid.wadMulDown(borrowableOracle.price());
assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth");
assertApproxEqAbs(borrowBalance(BORROWER), amountBorrowed - expectedRepaid, 100, "BORROWER balance");
assertEq(blue.collateral(id, BORROWER), amountCollateral - toSeize, "BORROWER collateral");
Expand All @@ -402,10 +407,10 @@ contract BlueTest is Test {
amountLent = bound(amountLent, 1000, 2 ** 64);

uint256 amountCollateral = amountLent;
uint256 borrowingPower = amountCollateral.wMul(LLTV);
uint256 amountBorrowed = borrowingPower.wMul(0.8e18);
uint256 borrowingPower = amountCollateral.wadMulDown(LLTV);
uint256 amountBorrowed = borrowingPower.wadMulDown(0.8e18);
uint256 toSeize = amountCollateral;
uint256 incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lltv) - WAD);
uint256 incentive = WAD + ALPHA.wadMulDown(WAD.wadDivDown(market.lltv) - WAD);

borrowableAsset.setBalance(address(this), amountLent);
collateralAsset.setBalance(BORROWER, amountCollateral);
Expand All @@ -431,9 +436,10 @@ contract BlueTest is Test {

uint256 liquidatorNetWorthAfter = netWorth(LIQUIDATOR);

uint256 expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price());
uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price())
- expectedRepaid.wMul(borrowableOracle.price());
uint256 expectedRepaid =
toSeize.wadMulUp(collateralOracle.price()).wadDivUp(incentive).wadDivUp(borrowableOracle.price());
uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wadMulDown(collateralOracle.price())
- expectedRepaid.wadMulDown(borrowableOracle.price());
assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth");
assertEq(borrowBalance(BORROWER), 0, "BORROWER balance");
assertEq(blue.collateral(id, BORROWER), 0, "BORROWER collateral");
Expand Down Expand Up @@ -510,10 +516,10 @@ contract BlueTest is Test {
function testEmptyMarket(uint256 amount) public {
vm.assume(amount > 0);

vm.expectRevert(stdError.divisionError);
vm.expectRevert();
blue.withdraw(market, amount);

vm.expectRevert(stdError.divisionError);
vm.expectRevert();
blue.repay(market, amount);

vm.expectRevert(stdError.arithmeticError);
Expand Down
0