diff --git a/contracts/base/connectors/morpho-blue/events.sol b/contracts/base/connectors/morpho-blue/events.sol new file mode 100644 index 0000000..590f202 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/events.sol @@ -0,0 +1,106 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.19; +import {Id, MarketParams} from "./interfaces/IMorpho.sol"; + +contract Events { + event LogSupplyAssets( + Id indexed id, + uint256 assets, + uint256 shares, + uint256 getId, + uint256 setId + ); + + event LogSupplyOnBehalf( + Id indexed id, + uint256 assets, + uint256 shares, + address indexed onBehalf, + uint256 getId, + uint256 setId + ); + + event LogSupplyCollateral( + Id indexed id, + uint256 assets, + uint256 getId, + uint256 setId + ); + + event LogSupplyCollateralOnBehalf( + Id indexed id, + uint256 assets, + address indexed onBehalf, + uint256 getId, + uint256 setId + ); + + event LogBorrow( + Id indexed id, + uint256 amounts, + uint256 shares, + uint256 getId, + uint256 setId + ); + + event LogBorrowOnBehalf( + Id indexed id, + uint256 amounts, + uint256 shares, + address indexed onBehalf, + address indexed receiver, + uint256 getId, + uint256 setId + ); + + event LogWithdraw( + Id indexed id, + uint256 amounts, + uint256 shares, + uint256 getId, + uint256 setId + ); + + event LogWithdrawOnBehalf( + Id indexed id, + uint256 amounts, + uint256 shares, + address indexed onBehalf, + address indexed receiver, + uint256 getId, + uint256 setId + ); + + event LogWithdrawCollateral( + Id indexed id, + uint256 amounts, + uint256 getId, + uint256 setId + ); + + event LogWithdrawCollateralOnBehalf( + Id indexed id, + uint256 amounts, + address indexed onBehalf, + address indexed receiver, + uint256 getId, + uint256 setId + ); + + event LogRepay( + Id indexed id, + uint256 amounts, + uint256 shares, + uint256 getId, + uint256 setId + ); + + event LogRepayOnBehalf( + Id indexed id, + uint256 amounts, + uint256 shares, + address indexed onBehalf, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/base/connectors/morpho-blue/helpers.sol b/contracts/base/connectors/morpho-blue/helpers.sol new file mode 100644 index 0000000..beeb2bf --- /dev/null +++ b/contracts/base/connectors/morpho-blue/helpers.sol @@ -0,0 +1,141 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {Id, IMorpho, MarketParams, Position, Market} from "./interfaces/IMorpho.sol"; +import "../../common/stores.sol"; +import "../../common/basic.sol"; +import "../../common/interfaces.sol"; +import {MorphoBalancesLib} from "./libraries/periphery/MorphoBalancesLib.sol"; +import {MorphoLib} from "./libraries/periphery/MorphoLib.sol"; +import {UtilsLib} from "./libraries/UtilsLib.sol"; +import {MarketParamsLib} from "./libraries/MarketParamsLib.sol"; +import {SharesMathLib} from "./libraries/SharesMathLib.sol"; + +abstract contract Helpers is Stores, Basic { + using MorphoBalancesLib for IMorpho; + using MorphoLib for IMorpho; + using MarketParamsLib for MarketParams; + using UtilsLib for uint256; + using SharesMathLib for uint256; + + IMorpho public constant MORPHO_BLUE = + IMorpho(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb); + + /// @notice Handles Eth to Weth conversion if assets are provided. + function _performEthToWethConversion( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + bool _isModeCollateral + ) internal returns (Id _id, MarketParams memory, uint256 _amt) { + _amt = getUint(_getId, _assets); + + bool _isEth = _isModeCollateral + ? _marketParams.collateralToken == ethAddr + : _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + _id = _marketParams.id(); + + // Check for max value + if (_assets == type(uint256).max) { + _amt = _isEth + ? address(this).balance + : _isModeCollateral + ? TokenInterface(_marketParams.collateralToken).balanceOf( + address(this) + ) + : TokenInterface(_marketParams.loanToken).balanceOf( + address(this) + ); + } + + // Perform eth to weth conversion if necessary + convertEthToWeth( + _isEth, + _isModeCollateral + ? TokenInterface(_marketParams.collateralToken) + : TokenInterface(_marketParams.loanToken), + _amt + ); + + return (_id, _marketParams, _amt); + } + + /// @notice Handles Eth to Weth conversion if shares are provided. + function _performEthToWethSharesConversion( + MarketParams memory _marketParams, + uint256 _shares, + uint256 _getId + ) internal returns (Id _id, MarketParams memory, uint256 _assets) { + uint256 _shareAmt = getUint(_getId, _shares); + bool _isEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + _id = _marketParams.id(); + + // Handle the max share case + if (_shares == type(uint256).max) { + _assets = _isEth + ? address(this).balance + : TokenInterface(_marketParams.loanToken).balanceOf( + address(this) + ); + } else { + ( + uint256 totalSupplyAssets, + uint256 totalSupplyShares, + , + + ) = MORPHO_BLUE.expectedMarketBalances(_marketParams); + + _assets = _shareAmt.toAssetsUp( + totalSupplyAssets, + totalSupplyShares + ); + } + + // Perform ETH to WETH conversion if necessary + convertEthToWeth( + _isEth, + TokenInterface(_marketParams.loanToken), + _assets + ); + + return (_id, _marketParams, _assets); + } + + /// @notice Returns the borrowed assets and shares of onBehalf. + function getPaybackBalance( + Id _id, + MarketParams memory _marketParams, + address _onBehalf + ) internal view returns (uint256 _assets, uint256 _borrowedShareAmt) { + Position memory _pos = MORPHO_BLUE.position(_id, _onBehalf); + _borrowedShareAmt = _pos.borrowShares; + + (, , uint256 totalBorrowAssets, uint256 totalBorrowShares) = MORPHO_BLUE + .expectedMarketBalances(_marketParams); + + _assets = _borrowedShareAmt.toAssetsUp( + totalBorrowAssets, + totalBorrowShares + ); + } + + function updateTokenAddresses( + MarketParams memory _marketParams + ) internal pure returns (MarketParams memory) { + _marketParams.loanToken = _marketParams.loanToken == ethAddr + ? wethAddr + : _marketParams.loanToken; + + _marketParams.collateralToken = _marketParams.collateralToken == ethAddr + ? wethAddr + : _marketParams.collateralToken; + + return _marketParams; + } +} diff --git a/contracts/base/connectors/morpho-blue/interfaces/IERC20.sol b/contracts/base/connectors/morpho-blue/interfaces/IERC20.sol new file mode 100644 index 0000000..4d16188 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/interfaces/IERC20.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title IERC20 +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @dev Empty because we only call library functions. It prevents calling transfer (transferFrom) instead of +/// safeTransfer (safeTransferFrom). +interface IERC20 {} diff --git a/contracts/base/connectors/morpho-blue/interfaces/IIrm.sol b/contracts/base/connectors/morpho-blue/interfaces/IIrm.sol new file mode 100644 index 0000000..3de0bc1 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/interfaces/IIrm.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import {MarketParams, Market} from "./IMorpho.sol"; + +/// @title IIrm +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement. +interface IIrm { + /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`. + /// @dev Assumes that `market` corresponds to `marketParams`. + function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256); + + /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any + /// storage. + /// @dev Assumes that `market` corresponds to `marketParams`. + function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256); +} diff --git a/contracts/base/connectors/morpho-blue/interfaces/IMorpho.sol b/contracts/base/connectors/morpho-blue/interfaces/IMorpho.sol new file mode 100644 index 0000000..9c82524 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/interfaces/IMorpho.sol @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +type Id is bytes32; + +struct MarketParams { + address loanToken; + address collateralToken; + address oracle; + address irm; + uint256 lltv; +} + +/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest +/// accrual. +struct Position { + uint256 supplyShares; + uint128 borrowShares; + uint128 collateral; +} + +/// @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 additional shares accrued by `feeRecipient` since the last +/// interest accrual. +struct Market { + uint128 totalSupplyAssets; + uint128 totalSupplyShares; + uint128 totalBorrowAssets; + uint128 totalBorrowShares; + uint128 lastUpdate; + uint128 fee; +} + +struct Authorization { + address authorizer; + address authorized; + bool isAuthorized; + uint256 nonce; + uint256 deadline; +} + +struct Signature { + uint8 v; + bytes32 r; + bytes32 s; +} + +/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho. +/// @dev Consider using the IMorpho interface instead of this one. +interface IMorphoBase { + /// @notice The EIP-712 domain separator. + /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing + /// the same chain id because the domain separator would be the same. + function DOMAIN_SEPARATOR() external view returns (bytes32); + + /// @notice The owner of the contract. + /// @dev It has the power to change the owner. + /// @dev It has the power to set fees on markets and set the fee recipient. + /// @dev It has the power to enable but not disable IRMs and LLTVs. + function owner() external view returns (address); + + /// @notice The fee recipient of all markets. + /// @dev The recipient receives the fees of a given market through a supply position on that market. + function feeRecipient() external view returns (address); + + /// @notice Whether the `irm` is enabled. + function isIrmEnabled(address irm) external view returns (bool); + + /// @notice Whether the `lltv` is enabled. + function isLltvEnabled(uint256 lltv) external view returns (bool); + + /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets. + /// @dev Anyone is authorized to modify their own positions, regardless of this variable. + function isAuthorized(address authorizer, address authorized) external view returns (bool); + + /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures. + function nonce(address authorizer) external view returns (uint256); + + /// @notice Sets `newOwner` as `owner` of the contract. + /// @dev Warning: No two-step transfer ownership. + /// @dev Warning: The owner can be set to the zero address. + function setOwner(address newOwner) external; + + /// @notice Enables `irm` as a possible IRM for market creation. + /// @dev Warning: It is not possible to disable an IRM. + function enableIrm(address irm) external; + + /// @notice Enables `lltv` as a possible LLTV for market creation. + /// @dev Warning: It is not possible to disable a LLTV. + function enableLltv(uint256 lltv) external; + + /// @notice Sets the `newFee` for the given market `marketParams`. + /// @param newFee The new fee, scaled by WAD. + /// @dev Warning: The recipient can be the zero address. + function setFee(MarketParams memory marketParams, uint256 newFee) external; + + /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee. + /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost. + /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To + /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes. + function setFeeRecipient(address newFeeRecipient) external; + + /// @notice Creates the market `marketParams`. + /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees + /// Morpho behaves as expected: + /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`. + /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with + /// burn functions are not supported. + /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`. + /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount + /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported. + /// - The IRM should not re-enter Morpho. + /// - The oracle should return a price with the correct scaling. + /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties + /// (funds could get stuck): + /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue. + /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and + /// `toSharesDown` overflow. + /// - The IRM can revert on `borrowRate`. + /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest` + /// overflow. + /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and + /// `liquidate` from being used under certain market conditions. + /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or + /// the computation of `assetsRepaid` in `liquidate` overflow. + /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to + /// the point where `totalBorrowShares` is very large and borrowing overflows. + function createMarket(MarketParams memory marketParams) external; + + /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's + /// `onMorphoSupply` function with the given `data`. + /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the + /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific + /// amount of shares is given for full compatibility and precision. + /// @dev Supplying a large amount can revert for overflow. + /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage. + /// Consider using the `assets` parameter to avoid this. + /// @param marketParams The market to supply assets to. + /// @param assets The amount of assets to supply. + /// @param shares The amount of shares to mint. + /// @param onBehalf The address that will own the increased supply position. + /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed. + /// @return assetsSupplied The amount of assets supplied. + /// @return sharesSupplied The amount of shares minted. + function supply( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + bytes memory data + ) external returns (uint256 assetsSupplied, uint256 sharesSupplied); + + /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`. + /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`. + /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. + /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow. + /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to + /// conversion roundings between shares and assets. + /// @param marketParams The market to withdraw assets from. + /// @param assets The amount of assets to withdraw. + /// @param shares The amount of shares to burn. + /// @param onBehalf The address of the owner of the supply position. + /// @param receiver The address that will receive the withdrawn assets. + /// @return assetsWithdrawn The amount of assets withdrawn. + /// @return sharesWithdrawn The amount of shares burned. + function withdraw( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + address receiver + ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn); + + /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`. + /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the + /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is + /// given for full compatibility and precision. + /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. + /// @dev Borrowing a large amount can revert for overflow. + /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage. + /// Consider using the `assets` parameter to avoid this. + /// @param marketParams The market to borrow assets from. + /// @param assets The amount of assets to borrow. + /// @param shares The amount of shares to mint. + /// @param onBehalf The address that will own the increased borrow position. + /// @param receiver The address that will receive the borrowed assets. + /// @return assetsBorrowed The amount of assets borrowed. + /// @return sharesBorrowed The amount of shares minted. + function borrow( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + address receiver + ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed); + + /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's + /// `onMorphoReplay` function with the given `data`. + /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`. + /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow. + /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion + /// roundings between shares and assets. + /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow. + /// @param marketParams The market to repay assets to. + /// @param assets The amount of assets to repay. + /// @param shares The amount of shares to burn. + /// @param onBehalf The address of the owner of the debt position. + /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed. + /// @return assetsRepaid The amount of assets repaid. + /// @return sharesRepaid The amount of shares burned. + function repay( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + bytes memory data + ) external returns (uint256 assetsRepaid, uint256 sharesRepaid); + + /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's + /// `onMorphoSupplyCollateral` function with the given `data`. + /// @dev Interest are not accrued since it's not required and it saves gas. + /// @dev Supplying a large amount can revert for overflow. + /// @param marketParams The market to supply collateral to. + /// @param assets The amount of collateral to supply. + /// @param onBehalf The address that will own the increased collateral position. + /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed. + function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data) + external; + + /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`. + /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. + /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow. + /// @param marketParams The market to withdraw collateral from. + /// @param assets The amount of collateral to withdraw. + /// @param onBehalf The address of the owner of the collateral position. + /// @param receiver The address that will receive the collateral assets. + function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver) + external; + + /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the + /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's + /// `onMorphoLiquidate` function with the given `data`. + /// @dev Either `seizedAssets` or `repaidShares` should be zero. + /// @dev Seizing more than the collateral balance will underflow and revert without any error message. + /// @dev Repaying more than the borrow balance will underflow and revert without any error message. + /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow. + /// @param marketParams The market of the position. + /// @param borrower The owner of the position. + /// @param seizedAssets The amount of collateral to seize. + /// @param repaidShares The amount of shares to repay. + /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed. + /// @return The amount of assets seized. + /// @return The amount of assets repaid. + function liquidate( + MarketParams memory marketParams, + address borrower, + uint256 seizedAssets, + uint256 repaidShares, + bytes memory data + ) external returns (uint256, uint256); + + /// @notice Executes a flash loan. + /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all + /// markets combined, plus donations). + /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached: + /// - `flashFee` is zero. + /// - `maxFlashLoan` is the token's balance of this contract. + /// - The receiver of `assets` is the caller. + /// @param token The token to flash loan. + /// @param assets The amount of assets to flash loan. + /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback. + function flashLoan(address token, uint256 assets, bytes calldata data) external; + + /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions. + /// @param authorized The authorized address. + /// @param newIsAuthorized The new authorization status. + function setAuthorization(address authorized, bool newIsAuthorized) external; + + /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions. + /// @dev Warning: Reverts if the signature has already been submitted. + /// @dev The signature is malleable, but it has no impact on the security here. + /// @dev The nonce is passed as argument to be able to revert with a different error message. + /// @param authorization The `Authorization` struct. + /// @param signature The signature. + function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external; + + /// @notice Accrues interest for the given market `marketParams`. + function accrueInterest(MarketParams memory marketParams) external; + + /// @notice Returns the data stored on the different `slots`. + function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory); +} + +/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler. +/// @dev Consider using the IMorpho interface instead of this one. +interface IMorphoStaticTyping is IMorphoBase { + /// @notice The state of the position of `user` on the market corresponding to `id`. + /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest + /// accrual. + function position(Id id, address user) + external + view + returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral); + + /// @notice The state of the market corresponding to `id`. + /// @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 accrued shares by `feeRecipient` since the last interest + /// accrual. + function market(Id id) + external + view + returns ( + uint128 totalSupplyAssets, + uint128 totalSupplyShares, + uint128 totalBorrowAssets, + uint128 totalBorrowShares, + uint128 lastUpdate, + uint128 fee + ); + + /// @notice The market params corresponding to `id`. + /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer + /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. + function idToMarketParams(Id id) + external + view + returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv); +} + +/// @title IMorpho +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures. +interface IMorpho is IMorphoBase { + /// @notice The state of the position of `user` on the market corresponding to `id`. + /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest + /// accrual. + function position(Id id, address user) external view returns (Position memory p); + + /// @notice The state of the market corresponding to `id`. + /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual. + /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual. + /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last + /// interest accrual. + function market(Id id) external view returns (Market memory m); + + /// @notice The market params corresponding to `id`. + /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer + /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. + function idToMarketParams(Id id) external view returns (MarketParams memory); +} diff --git a/contracts/base/connectors/morpho-blue/interfaces/IMorphoCallbacks.sol b/contracts/base/connectors/morpho-blue/interfaces/IMorphoCallbacks.sol new file mode 100644 index 0000000..b4c7898 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/interfaces/IMorphoCallbacks.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title IMorphoLiquidateCallback +/// @notice Interface that liquidators willing to use `liquidate`'s callback must implement. +interface IMorphoLiquidateCallback { + /// @notice Callback called when a liquidation occurs. + /// @dev The callback is called only if data is not empty. + /// @param repaidAssets The amount of repaid assets. + /// @param data Arbitrary data passed to the `liquidate` function. + function onMorphoLiquidate(uint256 repaidAssets, bytes calldata data) external; +} + +/// @title IMorphoRepayCallback +/// @notice Interface that users willing to use `repay`'s callback must implement. +interface IMorphoRepayCallback { + /// @notice Callback called when a repayment occurs. + /// @dev The callback is called only if data is not empty. + /// @param assets The amount of repaid assets. + /// @param data Arbitrary data passed to the `repay` function. + function onMorphoRepay(uint256 assets, bytes calldata data) external; +} + +/// @title IMorphoSupplyCallback +/// @notice Interface that users willing to use `supply`'s callback must implement. +interface IMorphoSupplyCallback { + /// @notice Callback called when a supply occurs. + /// @dev The callback is called only if data is not empty. + /// @param assets The amount of supplied assets. + /// @param data Arbitrary data passed to the `supply` function. + function onMorphoSupply(uint256 assets, bytes calldata data) external; +} + +/// @title IMorphoSupplyCollateralCallback +/// @notice Interface that users willing to use `supplyCollateral`'s callback must implement. +interface IMorphoSupplyCollateralCallback { + /// @notice Callback called when a supply of collateral occurs. + /// @dev The callback is called only if data is not empty. + /// @param assets The amount of supplied collateral. + /// @param data Arbitrary data passed to the `supplyCollateral` function. + function onMorphoSupplyCollateral(uint256 assets, bytes calldata data) external; +} + +/// @title IMorphoFlashLoanCallback +/// @notice Interface that users willing to use `flashLoan`'s callback must implement. +interface IMorphoFlashLoanCallback { + /// @notice Callback called when a flash loan occurs. + /// @dev The callback is called only if data is not empty. + /// @param assets The amount of assets that was flash loaned. + /// @param data Arbitrary data passed to the `flashLoan` function. + function onMorphoFlashLoan(uint256 assets, bytes calldata data) external; +} diff --git a/contracts/base/connectors/morpho-blue/interfaces/IOracle.sol b/contracts/base/connectors/morpho-blue/interfaces/IOracle.sol new file mode 100644 index 0000000..482737e --- /dev/null +++ b/contracts/base/connectors/morpho-blue/interfaces/IOracle.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title IOracle +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface that oracles used by Morpho must implement. +/// @dev It is the user's responsibility to select markets with safe oracles. +interface IOracle { + /// @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); +} diff --git a/contracts/base/connectors/morpho-blue/interfaces/LICENSE b/contracts/base/connectors/morpho-blue/interfaces/LICENSE new file mode 100644 index 0000000..aec4e2a --- /dev/null +++ b/contracts/base/connectors/morpho-blue/interfaces/LICENSE @@ -0,0 +1,389 @@ +This software is available under your choice of the GNU General Public +License, version 2 or later, or the Business Source License, as set +forth below. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Morpho Association + +Licensed Work: Morpho Blue Core + The Licensed Work is (c) 2023 Morpho Association + +Additional Use Grant: Any uses listed and defined at + morpho-blue-core-license-grants.morpho.eth + +Change Date: The earlier of (i) 2026-01-01, or (ii) a date specified + at morpho-blue-core-license-date.morpho.eth, or (iii) + upon the activation of the setFee function of the + Licensed Work’s applicable protocol smart contracts + deployed for production use. + +Change License: GNU General Public License v2.0 or later + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/contracts/base/connectors/morpho-blue/libraries/ConstantsLib.sol b/contracts/base/connectors/morpho-blue/libraries/ConstantsLib.sol new file mode 100644 index 0000000..c4e49fe --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/ConstantsLib.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +/// @dev The maximum fee a market can have (25%). +uint256 constant MAX_FEE = 0.25e18; + +/// @dev Oracle price scale. +uint256 constant ORACLE_PRICE_SCALE = 1e36; + +/// @dev Liquidation cursor. +uint256 constant LIQUIDATION_CURSOR = 0.3e18; + +/// @dev Max liquidation incentive factor. +uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18; + +/// @dev The EIP-712 typeHash for EIP712Domain. +bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); + +/// @dev The EIP-712 typeHash for Authorization. +bytes32 constant AUTHORIZATION_TYPEHASH = + keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)"); diff --git a/contracts/base/connectors/morpho-blue/libraries/ErrorsLib.sol b/contracts/base/connectors/morpho-blue/libraries/ErrorsLib.sol new file mode 100644 index 0000000..02cc944 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/ErrorsLib.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +/// @title ErrorsLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Library exposing error messages. +library ErrorsLib { + /// @notice Thrown when the caller is not the owner. + string internal constant NOT_OWNER = "not owner"; + + /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV. + string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded"; + + /// @notice Thrown when the fee to set exceeds the maximum fee. + string internal constant MAX_FEE_EXCEEDED = "max fee exceeded"; + + /// @notice Thrown when the value is already set. + string internal constant ALREADY_SET = "already set"; + + /// @notice Thrown when the IRM is not enabled at market creation. + string internal constant IRM_NOT_ENABLED = "IRM not enabled"; + + /// @notice Thrown when the LLTV is not enabled at market creation. + string internal constant LLTV_NOT_ENABLED = "LLTV not enabled"; + + /// @notice Thrown when the market is already created. + string internal constant MARKET_ALREADY_CREATED = "market already created"; + + /// @notice Thrown when a token to transfer doesn't have code. + string internal constant NO_CODE = "no code"; + + /// @notice Thrown when the market is not created. + string internal constant MARKET_NOT_CREATED = "market not created"; + + /// @notice Thrown when not exactly one of the input amount is zero. + string internal constant INCONSISTENT_INPUT = "inconsistent input"; + + /// @notice Thrown when zero assets is passed as input. + string internal constant ZERO_ASSETS = "zero assets"; + + /// @notice Thrown when a zero address is passed as input. + string internal constant ZERO_ADDRESS = "zero address"; + + /// @notice Thrown when the caller is not authorized to conduct an action. + string internal constant UNAUTHORIZED = "unauthorized"; + + /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`. + string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral"; + + /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`. + string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity"; + + /// @notice Thrown when the position to liquidate is healthy. + string internal constant HEALTHY_POSITION = "position is healthy"; + + /// @notice Thrown when the authorization signature is invalid. + string internal constant INVALID_SIGNATURE = "invalid signature"; + + /// @notice Thrown when the authorization signature is expired. + string internal constant SIGNATURE_EXPIRED = "signature expired"; + + /// @notice Thrown when the nonce is invalid. + string internal constant INVALID_NONCE = "invalid nonce"; + + /// @notice Thrown when a token transfer reverted. + string internal constant TRANSFER_REVERTED = "transfer reverted"; + + /// @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"; +} diff --git a/contracts/base/connectors/morpho-blue/libraries/LICENSE b/contracts/base/connectors/morpho-blue/libraries/LICENSE new file mode 100644 index 0000000..aec4e2a --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/LICENSE @@ -0,0 +1,389 @@ +This software is available under your choice of the GNU General Public +License, version 2 or later, or the Business Source License, as set +forth below. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Morpho Association + +Licensed Work: Morpho Blue Core + The Licensed Work is (c) 2023 Morpho Association + +Additional Use Grant: Any uses listed and defined at + morpho-blue-core-license-grants.morpho.eth + +Change Date: The earlier of (i) 2026-01-01, or (ii) a date specified + at morpho-blue-core-license-date.morpho.eth, or (iii) + upon the activation of the setFee function of the + Licensed Work’s applicable protocol smart contracts + deployed for production use. + +Change License: GNU General Public License v2.0 or later + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/contracts/base/connectors/morpho-blue/libraries/MarketParamsLib.sol b/contracts/base/connectors/morpho-blue/libraries/MarketParamsLib.sol new file mode 100644 index 0000000..456b0e1 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/MarketParamsLib.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Id, MarketParams} from "../interfaces/IMorpho.sol"; + +/// @title MarketParamsLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Library to convert a market to its id. +library MarketParamsLib { + /// @notice The length of the data used to compute the id of a market. + /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each. + uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32; + + /// @notice Returns the id of the market `marketParams`. + function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) { + assembly ("memory-safe") { + marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH) + } + } +} diff --git a/contracts/base/connectors/morpho-blue/libraries/MathLib.sol b/contracts/base/connectors/morpho-blue/libraries/MathLib.sol new file mode 100644 index 0000000..653db4f --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/MathLib.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +uint256 constant WAD = 1e18; + +/// @title MathLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Library to manage fixed-point arithmetic. +library MathLib { + /// @dev Returns (`x` * `y`) / `WAD` rounded down. + function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivDown(x, y, WAD); + } + + /// @dev Returns (`x` * `WAD`) / `y` rounded down. + function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivDown(x, WAD, y); + } + + /// @dev Returns (`x` * `WAD`) / `y` rounded up. + function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivUp(x, WAD, y); + } + + /// @dev Returns (`x` * `y`) / `d` rounded down. + function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { + return (x * y) / d; + } + + /// @dev Returns (`x` * `y`) / `d` rounded up. + function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { + return (x * y + (d - 1)) / d; + } + + /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a + /// continuous compound interest rate. + function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) { + uint256 firstTerm = x * n; + uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD); + uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD); + + return firstTerm + secondTerm + thirdTerm; + } +} diff --git a/contracts/base/connectors/morpho-blue/libraries/SharesMathLib.sol b/contracts/base/connectors/morpho-blue/libraries/SharesMathLib.sol new file mode 100644 index 0000000..3ed7115 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/SharesMathLib.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {MathLib} from "./MathLib.sol"; + +/// @title SharesMathLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Shares management library. +/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares: +/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack. +library SharesMathLib { + using MathLib for uint256; + + /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure + /// high precision computations. + /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price + /// stays low enough not to inflate these assets to a significant value. + /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt. + uint256 internal constant VIRTUAL_SHARES = 1e6; + + /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is + /// empty. + uint256 internal constant VIRTUAL_ASSETS = 1; + + /// @dev Calculates the value of `assets` quoted in shares, rounding down. + function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { + return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS); + } + + /// @dev Calculates the value of `shares` quoted in assets, rounding down. + function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { + return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES); + } + + /// @dev Calculates the value of `assets` quoted in shares, rounding up. + function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { + return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS); + } + + /// @dev Calculates the value of `shares` quoted in assets, rounding up. + function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { + return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES); + } +} diff --git a/contracts/base/connectors/morpho-blue/libraries/UtilsLib.sol b/contracts/base/connectors/morpho-blue/libraries/UtilsLib.sol new file mode 100644 index 0000000..f343ef7 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/UtilsLib.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {ErrorsLib} from "../libraries/ErrorsLib.sol"; + +/// @title UtilsLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Library exposing helpers. +/// @dev Inspired by https://github.com/morpho-org/morpho-utils. +library UtilsLib { + /// @dev Returns true if there is exactly one zero among `x` and `y`. + function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) { + assembly { + z := xor(iszero(x), iszero(y)) + } + } + + /// @dev Returns the min of `x` and `y`. + function min(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + z := xor(x, mul(xor(x, y), lt(y, x))) + } + } + + /// @dev Returns `x` safely cast to uint128. + function toUint128(uint256 x) internal pure returns (uint128) { + require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED); + return uint128(x); + } + + /// @dev Returns max(0, x - y). + function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + z := mul(gt(x, y), sub(x, y)) + } + } +} diff --git a/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoBalancesLib.sol b/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoBalancesLib.sol new file mode 100644 index 0000000..94b3b85 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoBalancesLib.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol"; +import {IIrm} from "../../interfaces/IIrm.sol"; + +import {MathLib} from "../MathLib.sol"; +import {UtilsLib} from "../UtilsLib.sol"; +import {MorphoLib} from "./MorphoLib.sol"; +import {SharesMathLib} from "../SharesMathLib.sol"; +import {MarketParamsLib} from "../MarketParamsLib.sol"; + +/// @title MorphoBalancesLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @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 MorphoBalancesLib { + using MathLib for uint256; + using MathLib for uint128; + using UtilsLib for uint256; + using MorphoLib for IMorpho; + using SharesMathLib for uint256; + using MarketParamsLib for MarketParams; + + /// @notice Returns the expected market balances of a market after having accrued interest. + /// @return The expected total supply assets. + /// @return The expected total supply shares. + /// @return The expected total borrow assets. + /// @return The expected total borrow shares. + function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams) + internal + view + returns (uint256, uint256, uint256, uint256) + { + Id id = marketParams.id(); + Market memory market = morpho.market(id); + + uint256 elapsed = block.timestamp - market.lastUpdate; + + // Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0). + if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) { + uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market); + uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + market.totalBorrowAssets += interest.toUint128(); + market.totalSupplyAssets += interest.toUint128(); + + if (market.fee != 0) { + uint256 feeAmount = interest.wMulDown(market.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(market.totalSupplyAssets - feeAmount, market.totalSupplyShares); + market.totalSupplyShares += feeShares.toUint128(); + } + } + + return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares); + } + + /// @notice Returns the expected total supply assets of a market after having accrued interest. + function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams) + internal + view + returns (uint256 totalSupplyAssets) + { + (totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams); + } + + /// @notice Returns the expected total borrow assets of a market after having accrued interest. + function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams) + internal + view + returns (uint256 totalBorrowAssets) + { + (,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams); + } + + /// @notice Returns the expected total supply shares of a market after having accrued interest. + function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams) + internal + view + returns (uint256 totalSupplyShares) + { + (, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams); + } + + /// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest. + /// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account. + /// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from + /// assets to shares. + function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user) + internal + view + returns (uint256) + { + Id id = marketParams.id(); + uint256 supplyShares = morpho.supplyShares(id, user); + (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams); + + return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares); + } + + /// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest. + /// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow + /// assets. + function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user) + internal + view + returns (uint256) + { + Id id = marketParams.id(); + uint256 borrowShares = morpho.borrowShares(id, user); + (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams); + + return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares); + } +} diff --git a/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoLib.sol b/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoLib.sol new file mode 100644 index 0000000..c366d1a --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoLib.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {IMorpho, Id} from "../../interfaces/IMorpho.sol"; +import {MorphoStorageLib} from "./MorphoStorageLib.sol"; + +/// @title MorphoLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Helper library to access Morpho storage variables. +/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest. +library MorphoLib { + function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) { + bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user)); + return uint256(morpho.extSloads(slot)[0]); + } + + function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) { + bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user)); + return uint128(uint256(morpho.extSloads(slot)[0])); + } + + function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) { + bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user)); + return uint256(morpho.extSloads(slot)[0] >> 128); + } + + 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) { + bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id)); + return uint256(morpho.extSloads(slot)[0] >> 128); + } + + 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) { + bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id)); + return uint256(morpho.extSloads(slot)[0] >> 128); + } + + 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) { + 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; + } +} diff --git a/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoStorageLib.sol b/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoStorageLib.sol new file mode 100644 index 0000000..07e3900 --- /dev/null +++ b/contracts/base/connectors/morpho-blue/libraries/periphery/MorphoStorageLib.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Id} from "../../interfaces/IMorpho.sol"; + +/// @title MorphoStorageLib +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Helper library exposing getters to access Morpho storage variables' slot. +/// @dev This library is not used in Morpho itself and is intended to be used by integrators. +library MorphoStorageLib { + /* SLOTS */ + + uint256 internal constant OWNER_SLOT = 0; + uint256 internal constant FEE_RECIPIENT_SLOT = 1; + uint256 internal constant POSITION_SLOT = 2; + uint256 internal constant MARKET_SLOT = 3; + uint256 internal constant IS_IRM_ENABLED_SLOT = 4; + uint256 internal constant IS_LLTV_ENABLED_SLOT = 5; + uint256 internal constant IS_AUTHORIZED_SLOT = 6; + uint256 internal constant NONCE_SLOT = 7; + uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8; + + /* SLOT OFFSETS */ + + uint256 internal constant LOAN_TOKEN_OFFSET = 0; + uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1; + uint256 internal constant ORACLE_OFFSET = 2; + uint256 internal constant IRM_OFFSET = 3; + uint256 internal constant LLTV_OFFSET = 4; + + uint256 internal constant SUPPLY_SHARES_OFFSET = 0; + uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1; + + uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0; + uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1; + uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2; + + /* GETTERS */ + + function ownerSlot() internal pure returns (bytes32) { + return bytes32(OWNER_SLOT); + } + + function feeRecipientSlot() internal pure returns (bytes32) { + return bytes32(FEE_RECIPIENT_SLOT); + } + + function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) { + return bytes32( + uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET + ); + } + + function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) { + return bytes32( + uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + + BORROW_SHARES_AND_COLLATERAL_OFFSET + ); + } + + function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET); + } + + function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET); + } + + function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET); + } + + function isIrmEnabledSlot(address irm) internal pure returns (bytes32) { + return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT)); + } + + function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) { + return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT)); + } + + function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) { + return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT)))); + } + + function nonceSlot(address authorizer) internal pure returns (bytes32) { + return keccak256(abi.encode(authorizer, NONCE_SLOT)); + } + + function idToLoanTokenSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET); + } + + function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET); + } + + function idToOracleSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET); + } + + function idToIrmSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET); + } + + function idToLltvSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET); + } +} diff --git a/contracts/base/connectors/morpho-blue/main.sol b/contracts/base/connectors/morpho-blue/main.sol new file mode 100644 index 0000000..3ac3a3f --- /dev/null +++ b/contracts/base/connectors/morpho-blue/main.sol @@ -0,0 +1,997 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "./helpers.sol"; +import "./events.sol"; +import {MarketParamsLib} from "./libraries/MarketParamsLib.sol"; +import {MorphoBalancesLib} from "./libraries/periphery/MorphoBalancesLib.sol"; +import {SharesMathLib} from "./libraries/SharesMathLib.sol"; + +abstract contract MorphoBlue is Helpers, Events { + using MarketParamsLib for MarketParams; + using MorphoBalancesLib for IMorpho; + using SharesMathLib for uint256; + + /** + * @dev Supply ETH/ERC20 Token for lending. + * @notice Supplies assets to Morpho Blue for lending. + * @param _marketParams The market to supply assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _assets The amount of assets to supply. (For max: `uint256(-1)`) + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function supply( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt; + Id _id; + ( + _id, + _marketParams, // Updated token contracts in case of Eth + _amt + ) = _performEthToWethConversion( + _marketParams, + _assets, + _getId, + false + ); + + approve( + TokenInterface(_marketParams.loanToken), + address(MORPHO_BLUE), + _amt + ); + + uint256 _shares; + (_assets, _shares) = MORPHO_BLUE.supply( + _marketParams, + _amt, + 0, + address(this), + new bytes(0) + ); + + setUint(_setId, _assets); + + _eventName = "LogSupplyAssets(bytes32,unit256,unit256,unit256,unit256)"; + _eventParam = abi.encode(_id, _assets, _shares, _getId, _setId); + } + + /** + * @dev Supply ETH/ERC20 Token for lending. + * @notice Supplies assets to Morpho Blue for lending. + * @param _marketParams The market to supply assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _assets The amount of assets to supply. (For max: `uint256(-1)`) + * @param _onBehalf The address that will get the shares. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function supplyOnBehalf( + MarketParams memory _marketParams, + uint256 _assets, + address _onBehalf, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt; + Id _id; + ( + _id, + _marketParams, // Updated token contracts in case of Eth + _amt + ) = _performEthToWethConversion( + _marketParams, + _assets, + _getId, + false + ); + + approve( + TokenInterface(_marketParams.loanToken), + address(MORPHO_BLUE), + _amt + ); + + uint256 _shares; + (_assets, _shares) = MORPHO_BLUE.supply( + _marketParams, + _amt, + 0, + _onBehalf, + new bytes(0) + ); + + setUint(_setId, _assets); + + _eventName = "LogSupplyOnBehalf(bytes32,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _assets, + _shares, + _onBehalf, + _getId, + _setId + ); + } + + /** + * @dev Supply ETH/ERC20 Token for lending. + * @notice Supplies assets for a perfect share amount to Morpho Blue for lending. + * @param _marketParams The market to supply assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _shares The exact amount of shares to mint. (For max: `uint256(-1)`) + * @param _onBehalf The address that will get the shares. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function supplySharesOnBehalf( + MarketParams memory _marketParams, + uint256 _shares, + address _onBehalf, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt; + Id _id; + ( + _id, + _marketParams, // Updated token contracts in case of Eth + _amt // Share amount converted to assets + ) = _performEthToWethSharesConversion(_marketParams, _shares, _getId); + + approve( + TokenInterface(_marketParams.loanToken), + address(MORPHO_BLUE), + _amt + ); + + uint256 _assets; + (_assets, _shares) = MORPHO_BLUE.supply( + _marketParams, + _amt, + 0, + _onBehalf, + new bytes(0) + ); + + setUint(_setId, _assets); + + _eventName = "LogSupplyOnBehalf(bytes32,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _assets, + _shares, + _onBehalf, + _getId, + _setId + ); + } + + /** + * @notice Supply ETH/ERC20 Token for collateralization. + * @param _marketParams The market to supply assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _assets The amount of assets to supply. (For max: `uint256(-1)`) + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function supplyCollateral( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt; + Id _id; + ( + _id, + _marketParams, // Updated token contracts in case of Eth + _amt + ) = _performEthToWethConversion( + _marketParams, + _assets, + _getId, + true + ); + + approve( + TokenInterface(_marketParams.collateralToken), + address(MORPHO_BLUE), + _amt + ); + + MORPHO_BLUE.supplyCollateral( + _marketParams, + _amt, + address(this), + new bytes(0) + ); + + setUint(_setId, _amt); + + _eventName = "LogSupplyCollateral(bytes32,uint256,uint256,uint256)"; + _eventParam = abi.encode(_id, _assets, _getId, _setId); + } + + /** + * @notice Supplies `assets` of collateral on behalf of `onBehalf`. + * @param _marketParams The market to supply assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _assets The amount of assets to supply. (For max: `uint256(-1)`) + * @param _onBehalf The address that will get the shares. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function supplyCollateralOnBehalf( + MarketParams memory _marketParams, + uint256 _assets, + address _onBehalf, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt; + Id _id; + ( + _id, + _marketParams, // Updated token contracts in case of Eth + _amt + ) = _performEthToWethConversion( + _marketParams, + _assets, + _getId, + true + ); + + approve( + TokenInterface(_marketParams.collateralToken), + address(MORPHO_BLUE), + _amt + ); + + MORPHO_BLUE.supplyCollateral( + _marketParams, + _amt, + _onBehalf, + new bytes(0) + ); + + setUint(_setId, _amt); + + _eventName = "LogSupplyCollateralOnBehalf(bytes32,uint256,address,uint256,uint256)"; + _eventParam = abi.encode(_id, _assets, _onBehalf, _getId, _setId); + } + + /** + * @notice Handles the collateral withdrawals. + * @dev The market to withdraw assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to withdraw assets from. + * @param _assets The amount of assets to withdraw. (For max: `uint256(-1)`) + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function withdrawCollateral( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + // If amount is max, fetch collateral value from Morpho's contract + if (_amt == type(uint256).max) { + Position memory _pos = MORPHO_BLUE.position(_id, address(this)); + _amt = _pos.collateral; + } + + MORPHO_BLUE.withdrawCollateral( + _marketParams, + _amt, + address(this), + address(this) + ); + + convertWethToEth( + _marketParams.collateralToken == ethAddr, + TokenInterface(wethAddr), + _amt + ); + + setUint(_setId, _amt); + + _eventName = "LogWithdrawCollateral(bytes32,uint256,uint256,uint256)"; + _eventParam = abi.encode(_id, _amt, _getId, _setId); + } + + /** + * @notice Handles the withdrawal of collateral by a user from a specific market of a specific amount. + * @dev The market to withdraw assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to withdraw assets from. + * @param _assets The amount of assets to withdraw. (For max: `uint256(-1)`) + * @param _onBehalf The address that already deposited position. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function withdrawCollateralOnBehalf( + MarketParams memory _marketParams, + uint256 _assets, + address _onBehalf, + address _receiver, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + // If amount is max, fetch collateral value from Morpho's contract + if (_amt == type(uint256).max) { + Position memory _pos = MORPHO_BLUE.position(_id, _onBehalf); + _amt = _pos.collateral; + } + + MORPHO_BLUE.withdrawCollateral( + _marketParams, + _amt, + _onBehalf, + _receiver + ); + + if (_receiver == address(this)) + convertWethToEth( + _marketParams.collateralToken == ethAddr, + TokenInterface(wethAddr), + _amt + ); + + setUint(_setId, _amt); + + _eventName = "LogWithdrawCollateralOnBehalf(bytes32,uint256,address,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _amt, + _onBehalf, + _receiver, + _getId, + _setId + ); + } + + /** + * @notice Handles the withdrawal of supplied assets. + * @dev The market to withdraw assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to withdraw assets from. + * @param _assets The amount of assets to withdraw. (For max: `uint256(-1)`) + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function withdraw( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + uint256 _shares = 0; + + // Using shares for max amounts to make sure no dust is left on the contract + if (_amt == type(uint256).max) { + Position memory _pos = MORPHO_BLUE.position(_id, address(this)); + _shares = _pos.supplyShares; + _amt = 0; + } + + // In case of max share amount will be used + (_assets, _shares) = MORPHO_BLUE.withdraw( + _marketParams, + _amt, + _shares, + address(this), + address(this) + ); + + convertWethToEth( + _marketParams.loanToken == ethAddr, + TokenInterface(wethAddr), + _assets + ); + + setUint(_setId, _assets); + + _eventName = "LogWithdraw(bytes32,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(_id, _assets, _shares, _getId, _setId); + } + + /** + * @notice Handles the withdrawal of a specified amount of assets by a user from a specific market. + * @dev The market to withdraw assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The parameters of the market. + * @param _assets The amount of assets the user is withdrawing. (For max: `uint256(-1)`) + * @param _onBehalf The address who's position to withdraw from. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function withdrawOnBehalf( + MarketParams memory _marketParams, + uint256 _assets, + address _onBehalf, + address _receiver, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + uint256 _shares = 0; + + // Using shares for max amounts to make sure no dust is left on the contract + if (_amt == type(uint256).max) { + Position memory _pos = MORPHO_BLUE.position(_id, _onBehalf); + _shares = _pos.supplyShares; + _amt = 0; + } + + (_assets, _shares) = MORPHO_BLUE.withdraw( + _marketParams, + _amt, + _shares, + _onBehalf, + _receiver + ); + + if (_receiver == address(this)) + convertWethToEth( + _marketParams.loanToken == ethAddr, + TokenInterface(wethAddr), + _assets + ); + + setUint(_setId, _assets); + + _eventName = "LogWithdrawOnBehalf(bytes32,uint256,uint256,address,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _assets, + _shares, + _onBehalf, + address(this), + _getId, + _setId + ); + } + + /** + * @notice Handles the withdrawal of a specified amount of assets by a user from a specific market. + * @dev The market to withdraw assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The parameters of the market. + * @param _shares The amount of shares the user is withdrawing. (For max: `uint256(-1)`) + * @param _onBehalf The address who's position to withdraw from. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens deposited. + */ + function withdrawSharesOnBehalf( + MarketParams memory _marketParams, + uint256 _shares, + address _onBehalf, + address _receiver, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _shareAmt = getUint(_getId, _shares); + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + if (_shareAmt == type(uint256).max) { + Position memory _pos = MORPHO_BLUE.position(_id, _onBehalf); + _shareAmt = _pos.supplyShares; + } + + (uint256 _assets, ) = MORPHO_BLUE.withdraw( + _marketParams, + 0, + _shareAmt, + _onBehalf, + _receiver + ); + + if (_receiver == address(this)) + convertWethToEth( + _marketParams.loanToken == ethAddr, + TokenInterface(wethAddr), + _assets + ); + + setUint(_setId, _assets); + + _eventName = "LogWithdrawOnBehalf(bytes32,uint256,uint256,address,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _assets, + _shareAmt, + _onBehalf, + _receiver, + _getId, + _setId + ); + } + + /** + * @notice Borrows assets. + * @dev The market to borrow assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to borrow assets from. + * @param _assets The amount of assets to borrow. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens borrowed. + */ + function borrow( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + bool _isLoanEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + (, uint256 _shares) = MORPHO_BLUE.borrow( + _marketParams, + _amt, + 0, + address(this), + address(this) + ); + + convertWethToEth( + _isLoanEth, + TokenInterface(wethAddr), + _amt + ); + + setUint(_setId, _amt); + + _eventName = "LogBorrow(bytes32,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(_id, _amt, _shares, _getId, _setId); + } + + /** + * @notice Borrows `assets` on behalf of `onBehalf` to `receiver`. + * @dev The market to borrow assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to borrow assets from. + * @param _assets The amount of assets to borrow. + * @param _onBehalf The address that will recieve the borrowing assets and own the borrow position. + * @param _receiver The address that will recieve the borrowed assets. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens borrowed. + */ + function borrowOnBehalf( + MarketParams memory _marketParams, + uint256 _assets, + address _onBehalf, + address _receiver, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + bool _isLoanEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + (, uint256 _shares) = MORPHO_BLUE.borrow( + _marketParams, + _amt, + 0, + _onBehalf, + _receiver + ); + + if (_receiver == address(this)) + convertWethToEth(_isLoanEth, TokenInterface(wethAddr), _amt); + + setUint(_setId, _amt); + + _eventName = "LogBorrowOnBehalf(bytes32,uint256,uint256,address,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _amt, + _shares, + _onBehalf, + _receiver, + _getId, + _setId + ); + } + + /** + * @notice Borrows `shares` on behalf of `onBehalf` to `receiver`. + * @dev The market to borrow assets from. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to borrow assets from. + * @param _shares The amount of shares to mint. + * @param _onBehalf The address that will own the borrow position. + * @param _receiver The address that will recieve the borrowed assets. + * @param _getId ID to retrieve shares amt. + * @param _setId ID stores the amount of tokens borrowed. + */ + function borrowOnBehalfShares( + MarketParams memory _marketParams, + uint256 _shares, + address _onBehalf, + address _receiver, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _shareAmt = getUint(_getId, _shares); + bool _isLoanEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + (uint256 _assets, ) = MORPHO_BLUE.borrow( + _marketParams, + 0, + _shareAmt, + _onBehalf, + _receiver + ); + + if (_receiver == address(this)) + convertWethToEth(_isLoanEth, TokenInterface(wethAddr), _assets); + + setUint(_setId, _assets); + + _eventName = "LogBorrowOnBehalf(bytes32,uint256,uint256,address,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _assets, + _shareAmt, + _onBehalf, + _receiver, + _getId, + _setId + ); + } + + /** + * @notice Repay assets. + * @dev The market to repay assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to repay assets to. + * @param _assets The amount of assets to repay. (For max: `uint256(-1)`) + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens repaid. + */ + function repay( + MarketParams memory _marketParams, + uint256 _assets, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + uint256 _shares = 0; + + bool _isMax = _amt == type(uint256).max; + bool _isEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + uint256 _maxDsaBalance; + uint256 _borrowedShareAmt; + + if (_amt == type(uint256).max) { + _maxDsaBalance = _isEth + ? address(this).balance + : TokenInterface(_marketParams.loanToken).balanceOf( + address(this) + ); + + uint256 _amtDebt; + (_amtDebt, _borrowedShareAmt) = getPaybackBalance( + _id, + _marketParams, + address(this) + ); + + // Amount is minimum of dsa balance or debt + _amt = UtilsLib.min(_maxDsaBalance, _amtDebt); + } + + convertEthToWeth( + _isEth, + TokenInterface(_marketParams.loanToken), + _amt + ); + + approve( + TokenInterface(_marketParams.loanToken), + address(MORPHO_BLUE), + _amt + ); + + if (_isMax && _amt < _maxDsaBalance) { + // case of max shares burn + _shares = _borrowedShareAmt; + _amt = 0; + } + + (_assets, _shares) = MORPHO_BLUE.repay( + _marketParams, + _amt, + _shares, + address(this), + new bytes(0) + ); + + setUint(_setId, _assets); + + _eventName = "LogRepay(bytes32,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(_id, _assets, _shares, _getId, _setId); + } + + /** + * @notice Repays assets on behalf. + * @dev The market to repay assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to repay assets to. + * @param _assets The amount of assets to repay. (For max: `uint256(-1)`) + * @param _onBehalf The address whose loan will be repaid. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens repaid. + */ + function repayOnBehalf( + MarketParams memory _marketParams, + uint256 _assets, + address _onBehalf, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(_getId, _assets); + uint256 _shares = 0; + + bool _isEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + uint256 _maxDsaBalance; + uint256 _borrowedShareAmt; + + if (_amt == type(uint256).max) { + _maxDsaBalance = _isEth + ? address(this).balance + : TokenInterface(_marketParams.loanToken).balanceOf( + address(this) + ); + + uint256 _amtDebt; + (_amtDebt, _borrowedShareAmt) = getPaybackBalance( + _id, + _marketParams, + _onBehalf + ); + + // Amount is minimum of dsa balance or debt + _amt = UtilsLib.min(_maxDsaBalance, _amtDebt); + } + + convertEthToWeth( + _isEth, + TokenInterface(_marketParams.loanToken), + _amt + ); + + approve( + TokenInterface(_marketParams.loanToken), + address(MORPHO_BLUE), + _amt + ); + + if (_amt == type(uint256).max && _amt < _maxDsaBalance) { + // Case for max shares burn + _shares = _borrowedShareAmt; + _amt = 0; + } + + (_assets, _shares) = MORPHO_BLUE.repay( + _marketParams, + _amt, + _shares, + _onBehalf, + new bytes(0) + ); + + setUint(_setId, _assets); + + _eventName = "LogRepayOnBehalf(bytes32,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _assets, + _shares, + _onBehalf, + _getId, + _setId + ); + } + + /** + * @notice Repays shares on behalf. + * @dev The market to repay assets to. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param _marketParams The market to repay assets to. + * @param _shares The amount of shares to burn. (For max: `uint256(-1)`) + * @param _onBehalf The address whose loan will be repaid. + * @param _getId ID to retrieve amt. + * @param _setId ID stores the amount of tokens repaid. + */ + function repayOnBehalfShares( + MarketParams memory _marketParams, + uint256 _shares, + address _onBehalf, + uint256 _getId, + uint256 _setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt; + uint256 _shareAmt = getUint(_getId, _shares); + + bool _isEth = _marketParams.loanToken == ethAddr; + + _marketParams = updateTokenAddresses(_marketParams); + + Id _id = _marketParams.id(); + + uint256 _borrowedShareAmt; + uint256 _maxDsaBalance; + + if (_shareAmt == type(uint256).max) { + _maxDsaBalance = _isEth + ? address(this).balance + : TokenInterface(_marketParams.loanToken).balanceOf( + address(this) + ); + + uint256 _assetsAmt; + (_assetsAmt, _borrowedShareAmt) = getPaybackBalance( + _id, + _marketParams, + _onBehalf + ); + + _amt = UtilsLib.min(_maxDsaBalance, _assetsAmt); + } else { + ( + , + , + uint256 totalBorrowAssets, + uint256 totalBorrowShares + ) = MORPHO_BLUE.expectedMarketBalances(_marketParams); + + _amt = _shareAmt.toAssetsUp(totalBorrowAssets, totalBorrowShares); + } + + convertEthToWeth( + _isEth, + TokenInterface(_marketParams.loanToken), + _amt + ); + + approve( + TokenInterface(_marketParams.loanToken), + address(MORPHO_BLUE), + _amt + ); + + if (_shareAmt == type(uint256).max && _amt < _maxDsaBalance) { + _shareAmt = _borrowedShareAmt; + _amt = 0; + } else { + _shareAmt = 0; + } + + (_amt, ) = MORPHO_BLUE.repay( + _marketParams, + _amt, + _shareAmt, + _onBehalf, + new bytes(0) + ); + + setUint(_setId, _amt); + + _eventName = "LogRepayOnBehalf(bytes32,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + _id, + _amt, + _shares, + _onBehalf, + _getId, + _setId + ); + } +} + +contract ConnectV2MorphoBlue is MorphoBlue { + string public constant name = "Morpho-Blue-v1.0"; +}