DEV Community

Ahmed Castro
Ahmed Castro

Posted on • Edited on

Introducción a Préstamos Programables en Morpho

Desde agentes de IA descentralizados hasta la automatización de tesorerías DAO y bots avanzados de trading en DeFi, las finanzas programables cada vez ofrecen nuevas posibilidades. En este tutorial aprenderemos cómo sacar un préstamo 100% on chain. Lo haremos de una manera práctica y fácil de replicar, por medio de Morpho v1 donde sacaremos un préstamo de WETH poniendo USDC como colateral en la red de Base Mainnet.

En este tutorial verás como protegerte de la volatilidad depositando USDC para sacar el préstamo de WETH en una sola transacción. Luego, cómo repagar tu deuda y recuperar el colateral. Todo de una manera personalizable que puede ser integrada en muchos casos de uso.

Préstamos Monolíticos vs. Multi‑Mercado

Protocolos grandes como Aave y Compound adoptaron una arquitectura monolítica, es decir que ofrecen un único mercado que agrupa toda la liquidez. Esto genera una piscina de liquidez grande y provee estabilidad en el mercado pero hace que los cambios en gobernanza seran más difíciles de adaptar a las condiciones de mercado.

Monolithic vs. Multi‑Market Lending
Aave ofrece un único mercado a nivel de protocolo, mientras que Morpho adopta un enfoque de múltiples mercados.

Morpho ofrece una solución multi‑mercado, donde diferentes organizaciones pueden crear y actualizar nuevos mercados. Morpho llama a estas organizaciones Curadores, y los usuarios pueden elegir entrar al mercado de curadores que prefieran. Los Curadores establecen sus propios parámetros como ser token prestado, token de colateral, el módulo de tasa de interés (IRM), oráculo, relación de colateralización, etc., mientras que Morpho proporciona la infraestructura y el motor de emparejamiento. El resultado es una mayor flexibilidad de mercados y más opciones para los usuarios, todo accedido desde una interfaz unificada.

La API de Morpho

Morpho ofrece cuatro funciones principales:

  1. supplyCollateral: Deposita colateral para luego poder pedir un préstamo.
  2. borrow: Obtiene los fondos del préstamo.
  3. repay: Devuelve los fondos prestado más intereses.
  4. withdrawCollateral: Recupera tu colateral una vez saldada la deuda.

Para hacerlo más simple, nuestro contrato vá a agruparsupplyCollateral + borrow en una sola función llamada borrowAgainstCollateral() call, y repay + withdrawCollateral en repayAndWithdraw().

Parámetros de mercado

En cada llamada, Morpho espera un struct llamado MarketParams que contiene la información del mercado en el que vamos a participar. Esto es porque Morpho soporta muchos mercados.

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}
Enter fullscreen mode Exit fullscreen mode

En este ejemplo vamos a usar un mercado de WETH/USDC en Base con los parámetros a continuación:

  • Contrato de morpho en base: 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
  • loanToken token del préstamo: 0x4200000000000000000000000000000000000006 (WETH)
  • collateralToken token colateral: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (USDC)
  • oracle oráculo proveedor de precios: 0xD09048c8B568Dbf5f189302beA26c9edABFC4858
  • irm dirección de curva adaptativa: 0x46415998764C29aB2a25CbeA6254146D50D22687
  • lltv: 860000000000000000 (86 % ratio sobrecolateralización)

Si quieres usar otro mercado, cambia estas direcciones (todas están disponibles en la documentación de Morpho y la webapp de Morpho).

Morpho Market
El mercado que usamos en este demo está disponible aquí.

Cada Curador puede elegir los pares colateral y préstamo, dependiendo de la oferta y demanda. También pueden escoger el oráculo más descentralizado que cubra un mercado específico (en este caso, Chainlink). Esto es muy importante ya que los oráculos son la parte menos descentralizada de un protocolo de préstamos. Además, dependiendo de la liquidez y volatilidad del mercado, los Curadores pueden fijar el nivel de sobrecolateralización necesario para tomar un préstamo, e incluso modificar cómo se adapta automáticamente en función de la liquidez del mercado mediante la curva adaptativa.

Implementación en Solidity

contract demo
El contrato abstrae el proceso en dos funciones, una de préstamo y otra de pago.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

// IERC20 interface needed to interact with USDC and WETH
interface IERC20 {
    function approve(address spender, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

// Morpho interface to get and repay loans
interface IMorpho{
    struct MarketParams {
        address loanToken;
        address collateralToken;
        address oracle;
        address irm;
        uint256 lltv;
    }

    function supplyCollateral(
        MarketParams calldata marketParams,
        uint256 amount,
        address onBehalf,
        bytes calldata data
    ) external;

    function borrow(
        MarketParams calldata marketParams,
        uint256 amount,
        uint256 maxShares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    function repay(
        MarketParams calldata marketParams,
        uint256 amount,
        uint256 maxShares,
        address onBehalf,
        bytes calldata data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    function withdrawCollateral(
        MarketParams calldata marketParams,
        uint256 amount,
        address onBehalf,
        address receiver
    ) external;
}

// Demo contrat that abstracts borrowing and repayment into two functions
contract MorphoBorrower is Ownable {
    address public constant morphoAddress = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;     // Morpho address on Base

    address public constant loanToken = 0x4200000000000000000000000000000000000006;         // WETH on base
    address public constant collateralToken = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;   // USDC on base
    address public constant oracle = 0xD09048c8B568Dbf5f189302beA26c9edABFC4858;            // Chainlink oracle for our market
    address public constant irm = 0x46415998764C29aB2a25CbeA6254146D50D22687;               // Adaptative curve strategy for our market
    uint256 public constant lltv = 860000000000000000;                                      // 86% loan to liquidation treshold

    constructor() Ownable(msg.sender) {}

    // Our target morpho market settings
    IMorpho.MarketParams public marketParams = IMorpho.MarketParams({
        loanToken: loanToken,
        collateralToken: collateralToken,
        oracle: oracle,
        irm: irm,
        lltv: lltv
    });

    uint256 public sharesBorrowed; // Asset borrow tracking

    // Borrow WETH against USDC
    function borrowAgainstCollateral(uint256 collateralAmount, uint256 borrowAmount) external onlyOwner {
        // Transfer USDC from user and supply it as collateral in the Morpho market
        IERC20(collateralToken).transferFrom(msg.sender, address(this), collateralAmount);
        IERC20(collateralToken).approve(morphoAddress, collateralAmount);
        IMorpho(morphoAddress).supplyCollateral(
            marketParams,
            collateralAmount,
            address(this),
            ""
        );

        // Open a borrow position on this smart contract account and transfer the borrowed WETH to the user
        (uint256 assetsBorrowed, uint256 shares) = IMorpho(morphoAddress).borrow(
            marketParams,
            borrowAmount,
            0,
            address(this),
            address(this)
        );
        sharesBorrowed = shares;
        IERC20(loanToken).transfer(msg.sender, assetsBorrowed);
    }

    function repayAndWithdraw(uint256 repayAmount, uint256 collateralAmount) external onlyOwner {
        // Transfer WETH from the user and repay the debt with it
        IERC20(loanToken).transferFrom(msg.sender, address(this), repayAmount);
        IERC20(loanToken).approve(morphoAddress, repayAmount);
        IMorpho(morphoAddress).repay(
            marketParams,
            0,
            sharesBorrowed,
            address(this),
            ""
        );

        // Transfer back excess loanToken
        IERC20(loanToken).transfer(msg.sender, IERC20(loanToken).balanceOf(address(this)));

        // Withdraw all collateral and send to sender
        IMorpho(morphoAddress).withdrawCollateral(
            marketParams,
            collateralAmount,
            address(this),
            msg.sender
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Cómo usarlo

En la página de USDC en BaseScan, llama a approve(spender, amount) pasando como parámetro el contrato de MorphoBorower que recien lanzaste y la cantidad de USDC que vas a depositar, por ejemplo 3000, o sea 0.003 USDC ya que USDC tiene 6 decimales.

Approve on basescan
Aprueba tu contrato en la página de USDC en BaseScan.

Luego, saca un préstamo de WETH llamando a borrowAgainstCollateral(3000, 1_000_000_000), es decir, 0.003 USDC como colateral para pedir prestado 0.000000001 WETH. En este punto, el WETH estará en tu billetera y podrás usarlo como gustes.

Morpho get loan
Prueba solicitando 0.000000001 WETH con 0.003 USDC de colateral. Puedes ver un ejemplo de transacción aquí.

Para pagar tu deuda, aprueba WETH en BaseScan con una cantidad suficiente para cubrir capital + intereses (ej. 1_010_000_000). Luego llama a repayAndWithdraw(1_010_000_000, 3000). Cualquier WETH sobrante será reembolsado automáticamente y recuperarás tus 0.003 USDC.

Morpho repay debt
Cuando tu deuda es saldada, todo el WETH sobrante será devuelto. Puedes ver un ejemplo transacción aquí.

¡Gracias por ver este artículo!

Sígueme en dev.to y en Youtube para todo lo relacionado al desarrollo en Blockchain en Español.

Top comments (0)