Skip to content

Morpho Borrow Integration Guide - Solidity

This guide explains how to integrate the borrowing functionality of Morpho into your application. It covers four core functions: supplying collateral, borrowing assets, repaying debt, and withdrawing collateral.

Market Parameters

Every interaction with Morpho requires specifying a market using the MarketParams structure:

struct MarketParams {
    address loanToken;        // Token that can be borrowed and supplied
    address collateralToken;  // Token that can be used as collateral
    address oracle;           // Oracle that can be used to price loan and collateral token
    address irm;              // Interest rate model
    uint256 lltv;             // Liquidation loan-to-value ratio
}

Each market is uniquely identified by these parameters, which must be passed to every function call.

Understanding Assets vs. Shares

Morpho offers two approaches for most operations:

  • Asset-First Approach: Specify the exact token amount (e.g., "1000 USDC")
  • Shares-First Approach: Specify the exact shares amount (internal accounting units)

This dual approach enables both intuitive user experiences and precision accounting.

ConceptDescription
AssetsThe actual tokens (USDC, DAI, etc.) with their respective decimals
SharesInternal accounting units that represent proportional ownership

Supplying Collateral

Before borrowing, supply collateral to secure the loan position.

Function Signature

function supplyCollateral(
    MarketParams memory marketParams,
    uint256 assets,
    address onBehalf,
    bytes memory data
) external;

Required Steps

  1. Approve token transfer:

    IERC20(collateralToken).approve(morphoAddress, collateralAmount);
  2. Supply collateral:

    IMorpho(morphoAddress).supplyCollateral(
        marketParams,
        collateralAmount,
        address(this),  // Supply on behalf of this contract
        ""              // No callback data needed
    );

Important Notes

  • This function only accepts assets (no shares parameter)
  • Collateral does not earn yield in Morpho's base implementation
  • Ensure sufficient token approval before calling

Borrowing Assets

After supplying collateral, borrow assets against it.

Function Signature

function borrow(
    MarketParams memory marketParams,
    uint256 assets,
    uint256 shares,
    address onBehalf,
    address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

Required Steps

// Specify exact amount of assets to borrow
(uint256 assetsBorrowed, uint256 sharesBorrowed) = IMorpho(morphoAddress).borrow(
    marketParams,
    borrowAmount,   // Amount of assets to borrow
    0,              // Use 0 for shares when specifying assets
    address(this),  // Borrow on behalf of this contract
    address(this)   // Send borrowed assets to this contract
);

Asset-First vs. Shares-First Approach

  • Use assets (assets > 0, shares = 0): For borrowing a specific token amount ✓
  • Use shares (assets = 0, shares > 0): For advanced use cases requiring share precision

Borrow Limits

Borrowing is limited by:

  • Collateral value
  • Market's LLTV (Liquidation Loan-To-Value)
  • Position health requirement (debt ≤ collateral × LLTV)

Repaying Debt

Repay borrowed assets to reduce or eliminate debt.

Function Signature

function repay(
    MarketParams memory marketParams,
    uint256 assets,
    uint256 shares,
    address onBehalf,
    bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

Required Steps

  1. Approve token transfer:

    IERC20(loanToken).approve(morphoAddress, repayAmount);
  2. Partial repayment (asset-based):

    (uint256 assetsRepaid, uint256 sharesRepaid) = IMorpho(morphoAddress).repay(
        marketParams,
        repayAmount,   // Amount of assets to repay
        0,             // Use 0 for shares when specifying assets
        address(this), // Repay on behalf of this contract
        ""             // No callback data needed
    );
  3. Full repayment (share-based - recommended):

    // Fetch current borrow shares from position
    uint256 borrowShares = position[id][address(this)].borrowShares;
     
    (uint256 assetsRepaid, uint256 sharesRepaid) = IMorpho(morphoAddress).repay(
        marketParams,
        0,             // Use 0 for assets when specifying shares
        borrowShares,  // Repay exact shares amount (full repayment)
        address(this), // Repay on behalf of this contract
        ""             // No callback data needed
    );

When to Use Each Approach

  • For partial repayments: Use assets parameter (assets > 0, shares = 0)
  • For full repayments: Use shares parameter (assets = 0, shares > 0) to avoid dust

Security Considerations

  • Always check for sufficient approvals before calling
  • Always check for the health factor of the future loan position. Consider always adding buffer for users for protection.

Withdrawing Collateral

Retrieve collateral after repaying debt.

Function Signature

function withdrawCollateral(
    MarketParams memory marketParams,
    uint256 assets,
    address onBehalf,
    address receiver
) external;

Required Steps

IMorpho(morphoAddress).withdrawCollateral(
    marketParams,
    withdrawAmount, // Amount of collateral to withdraw
    address(this),  // Withdraw on behalf of this contract
    address(this)   // Send withdrawn collateral to this contract
);

Important Notes

  • Can only withdraw if position remains healthy afterward
  • Full withdrawal is possible when no outstanding debt exists
  • Position health check prevents excessive withdrawal when debt exists

Complete Borrowing Workflow

Here's the complete borrowing and repayment workflow:

// 1. Approve collateral tokens
IERC20(collateralToken).approve(morphoAddress, collateralAmount);
 
// 2. Supply collateral
IMorpho(morphoAddress).supplyCollateral(
    marketParams,
    collateralAmount,
    address(this),
    ""
);
 
// 3. Borrow assets against collateral
(uint256 assetsBorrowed, uint256 sharesBorrowed) = IMorpho(morphoAddress).borrow(
    marketParams,
    borrowAmount,
    0,
    address(this),
    address(this)
);
 
// ... Use borrowed assets ...
 
// 4. Approve loan tokens for repayment
IERC20(loanToken).approve(morphoAddress, repayAmount);
 
// 5. Repay debt (using shares for full repayment)
(uint256 assetsRepaid, uint256 sharesRepaid) = IMorpho(morphoAddress).repay(
    marketParams,
    0,
    sharesBorrowed, // Repay full amount of shares
    address(this),
    ""
);
 
// 6. Withdraw collateral
IMorpho(morphoAddress).withdrawCollateral(
    marketParams,
    collateralAmount,
    address(this),
    address(this)
);

Best Practices for Borrowing Integration

For Collateral Management

  • Apply a safety margin when calculating collateral requirements
  • Monitor price fluctuations that could affect position health
  • Consider over-collateralizing to reduce liquidation risk

For Borrowing

  • Use asset-based approach (assets > 0, shares = 0) for most use cases
  • Verify market liquidity before attempting large borrows
  • Calculate maximum borrow amount based on collateral and LLTV, and be sure users are protected with a buffer.

For Repayment

  • Use share-based approach for full repayments to avoid dust
  • Use asset-based approach for partial repayments

For Withdrawing Collateral

  • Check position health before attempting withdrawals
  • Batch repayments and withdrawals when possible for gas efficiency

Function Selection Guide

OperationFunctionApproachWhen to Use
Add CollateralsupplyCollateralAssets onlyBefore borrowing to secure position
Borrowborrow with assetsAsset-FirstWhen exact token amount is needed
Borrowborrow with sharesShare-FirstAdvanced integration with share accounting
Partial Repayrepay with assetsAsset-FirstWhen repaying specific token amount
Full Repayrepay with sharesShare-FirstWhen repaying entire debt to avoid dust
Remove CollateralwithdrawCollateralAssets onlyAfter repaying to retrieve collateral

By following these patterns, you can build robust integrations with Morpho's borrowing functionality.