Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x2A66F992bF227D2e50eF19EDD21503C3c4F3f682
Balance 0 ETH
Nonce 1
Code Size 6411 bytes
Indexed Transactions 2 (24,437,33124,437,333)
Gas Used (indexed) 853,785
External Etherscan · Sourcify

Contract Bytecode

6411 bytes
0x604060808152600480361015610013575f80fd5b5f3560e01c9081632b97236d146105bc5781633acb56241461054e57816348c4cf8c146105095781635d150a29146104585781637bde82f21461032c57816383df6747146102d857816389a302711461026a578163cb6d71bc146101fc578163f7f6ba531461018e578163f866af0f14610107575063fcd72b8614610096575f80fd5b34610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc168152f35b5f80fd5b82346101035760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101035760209135906101566101476106a5565b61014f610781565b9084610bcf565b918151908152606435848201527f51bd79fe925d34dd35ece291269c5b8870db05f360c336d1cb6b778d2b252cc1823392a251908152f35b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f689555121e529ff0463e191f9bd9d1e496164a7168152f35b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e168152f35b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168152f35b82346101035760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576103256020926103166106a5565b61031e610781565b9135610bcf565b9051908152f35b823461010357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101035760206103c6926103696106a5565b83517fba087652000000000000000000000000000000000000000000000000000000008152823592810192835273ffffffffffffffffffffffffffffffffffffffff90911660208301523360408301529384918291606090910190565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc165af190811561044f575f9161041a575b6020925051908152f35b90506020823d602011610447575b8161043560209383610637565b81010312610103576020915190610410565b3d9150610428565b513d5f823e3d90fd5b905034610103576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261010357610493366106c8565b9060e4359173ffffffffffffffffffffffffffffffffffffffff8316830361010357610104359267ffffffffffffffff928385116101035736602386011215610103578401359283116101035736602484860101116101035760246104fd94019160c43590610a55565b82519182526020820152f35b82346101035760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576104fd610545366106c8565b60c435906107a6565b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc168152f35b8234610103576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576105f9610545366106c8565b825182815260e43560208201527f1b7ae6a70016176e99f0d91a76a774e1209b170912caca81475e3a2d85863af1843392a282519182526020820152f35b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761067857604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361010357565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60c0910112610103576040519060c0820182811067ffffffffffffffff8211176106785760405273ffffffffffffffffffffffffffffffffffffffff826004358281168103610103578152602435828116810361010357602082015260443582811681036101035760408201526064358281168103610103576060820152608435608082015260a43591821682036101035760a00152565b60443590811515820361010357565b9190826040910312610103576020825192015190565b91909173ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e166040928351957fc6e6f59200000000000000000000000000000000000000000000000000000000875260048701526020928387602481865afa968715610a0d575f976109da575b5061014485925f6108b89385519a8b9586947f107ddb03000000000000000000000000000000000000000000000000000000008652600486019060a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b60c48401528160e484015233610104840152306101248401527f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af19485156109d0575f905f96610994575b5083517fba0876520000000000000000000000000000000000000000000000000000000081526004810191909152336024820152306044820152919081908390815f81606481015b03925af192831561044f57505f92610966575b50509190565b90809250813d831161098d575b61097d8183610637565b8101031261010357515f80610960565b503d610973565b839296505f9391506109be61094d91863d88116109c9575b6109b68183610637565b810190610790565b979394509150610905565b503d6109ac565b83513d5f823e3d90fd5b919096508382813d8311610a06575b6109f38183610637565b8101031261010357905195610144610828565b503d6109e9565b85513d5f823e3d90fd5b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b9194939291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103610a92575093610a8e9394611016565b9091565b92610b475f60409493610b6d610aa88833610f33565b9573ffffffffffffffffffffffffffffffffffffffff958689519c8d998a9889977f58e83ce0000000000000000000000000000000000000000000000000000000008952600489019060a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b60c48701528660e487015216610104850152610140610124850152610144840191610a17565b03927f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af1928315610bc4575f93610ba557509190565b610bbf91935060403d6040116109c9576109b68183610637565b905090565b6040513d5f823e3d90fd5b9173ffffffffffffffffffffffffffffffffffffffff9182811691823303610f0957610c1f853033877f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481661176c565b15610e5b57827f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc166040948551937f46aa2f1200000000000000000000000000000000000000000000000000000000855260048501526020938481602481865afa908115610e51579082915f91610e20575b5010610dc35785517f6e553f65000000000000000000000000000000000000000000000000000000008082526004820192909252306024820152918490839060449082905f905af1918215610db9575f92610d82575b508551908152600481019190915273ffffffffffffffffffffffffffffffffffffffff90911660248201529181908390815f81604481015b03927f000000000000000000000000f689555121e529ff0463e191f9bd9d1e496164a7165af192831561044f57505f92610d5857505090565b90809250813d8311610d7b575b610d6f8183610637565b81010312610103575190565b503d610d65565b929150938383813d8311610db2575b610d9b8183610637565b810103126101035791519193909190610d1f610ce7565b503d610d91565b86513d5f823e3d90fd5b6064848751907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601560248201527f4465706f7369742065786365656473206c696d697400000000000000000000006044820152fd5b809250868092503d8311610e4a575b610e398183610637565b81010312610103578190515f610c91565b503d610e2f565b87513d5f823e3d90fd5b6040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481019490945273ffffffffffffffffffffffffffffffffffffffff16602484015250602090829060449082905f907f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc165af1908115610bc4575f91610ee6575090565b90506020813d602011610f01575b81610d6f60209383610637565b3d9150610ef4565b60046040517f82b42900000000000000000000000000000000000000000000000000000000008152fd5b610fc26020915f93610f7f8173ffffffffffffffffffffffffffffffffffffffff933090857f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481661176c565b6040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481019190915230602482015293849283919082906044820190565b03927f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e165af1908115610bc4575f91610ee6575090565b51906fffffffffffffffffffffffffffffffff8216820361010357565b93929360c08120925f956040516040810181811067ffffffffffffffff82111761067857604052600181526020368183013780511561173f5773ffffffffffffffffffffffffffffffffffffffff8516602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc163b156101035760405180917f78c56d650000000000000000000000000000000000000000000000000000000082528760048301526040602483015280518060448401526020606484019201905f5b8181106117105750505090805f9203818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af18015610bc4576116f3575b506040517f93c5206200000000000000000000000000000000000000000000000000000000815285600482015273ffffffffffffffffffffffffffffffffffffffff8516602482015260608160448173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165afa9081156114ee57889161167e575b506fffffffffffffffffffffffffffffffff6020820151161561167457604051957f5c60e39a000000000000000000000000000000000000000000000000000000008752600487015260e08660248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165afa9586156114ee5788966115ad575b506fffffffffffffffffffffffffffffffff6020820151166fffffffffffffffffffffffffffffffff60608160408a015116980151166001880180981161158057620f42408101918282116115535788810298818a04149015171561158057620f423f908282820111611553578801018097116115265780156114f957604051967fb3d7f6b900000000000000000000000000000000000000000000000000000000885204600487015260208660248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e165afa9586156114ee5788966114b0575b50611431926040949273ffffffffffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6020611457956113a78c33610f33565b5001511697875198899788977f58e83ce0000000000000000000000000000000000000000000000000000000008952600489019060a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b8c60c488015260e487015216610104850152610140610124850152610144840191610a17565b03818773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af19384156114a45793610ba557509190565b604051903d90823e3d90fd5b909550602093919293813d6020116114e6575b816114d060209383610637565b8101031261010357519490929190611431611368565b3d91506114c3565b6040513d8a823e3d90fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526012600452fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60248b7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b90955060e0813d60e01161166c575b816115c960e09383610637565b81010312611668576040519060e0820182811067ffffffffffffffff8211176106785761165c9160c0916040526115ff81610ff9565b845261160d60208201610ff9565b602085015261161e60408201610ff9565b604085015261162f60608201610ff9565b606085015261164060808201610ff9565b608085015261165160a08201610ff9565b60a085015201610ff9565b60c0820152945f611271565b8780fd5b3d91506115bc565b5050505050508190565b90506060813d6060116116eb575b8161169960609383610637565b8101031261166857604051906060820182811067ffffffffffffffff821117610678576116e0916040918252805184526116d560208201610ff9565b602085015201610ff9565b60408201525f6111db565b3d915061168c565b90965067ffffffffffffffff8111610678576040525f955f611145565b825173ffffffffffffffffffffffffffffffffffffffff168452859450602093840193909201916001016110f0565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91909273ffffffffffffffffffffffffffffffffffffffff80931692833b156118e157604051928160208501967f23b872dd00000000000000000000000000000000000000000000000000000000885216602485015216604483015260648201526064815260a081019167ffffffffffffffff9382841085851117610678575f809493819460405251925af13d156118d8573d918211610678576040519161183c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610637565b82523d5f602084013e5b156118ae57805190811515918261188a575b505061186057565b60046040517f1eded19c000000000000000000000000000000000000000000000000000000008152fd5b81925090602091810103126101035760200151801590811503610103575f80611858565b60046040517fe65b7a77000000000000000000000000000000000000000000000000000000008152fd5b60609150611846565b60046040517ff046a714000000000000000000000000000000000000000000000000000000008152fd

Verified Source Code Partial Match

Compiler: v0.8.22+commit.4fc1097e EVM: shanghai Optimization: Yes (999999 runs)
Helper.sol 163 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IMorpho, IMorphoCredit, Id, Market, Position} from "./interfaces/IMorpho.sol";
import {MarketParams} from "./interfaces/IMorpho.sol";
import {IHelper} from "./interfaces/IHelper.sol";
import {IUSD3} from "./interfaces/IUSD3.sol";
import {IERC20} from "./interfaces/IERC20.sol";

import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {SafeTransferLib} from "./libraries/SafeTransferLib.sol";
import {SharesMathLib} from "./libraries/SharesMathLib.sol";
import {MarketParamsLib} from "./libraries/MarketParamsLib.sol";

import {IERC4626} from "../lib/forge-std/src/interfaces/IERC4626.sol";

/// @title Helper
/// @author 3Jane
/// @custom:contact [email protected]
contract Helper is IHelper {
    using SafeTransferLib for IERC20;
    using SharesMathLib for uint256;
    using MarketParamsLib for MarketParams;

    /// @inheritdoc IHelper
    address public immutable MORPHO;
    /// @inheritdoc IHelper
    address public immutable USD3;
    /// @inheritdoc IHelper
    address public immutable sUSD3;
    /// @inheritdoc IHelper
    address public immutable USDC;
    /// @inheritdoc IHelper
    address public immutable WAUSDC;

    /* CONSTRUCTOR */

    constructor(address morpho, address usd3, address susd3, address usdc, address wausdc) {
        if (morpho == address(0)) revert ErrorsLib.ZeroAddress();
        if (usd3 == address(0)) revert ErrorsLib.ZeroAddress();
        if (susd3 == address(0)) revert ErrorsLib.ZeroAddress();
        if (usdc == address(0)) revert ErrorsLib.ZeroAddress();
        if (wausdc == address(0)) revert ErrorsLib.ZeroAddress();

        MORPHO = morpho;
        USD3 = usd3;
        sUSD3 = susd3;
        USDC = usdc;
        WAUSDC = wausdc;

        // Set max approvals
        IERC20(USDC).approve(WAUSDC, type(uint256).max);
        IERC20(USDC).approve(USD3, type(uint256).max);
        IERC20(WAUSDC).approve(MORPHO, type(uint256).max);
        IERC20(USD3).approve(sUSD3, type(uint256).max);
    }

    /// @inheritdoc IHelper
    function deposit(uint256 assets, address receiver, bool hop, bytes32 referral) external returns (uint256 shares) {
        (shares) = deposit(assets, receiver, hop);
        emit DepositReferred(msg.sender, assets, referral);
    }

    /// @inheritdoc IHelper
    function deposit(uint256 assets, address receiver, bool hop) public returns (uint256) {
        if (msg.sender != receiver) revert ErrorsLib.Unauthorized();
        IERC20(USDC).safeTransferFrom(msg.sender, address(this), assets);

        if (hop) {
            require(IUSD3(USD3).availableDepositLimit(receiver) >= assets, "Deposit exceeds limit");
            uint256 usd3Shares = IUSD3(USD3).deposit(assets, address(this));
            return IERC4626(sUSD3).deposit(usd3Shares, receiver);
        } else {
            return IUSD3(USD3).deposit(assets, receiver);
        }
    }

    /// @inheritdoc IHelper
    function redeem(uint256 shares, address receiver) external returns (uint256) {
        uint256 usdcAssets = IUSD3(USD3).redeem(shares, receiver, msg.sender);
        return usdcAssets;
    }

    /// @inheritdoc IHelper
    function borrow(MarketParams memory marketParams, uint256 assets, bytes32 referral)
        external
        returns (uint256 amount, uint256 shares)
    {
        (amount, shares) = borrow(marketParams, assets);
        emit BorrowReferred(msg.sender, amount, referral);
    }

    /// @inheritdoc IHelper
    function borrow(MarketParams memory marketParams, uint256 assets) public returns (uint256, uint256) {
        uint256 waUsdcShares = IERC4626(WAUSDC).convertToShares(assets);
        (uint256 waUSDCAmount, uint256 shares) =
            IMorpho(MORPHO).borrow(marketParams, waUsdcShares, 0, msg.sender, address(this));
        uint256 usdcAmount = IERC4626(WAUSDC).redeem(waUSDCAmount, msg.sender, address(this));
        return (usdcAmount, shares);
    }

    /// @inheritdoc IHelper
    function repay(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
        external
        returns (uint256, uint256)
    {
        // Check if this is a full repayment request
        if (assets == type(uint256).max) {
            return _repayFull(marketParams, onBehalf, data);
        } else {
            // Normal partial repayment flow
            uint256 waUSDCAmount = _wrap(msg.sender, assets);
            (, uint256 shares) = IMorpho(MORPHO).repay(marketParams, waUSDCAmount, 0, onBehalf, data);
            return (assets, shares);
        }
    }

    function _repayFull(MarketParams memory marketParams, address onBehalf, bytes calldata data)
        internal
        returns (uint256, uint256)
    {
        Id id = marketParams.id();

        // Accrue premium first to get accurate borrow shares
        _accruePremiumsForBorrower(id, onBehalf);

        // Get current borrow shares after premium accrual
        Position memory pos = IMorpho(MORPHO).position(id, onBehalf);

        // If no debt, return early
        if (pos.borrowShares == 0) {
            return (0, 0);
        }

        // Get market state to calculate assets needed
        Market memory market = IMorpho(MORPHO).market(id);

        // Calculate waUSDC assets needed (rounds up like Morpho does)
        uint256 waUsdcNeeded = uint256(pos.borrowShares).toAssetsUp(market.totalBorrowAssets, market.totalBorrowShares);

        // Convert to USDC amount needed (preview how much USDC needed to mint waUsdcNeeded)
        uint256 usdcNeeded = IERC4626(WAUSDC).previewMint(waUsdcNeeded);

        // Pull USDC from user and wrap to waUSDC
        _wrap(msg.sender, usdcNeeded);

        // Repay with shares to ensure complete repayment
        (, uint256 sharesRepaid) = IMorpho(MORPHO).repay(marketParams, 0, pos.borrowShares, onBehalf, data);

        return (usdcNeeded, sharesRepaid);
    }

    function _wrap(address from, uint256 assets) internal returns (uint256) {
        IERC20(USDC).safeTransferFrom(from, address(this), assets);
        return IERC4626(WAUSDC).deposit(assets, address(this));
    }

    function _accruePremiumsForBorrower(Id id, address borrower) internal {
        address[] memory borrowers = new address[](1);
        borrowers[0] = borrower;
        IMorphoCredit(MORPHO).accruePremiumsForBorrowers(id, borrowers);
    }
}
IMorpho.sol 432 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.18;

type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
    address creditLine;
}

/// @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.
/// @dev Warning: `totalMarkdownAmount` may be stale as markdowns are only updated when borrowers are touched.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
    uint128 totalMarkdownAmount; // Running tally of all borrower markdowns
}

/// @notice Per-borrower premium tracking
/// @param lastAccrualTime Timestamp of the last premium accrual for this borrower
/// @param rate Current risk premium rate per second (scaled by WAD)
/// @param borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
struct BorrowerPremium {
    uint128 lastAccrualTime;
    uint128 rate;
    uint128 borrowAssetsAtLastAccrual;
}

/// @notice Repayment tracking structures
enum RepaymentStatus {
    Current,
    GracePeriod,
    Delinquent,
    Default
}

struct PaymentCycle {
    uint256 endDate;
}

struct RepaymentObligation {
    uint128 paymentCycleId;
    uint128 amountDue;
    uint128 endingBalance;
}

/// @notice Markdown state for tracking defaulted debt value reduction
/// @param lastCalculatedMarkdown Last calculated markdown amount
struct MarkdownState {
    uint128 lastCalculatedMarkdown;
}

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 chains sharing the
    /// same chain id and on forks 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 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 assumptions on the market's dependencies which, if broken, could break Morpho's liveness
    /// properties (funds could get stuck):
    /// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
    /// - The amount of assets supplied and borrowed should not go above ~1e35 (otherwise the computation of
    /// `toSharesUp` and `toSharesDown` can overflow).
    /// - The IRM should not revert on `borrowRate`.
    /// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
    /// `_accrueInterest` can overflow).
    /// - The oracle should not revert `price`.
    /// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
    /// `assetsRepaid` in `liquidate` can 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
    /// `onMorphoRepay` 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 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,
            uint128 totalMarkdownAmount
        );

    /// @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,
            address creditLine
        );
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @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);
}

/// @title IMorphoCredit
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorphoCredit {
    /// @notice The helper of the contract.
    function helper() external view returns (address);

    /// @notice The usd3 contract
    function usd3() external view returns (address);

    /// @notice The protocol config of the contract.
    function protocolConfig() external view returns (address);

    /// @notice Sets `helper` as `helper` of the contract.
    /// @param newHelper The new helper address
    function setHelper(address newHelper) external;

    /// @notice Sets `usd3` as `usd3` of the contract.
    /// @param newUsd3 The new usd3 address
    function setUsd3(address newUsd3) external;

    /// @notice Sets the credit line and premium rate for a borrower
    /// @param id The market ID
    /// @param borrower The borrower address
    /// @param credit The credit line amount
    /// @param drp The drp per second in WAD
    function setCreditLine(Id id, address borrower, uint256 credit, uint128 drp) external;

    /// @notice Returns the premium data for a specific borrower in a market
    /// @param id The market ID
    /// @param borrower The borrower address
    /// @return lastAccrualTime Timestamp of the last premium accrual
    /// @return rate Current risk premium rate per second (scaled by WAD)
    /// @return borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
    function borrowerPremium(Id id, address borrower)
        external
        view
        returns (uint128 lastAccrualTime, uint128 rate, uint128 borrowAssetsAtLastAccrual);

    /// @notice Batch accrue premiums for multiple borrowers
    /// @param id Market ID
    /// @param borrowers Array of borrower addresses
    /// @dev Gas usage scales linearly with array size. Callers should manage batch sizes based on block gas limits.
    function accruePremiumsForBorrowers(Id id, address[] calldata borrowers) external;

    /// @notice Close a payment cycle and post obligations for multiple borrowers
    /// @param id Market ID
    /// @param endDate Cycle end date
    /// @param borrowers Array of borrower addresses
    /// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
    /// @param endingBalances Array of ending balances for penalty calculations
    function closeCycleAndPostObligations(
        Id id,
        uint256 endDate,
        address[] calldata borrowers,
        uint256[] calldata repaymentBps,
        uint256[] calldata endingBalances
    ) external;

    /// @notice Add obligations to the latest payment cycle
    /// @param id Market ID
    /// @param borrowers Array of borrower addresses
    /// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
    /// @param endingBalances Array of ending balances
    function addObligationsToLatestCycle(
        Id id,
        address[] calldata borrowers,
        uint256[] calldata repaymentBps,
        uint256[] calldata endingBalances
    ) external;

    /// @notice Get repayment obligation for a borrower
    /// @param id Market ID
    /// @param borrower Borrower address
    /// @return cycleId The payment cycle ID
    /// @return amountDue The amount due
    /// @return endingBalance The ending balance for penalty calculations
    function repaymentObligation(Id id, address borrower)
        external
        view
        returns (uint128 cycleId, uint128 amountDue, uint128 endingBalance);

    /// @notice Get payment cycle end date
    /// @param id Market ID
    /// @param cycleId Cycle ID
    /// @return endDate The cycle end date
    function paymentCycle(Id id, uint256 cycleId) external view returns (uint256 endDate);

    /// @notice Settle a borrower's account by writing off all remaining debt
    /// @dev Only callable by credit line contract
    /// @dev Should be called after any partial repayments have been made
    /// @param marketParams The market parameters
    /// @param borrower The borrower whose account to settle
    /// @return writtenOffAssets Amount of assets written off
    /// @return writtenOffShares Amount of shares written off
    function settleAccount(MarketParams memory marketParams, address borrower)
        external
        returns (uint256 writtenOffAssets, uint256 writtenOffShares);

    /// @notice Get markdown state for a borrower
    /// @param id Market ID
    /// @param borrower Borrower address
    /// @return lastCalculatedMarkdown Last calculated markdown amount
    function markdownState(Id id, address borrower) external view returns (uint128 lastCalculatedMarkdown);
}
IHelper.sol 95 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IERC4626} from "../../lib/forge-std/src/interfaces/IERC4626.sol";

import {MarketParams} from "./IMorpho.sol";

/// @title IHelper
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper contract to simplify interactions with 3Jane's token ecosystem and Morpho protocol
/// @dev This contract handles token conversions for USD3 (post-reinitialize) and MorphoCredit operations
/// @dev After USD3's reinitialize(), the flow is: USDC → USD3 (→ sUSD3 if hop=true)
/// @dev MorphoCredit still uses waUSDC, so borrow/repay operations handle the USDC ↔ waUSDC conversion
interface IHelper {
    event DepositReferred(address indexed depositor, uint256 amount, bytes32 code);
    event BorrowReferred(address indexed borrower, uint256 amount, bytes32 code);

    /// @notice The Morpho protocol contract address
    /// @return The address of the Morpho contract
    function MORPHO() external view returns (address);

    /// @notice The USD3 token address (ERC4626 vault, accepts USDC directly after reinitialize)
    /// @return The address of the USD3 token
    function USD3() external view returns (address);

    /// @notice The sUSD3 token address (subordinate/staked USD3, also ERC4626)
    /// @return The address of the sUSD3 token
    function sUSD3() external view returns (address);

    /// @notice The USDC token address (base stablecoin)
    /// @return The address of the USDC token
    function USDC() external view returns (address);

    /// @notice The waUSDC token address (wrapped asset USDC, ERC4626 vault)
    /// @return The address of the waUSDC token
    function WAUSDC() external view returns (address);

    /// @notice Deposits USDC into USD3 (and optionally sUSD3)
    /// @dev After USD3's reinitialize(), flow is: USDC → USD3 (→ sUSD3 if hop=true)
    /// @dev USD3 handles USDC directly and manages waUSDC wrapping internally
    /// @param assets The amount of USDC to deposit
    /// @param receiver The address that will receive the USD3/sUSD3 shares
    /// @param hop If true, deposits into sUSD3; if false, stops at USD3
    /// @return The amount of shares minted (USD3 or sUSD3 depending on hop)
    function deposit(uint256 assets, address receiver, bool hop) external returns (uint256);

    /// @notice Deposits USDC into USD3 (and optionally sUSD3) with referral tracking
    /// @dev Same as deposit() but emits DepositReferred event for referral tracking
    /// @param assets The amount of USDC to deposit
    /// @param receiver The address that will receive the USD3/sUSD3 shares
    /// @param hop If true, deposits into sUSD3; if false, stops at USD3
    /// @param referral Referral code for tracking purposes
    /// @return The amount of shares minted (USD3 or sUSD3 depending on hop)
    function deposit(uint256 assets, address receiver, bool hop, bytes32 referral) external returns (uint256);

    /// @notice Redeems USD3 shares back to USDC
    /// @dev After USD3's reinitialize(), USD3 returns USDC directly
    /// @dev Caller must have approved Helper for USD3 spending
    /// @param shares The amount of USD3 shares to redeem
    /// @param receiver The address that will receive the USDC
    /// @return The amount of USDC received
    function redeem(uint256 shares, address receiver) external returns (uint256);

    /// @notice Borrows assets from a Morpho market and unwraps to USDC
    /// @dev The borrowed waUSDC is automatically unwrapped to USDC for the borrower
    /// @param marketParams The market parameters defining which market to borrow from
    /// @param assets The amount of assets to borrow (in waUSDC terms)
    /// @return usdcAmount The amount of USDC received by the borrower
    /// @return shares The amount of borrow shares created
    function borrow(MarketParams memory marketParams, uint256 assets) external returns (uint256, uint256);

    /// @notice Borrows assets from a Morpho market and unwraps to USDC with referral tracking
    /// @dev Same as borrow() but emits BorrowReferred event for referral tracking
    /// @param marketParams The market parameters defining which market to borrow from
    /// @param assets The amount of assets to borrow (in waUSDC terms)
    /// @param referral Referral code for tracking purposes
    /// @return usdcAmount The amount of USDC received by the borrower
    /// @return shares The amount of borrow shares created
    function borrow(MarketParams memory marketParams, uint256 assets, bytes32 referral)
        external
        returns (uint256, uint256);

    /// @notice Repays a loan by wrapping USDC to waUSDC
    /// @dev Flow: USDC → waUSDC → Morpho repay. Caller must have approved Helper for USDC spending
    /// @param marketParams The market parameters defining which market to repay
    /// @param assets The amount of USDC to repay
    /// @param onBehalf The address whose debt is being repaid
    /// @param data Additional data for the repay operation (e.g., for callbacks)
    /// @return usdcAmount The amount of USDC used from the caller
    /// @return shares The amount of borrow shares repaid
    function repay(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
        external
        returns (uint256, uint256);
}
IUSD3.sol 17 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IERC4626} from "../../lib/forge-std/src/interfaces/IERC4626.sol";

/// @title IUSD3
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface for USD3 token, which is an ERC4626 vault
interface IUSD3 is IERC4626 {
    // USD3 inherits all ERC4626 functions including:
    // - deposit(uint256 assets, address receiver) returns (uint256 shares)
    // - redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)
    // - And all other ERC4626 standard functions
    function whitelist(address user) external view returns (bool);
    function availableDepositLimit(address owner) external view returns (uint256);
}
IERC20.sol 11 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title IERC20
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Empty because we only call library functions. It prevents calling transfer (transferFrom) instead of
/// safeTransfer (safeTransferFrom).
interface IERC20 {
    function approve(address spender, uint256 value) external returns (bool);
}
ErrorsLib.sol 146 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing custom errors.
library ErrorsLib {
    /// @notice Thrown when the caller is not the owner.
    error NotOwner();

    /// @notice Thrown when the caller is not the market's credit line.
    error NotCreditLine();

    /// @notice Thrown when the caller is not the market's helper.
    error NotHelper();

    /// @notice Thrown when the caller is not the market's usd3.
    error NotUsd3();

    /// @notice Thrown when the caller is not the owner or ozd.
    error NotOwnerOrOzd();

    /// @notice Thrown when the user is unverified.
    error Unverified();

    /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
    error MaxLltvExceeded();

    /// @notice Thrown when the LTV to enable exceeds the maximum LTV.
    error MaxLtvExceeded();

    /// @notice Thrown when the VV to enable exceeds the maximum VV.
    error MaxVvExceeded();

    /// @notice Thrown when the credit to enable exceeds the maximum credit.
    error MaxCreditLineExceeded();

    /// @notice Thrown when the credit to enable is below the minimum credit.
    error MinCreditLineExceeded();

    /// @notice Thrown when the fee to set exceeds the maximum fee.
    error MaxFeeExceeded();

    /// @notice Thrown when the value is already set.
    error AlreadySet();

    /// @notice Thrown when the IRM is not enabled at market creation.
    error IrmNotEnabled();

    /// @notice Thrown when the LLTV is not enabled at market creation.
    error LltvNotEnabled();

    /// @notice Thrown when the market is already created.
    error MarketAlreadyCreated();

    /// @notice Thrown when a token to transfer doesn't have code.
    error NoCode();

    /// @notice Thrown when the market is not created.
    error MarketNotCreated();

    /// @notice Thrown when not exactly one of the input amount is zero.
    error InconsistentInput();

    /// @notice Thrown when zero assets is passed as input.
    error ZeroAssets();

    /// @notice Thrown when a zero address is passed as input.
    error ZeroAddress();

    /// @notice Thrown when an array has an invalid length.
    error InvalidArrayLength();

    /// @notice Thrown when the caller is not authorized to conduct an action.
    error Unauthorized();

    /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
    error InsufficientCollateral();

    /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
    error InsufficientLiquidity();

    /// @notice Thrown when borrowing shares would result in borrowing zero assets.
    error InsufficientBorrowAmount();

    /// @notice Thrown when a token transfer reverted.
    error TransferReverted();

    /// @notice Thrown when a token transfer returned false.
    error TransferReturnedFalse();

    /// @notice Thrown when a token transferFrom reverted.
    error TransferFromReverted();

    /// @notice Thrown when a token transferFrom returned false
    error TransferFromReturnedFalse();

    /// @notice Thrown when the maximum uint128 is exceeded.
    error MaxUint128Exceeded();

    /// @notice Thrown when the premium rate exceeds the maximum allowed.
    error MaxDrpExceeded();

    /// @notice Thrown when the borrower has outstanding repayment obligations.
    error OutstandingRepayment();

    /// @notice Thrown when the protocol is paused.
    error Paused();

    /// @notice Thrown when trying to close a future cycle.
    error CannotCloseFutureCycle();

    /// @notice Thrown when cycle duration is invalid.
    error InvalidCycleDuration();

    /// @notice Thrown when no payment cycles exist.
    error NoCyclesExist();

    /// @notice Thrown when cycle ID is invalid.
    error InvalidCycleId();

    /// @notice Thrown when partial payment is attempted but full obligation payment is required.
    error MustPayFullObligation();

    /// @notice Thrown when repayment basis points exceed 100%.
    error RepaymentExceedsHundredPercent();

    /// @notice Thrown when an invalid markdown manager is set.
    error InvalidMarkdownManager();

    /// @notice Thrown when trying to settle non-existent debt.
    error NoAccountToSettle();

    /// @notice Thrown when the cover amount exceeds the assets amount.
    error InvalidCoverAmount();

    /// @notice Thrown when attempting operations on a frozen market.
    error MarketFrozen();

    /// @notice Thrown when a borrow would exceed the protocol debt cap.
    error DebtCapExceeded();

    /// @notice Thrown when borrow or repay would result in debt below minimum borrow amount.
    error BelowMinimumBorrow();
}
SafeTransferLib.sol 36 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {IERC20} from "../interfaces/IERC20.sol";

import {ErrorsLib} from "../libraries/ErrorsLib.sol";

interface IERC20Internal {
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

/// @title SafeTransferLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to manage transfers of tokens, even if calls to the transfer or transferFrom functions are not
/// returning a boolean.
library SafeTransferLib {
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        if (address(token).code.length == 0) revert ErrorsLib.NoCode();

        (bool success, bytes memory returndata) =
            address(token).call(abi.encodeCall(IERC20Internal.transfer, (to, value)));
        if (!success) revert ErrorsLib.TransferReverted();
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) revert ErrorsLib.TransferReturnedFalse();
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        if (address(token).code.length == 0) revert ErrorsLib.NoCode();

        (bool success, bytes memory returndata) =
            address(token).call(abi.encodeCall(IERC20Internal.transferFrom, (from, to, value)));
        if (!success) revert ErrorsLib.TransferFromReverted();
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) revert ErrorsLib.TransferFromReturnedFalse();
    }
}
SharesMathLib.sol 45 lines
// 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 [email protected]
/// @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);
    }
}
MarketParamsLib.sol 21 lines
// 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 [email protected]
/// @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 6 * 32 because `MarketParams` has 6 variables of 32 bytes each.
    uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 6 * 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)
        }
    }
}
IERC4626.sol 190 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

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

/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
/// https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 is IERC20 {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    );

    /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    /// @dev
    /// - MUST be an ERC-20 token contract.
    /// - MUST NOT revert.
    function asset() external view returns (address assetTokenAddress);

    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.
    /// @dev
    /// - SHOULD include any compounding that occurs from yield.
    /// - MUST be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT revert.
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
    /// scenario where all the conditions are met.
    /// @dev
    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT show any variations depending on the caller.
    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    /// - MUST NOT revert.
    ///
    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    /// from.
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
    /// scenario where all the conditions are met.
    /// @dev
    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT show any variations depending on the caller.
    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    /// - MUST NOT revert.
    ///
    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    /// from.
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
    /// through a deposit call.
    /// @dev
    /// - MUST return a limited value if receiver is subject to some deposit limit.
    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
    /// - MUST NOT revert.
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
    /// current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
    ///   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
    ///   in the same transaction.
    /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
    ///   deposit would be accepted, regardless if the user has enough tokens approved, etc.
    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    /// @dev
    /// - MUST emit the Deposit event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   deposit execution, and are accounted for during deposit.
    /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
    ///   approving enough underlying tokens to the Vault contract, etc).
    ///
    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
    /// @dev
    /// - MUST return a limited value if receiver is subject to some mint limit.
    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
    /// - MUST NOT revert.
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
    /// current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
    ///   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
    ///   same transaction.
    /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
    ///   would be accepted, regardless if the user has enough tokens approved, etc.
    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by minting.
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    /// @dev
    /// - MUST emit the Deposit event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
    ///   execution, and are accounted for during mint.
    /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
    ///   approving enough underlying tokens to the Vault contract, etc).
    ///
    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
    /// Vault, through a withdrawal call.
    /// @dev
    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    /// - MUST NOT revert.
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
    /// given current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
    ///   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
    ///   called
    ///   in the same transaction.
    /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
    ///   the withdrawal would be accepted, regardless if the user has enough shares, etc.
    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    /// @dev
    /// - MUST emit the Withdraw event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   withdraw execution, and are accounted for during withdrawal.
    /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
    ///   not having enough shares, etc).
    ///
    /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    /// Those methods should be performed separately.
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
    /// through a redeem call.
    /// @dev
    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
    /// - MUST NOT revert.
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
    /// given current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
    ///   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
    ///   same transaction.
    /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
    ///   redemption would be accepted, regardless if the user has enough shares, etc.
    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by redeeming.
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    /// @dev
    /// - MUST emit the Withdraw event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   redeem execution, and are accounted for during redeem.
    /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
    ///   not having enough shares, etc).
    ///
    /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    /// Those methods should be performed separately.
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
MathLib.sol 75 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

uint256 constant WAD = 1e18;

/// @title MathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @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;
    }

    /// @dev Computes the inverse of wTaylorCompounded, finding the rate that produces the given growth factor.
    /// Uses a 3-term Taylor series approximation of ln(x) to solve for rate in the compound interest formula.
    /// Formula: rate = ln(x) / n ≈ [(x-1) - (x-1)²/2 + (x-1)³/3] / n
    ///
    /// Accuracy notes:
    /// - The Taylor approximation of ln(x) is most accurate for x close to 1
    /// - At growth factor x = 1.69*WAD (69% growth), approximation error < 2%
    /// - At growth factor x = 2*WAD (100% growth, where ln(2) ≈ 0.69), approximation error < 5%
    /// - Accuracy decreases for larger growth factors; not recommended for x > 2.5*WAD (150% growth)
    ///
    /// Example: If debt grew from 1000 to 1105 over 1 year (10.5% growth):
    /// - x = 1.105*WAD (growth factor)
    /// - n = 365 days (time period)
    /// - Returns ~10% APR as rate per second
    ///
    /// @param x The growth factor scaled by WAD (e.g., 1.1*WAD for 10% growth). Must be >= WAD.
    /// @param n The time period over which the growth occurred (in seconds)
    /// @return The continuously compounded rate per second that would produce this growth (scaled by WAD)
    function wInverseTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        require(x >= WAD, "ln undefined");

        uint256 firstTerm = x - WAD;
        uint256 secondTerm = wMulDown(firstTerm, firstTerm);
        uint256 thirdTerm = wMulDown(secondTerm, firstTerm);

        uint256 series = firstTerm - secondTerm / 2 + thirdTerm / 3;

        return series / n;
    }
}
IERC20.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
    /// is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Moves `amount` tokens from the caller's account to `to`.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` is allowed
    /// to spend on behalf of `owner`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
    /// `amount` is then deducted from the caller's allowance.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token.
    function symbol() external view returns (string memory);

    /// @notice Returns the decimals places of the token.
    function decimals() external view returns (uint8);
}

Read Contract

MORPHO 0x3acb5624 → address
USD3 0xfcd72b86 → address
USDC 0x89a30271 → address
WAUSDC 0xcb6d71bc → address
sUSD3 0xf7f6ba53 → address

Write Contract 6 functions

These functions modify contract state and require a wallet transaction to execute.

borrow 0x5a36289e
tuple marketParams
uint256 assets
bytes32 referral
returns: uint256, uint256
borrow 0x0011c388
tuple marketParams
uint256 assets
returns: uint256, uint256
deposit 0x83df6747
uint256 assets
address receiver
bool hop
returns: uint256
deposit 0xf866af0f
uint256 assets
address receiver
bool hop
bytes32 referral
returns: uint256
redeem 0x7bde82f2
uint256 shares
address receiver
returns: uint256
repay 0x118df897
tuple marketParams
uint256 assets
address onBehalf
bytes data
returns: uint256, uint256

Top Interactions

AddressTxnsSentReceived
0xB93D314D...8d19 1 1
0xFf2C13A2...EfdE 1 1

Recent Transactions

CSV
|
Hash Method Block Age From/To Value Txn Fee Type
0x1d956148...08ebf7 0x7bde82f2 24,437,333 IN 0xB93D314D...8d19 0 ETH EIP-1559
0x00d2aa06...a9ac6b 0x48c4cf8c 24,437,331 IN 0xFf2C13A2...EfdE 0 ETH EIP-1559