Skip to content

Commit

Permalink
Merge pull request #248 from morpho-labs/feat/amount-and-shares-as-input
Browse files Browse the repository at this point in the history
Amount and shares as input
  • Loading branch information
MathisGD authored Aug 10, 2023
2 parents 3de9d77 + a0c118a commit b04316d
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 235 deletions.
37 changes: 25 additions & 12 deletions src/Blue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {IOracle} from "./interfaces/IOracle.sol";
import {Id, Market, Signature, IBlue} from "./interfaces/IBlue.sol";

import {Errors} from "./libraries/Errors.sol";
import {UtilsLib} from "./libraries/UtilsLib.sol";
import {SharesMath} from "./libraries/SharesMath.sol";
import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol";
import {MarketLib} from "./libraries/MarketLib.sol";
Expand Down Expand Up @@ -143,15 +144,18 @@ contract Blue is IBlue {

// Supply management.

function supply(Market memory market, uint256 amount, address onBehalf, bytes calldata data) external {
function supply(Market memory market, uint256 amount, uint256 shares, address onBehalf, bytes calldata data)
external
{
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(amount != 0, Errors.ZERO_AMOUNT);
require(UtilsLib.exactlyOneZero(amount, shares), Errors.INCONSISTENT_INPUT);
require(onBehalf != address(0), Errors.ZERO_ADDRESS);

_accrueInterests(market, id);

uint256 shares = amount.toSharesDown(totalSupply[id], totalSupplyShares[id]);
if (amount > 0) shares = amount.toSharesDown(totalSupply[id], totalSupplyShares[id]);
else amount = shares.toAssetsUp(totalSupply[id], totalSupplyShares[id]);

supplyShares[id][onBehalf] += shares;
totalSupplyShares[id] += shares;
Expand All @@ -164,17 +168,20 @@ contract Blue is IBlue {
IERC20(market.borrowableAsset).safeTransferFrom(msg.sender, address(this), amount);
}

function withdraw(Market memory market, uint256 shares, address onBehalf, address receiver) external {
function withdraw(Market memory market, uint256 amount, uint256 shares, address onBehalf, address receiver)
external
{
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(shares != 0, Errors.ZERO_SHARES);
require(UtilsLib.exactlyOneZero(amount, shares), Errors.INCONSISTENT_INPUT);
// No need to verify that onBehalf != address(0) thanks to the authorization check.
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(_isSenderAuthorized(onBehalf), Errors.UNAUTHORIZED);

_accrueInterests(market, id);

uint256 amount = shares.toAssetsDown(totalSupply[id], totalSupplyShares[id]);
if (amount > 0) shares = amount.toSharesUp(totalSupply[id], totalSupplyShares[id]);
else amount = shares.toAssetsDown(totalSupply[id], totalSupplyShares[id]);

supplyShares[id][onBehalf] -= shares;
totalSupplyShares[id] -= shares;
Expand All @@ -189,17 +196,20 @@ contract Blue is IBlue {

// Borrow management.

function borrow(Market memory market, uint256 amount, address onBehalf, address receiver) external {
function borrow(Market memory market, uint256 amount, uint256 shares, address onBehalf, address receiver)
external
{
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(amount != 0, Errors.ZERO_AMOUNT);
require(UtilsLib.exactlyOneZero(amount, shares), Errors.INCONSISTENT_INPUT);
// No need to verify that onBehalf != address(0) thanks to the authorization check.
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(_isSenderAuthorized(onBehalf), Errors.UNAUTHORIZED);

_accrueInterests(market, id);

uint256 shares = amount.toSharesUp(totalBorrow[id], totalBorrowShares[id]);
if (amount > 0) shares = amount.toSharesUp(totalBorrow[id], totalBorrowShares[id]);
else amount = shares.toAssetsDown(totalBorrow[id], totalBorrowShares[id]);

borrowShares[id][onBehalf] += shares;
totalBorrowShares[id] += shares;
Expand All @@ -213,15 +223,18 @@ contract Blue is IBlue {
IERC20(market.borrowableAsset).safeTransfer(receiver, amount);
}

function repay(Market memory market, uint256 shares, address onBehalf, bytes calldata data) external {
function repay(Market memory market, uint256 amount, uint256 shares, address onBehalf, bytes calldata data)
external
{
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(shares != 0, Errors.ZERO_SHARES);
require(UtilsLib.exactlyOneZero(amount, shares), Errors.INCONSISTENT_INPUT);
require(onBehalf != address(0), Errors.ZERO_ADDRESS);

_accrueInterests(market, id);

uint256 amount = shares.toAssetsUp(totalBorrow[id], totalBorrowShares[id]);
if (amount > 0) shares = amount.toSharesDown(totalBorrow[id], totalBorrowShares[id]);
else amount = shares.toAssetsUp(totalBorrow[id], totalBorrowShares[id]);

borrowShares[id][onBehalf] -= shares;
totalBorrowShares[id] -= shares;
Expand Down
12 changes: 8 additions & 4 deletions src/interfaces/IBlue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,14 @@ interface IBlue is IFlashLender {
function setFeeRecipient(address recipient) external;
function createMarket(Market memory market) external;

function supply(Market memory market, uint256 amount, address onBehalf, bytes memory data) external;
function withdraw(Market memory market, uint256 amount, address onBehalf, address receiver) external;
function borrow(Market memory market, uint256 amount, address onBehalf, address receiver) external;
function repay(Market memory market, uint256 amount, address onBehalf, bytes memory data) external;
function supply(Market memory market, uint256 amount, uint256 shares, address onBehalf, bytes memory data)
external;
function withdraw(Market memory market, uint256 amount, uint256 shares, address onBehalf, address receiver)
external;
function borrow(Market memory market, uint256 amount, uint256 shares, address onBehalf, address receiver)
external;
function repay(Market memory market, uint256 amount, uint256 shares, address onBehalf, bytes memory data)
external;
function supplyCollateral(Market memory market, uint256 amount, address onBehalf, bytes memory data) external;
function withdrawCollateral(Market memory market, uint256 amount, address onBehalf, address receiver) external;
function liquidate(Market memory market, address borrower, uint256 seized, bytes memory data) external;
Expand Down
38 changes: 0 additions & 38 deletions src/libraries/BlueLib.sol

This file was deleted.

2 changes: 1 addition & 1 deletion src/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ library Errors {

string internal constant ZERO_AMOUNT = "zero amount";

string internal constant ZERO_SHARES = "zero shares";
string internal constant INCONSISTENT_INPUT = "inconsistent input";

string internal constant ZERO_ADDRESS = "zero address";

Expand Down
17 changes: 4 additions & 13 deletions src/libraries/FixedPointMathLib.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {UtilsLib} from "./UtilsLib.sol";

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
Expand Down Expand Up @@ -33,8 +35,8 @@ library FixedPointMathLib {
/// to approximate a compound interest rate: (1 + x)^n - 1.
function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
uint256 firstTerm = x * n;
uint256 secondTerm = mulWadDown(firstTerm, x * zeroFloorSub(n, 1)) / 2;
uint256 thirdTerm = mulWadDown(secondTerm, x * zeroFloorSub(n, 2)) / 3;
uint256 secondTerm = mulWadDown(firstTerm, x * UtilsLib.zeroFloorSub(n, 1)) / 2;
uint256 thirdTerm = mulWadDown(secondTerm, x * UtilsLib.zeroFloorSub(n, 2)) / 3;

return firstTerm + secondTerm + thirdTerm;
}
Expand Down Expand Up @@ -65,15 +67,4 @@ library FixedPointMathLib {
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}

/*//////////////////////////////////////////////////////////////
INTEGER OPERATIONS
//////////////////////////////////////////////////////////////*/

/// @dev Returns max(x - y, 0).
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
}
28 changes: 0 additions & 28 deletions src/libraries/SharesMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,4 @@ library SharesMath {
function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}

/// @dev Calculates the amount of shares corresponding to an exact amount of supply to withdraw.
/// Note: only works as long as totalSupplyShares + VIRTUAL_SHARES >= totalSupply + VIRTUAL_ASSETS.
function toWithdrawShares(uint256 amount, uint256 totalSupply, uint256 totalSupplyShares)
internal
pure
returns (uint256)
{
uint256 sharesMin = toSharesDown(amount, totalSupply, totalSupplyShares);
uint256 sharesMax = toSharesUp(amount + 1, totalSupply, totalSupplyShares);

return (sharesMin + sharesMax) / 2;
}

/// @dev Calculates the amount of shares corresponding to an exact amount of debt to repay.
/// Note: only works as long as totalBorrowShares + VIRTUAL_SHARES >= totalBorrow + VIRTUAL_ASSETS.
function toRepayShares(uint256 amount, uint256 totalBorrow, uint256 totalBorrowShares)
internal
pure
returns (uint256)
{
if (amount == 0) return 0;

uint256 sharesMin = toSharesDown(amount - 1, totalBorrow, totalBorrowShares);
uint256 sharesMax = toSharesUp(amount, totalBorrow, totalBorrowShares);

return (sharesMin + sharesMax) / 2;
}
}
19 changes: 19 additions & 0 deletions src/libraries/UtilsLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/// @dev Inspired by morpho-utils.
library UtilsLib {
/// @dev Returns max(x - y, 0).
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}

/// @dev Returns true if there is exactly one zero.
function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
assembly {
z := xor(iszero(x), iszero(y))
}
}
}
Loading

0 comments on commit b04316d

Please sign in to comment.