PIENSA en Grande,
Piensa en CRIPTO

Culminando tu Dapp de Préstamos en Solidity: Maestría en Blockchain, 2024, Finaliza con Éxito #Parte 3

Iniciemos la creación del sitio web para nuestra Dapp de préstamos. Con el contrato inteligente ya desarrollado y testeado con éxito, es hora de construir una interfaz interactiva usando HTML, CSS y JavaScript puro. Este proceso te guiará en la creación de una página web funcional y atractiva, esencial para que los usuarios interactúen con tu Dapp. Aunque nuestro foco es el desarrollo en Solidity, el conocimiento básico de desarrollo web es clave para presentar tu proyecto de manera efectiva. ¡Empecemos! 💪


Dar vida al aspecto visual de tu aplicación de préstamos descentralizada

Parte 1: Creación del Sitio Web Frontend para tu Dapp de Préstamos

Primero, dentro de la carpeta de tu proyecto, crea una nueva carpeta nombrada website/ (puedes ponerle el nombre que quieras).

Vamos a aprender a desarrollar el sitio web utilizando HTML, CSS y JavaScript puro. Comienza creando un archivo llamado index.html dentro de la carpeta website/. Añade el siguiente código:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Página de Préstamos</title>
    <style>
        .box {
            display: none;
            max-width: 700px;
            margin: auto;
        }
    </style>
    <!-- Solo CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
</head>
<body>

</body>
</html>

Esta estructura básica de HTML incluye el título del sitio, que se mostrará en la barra superior del navegador. Dentro de la etiqueta <style>, puedes agregar tus estilos CSS, que sirve para modificar la apariencia de tu sitio web. HTML, por otro lado, es la estructura y el contenido textual.

Hemos incorporado un enlace a Bootstrap, una biblioteca que facilita la creación del sitio web proporcionando estilos predefinidos. Es como un atajo para diseñar más rápidamente.

A continuación, añadiremos elementos de diseño para que realmente adquiera ese aspecto. Copia el siguiente código y colócalo entre las etiquetas <body> </body>:

<!-- 1 Barra de Navegación -->
<nav class="navbar navbar-dark bg-dark">
    <div class="container-fluid">
        <a class="navbar-brand" href="/">Dapp de Préstamos DeFi</a>
    </div>
</nav>

<!-- 2 Textos Descriptivos -->
<div class="container text-center">
    <div class="row">
        <div class="col">
            Como prestamista: Despliega un nuevo contrato de préstamo, deposita algo de ETH de prueba y comparte la dirección del contrato con tu amigo.
        </div>
    </div>
    <div class="row">
        <div class="col">
            Como prestatario: Pega la dirección del contrato en el cuadro y solicita algunos fondos. Luego, asegúrate de devolverlos antes de la fecha acordada.
        </div>
    </div>
</div>

<!-- 3 Botones de Acción -->
<div class="container text-center">
    <div class="row">
        <div class="col">
            <button onclick="showLend()" type="button" class="btn btn-secondary">Quiero prestar cripto</button>
        </div>
        <div class="col">
            <button onclick="showBorrow()" type="button" class="btn btn-secondary">Quiero pedir prestado cripto</button>
        </div>
        <div class="col">
            <button onclick="showRepay()" type="button" class="btn btn-secondary">Quiero devolver mi préstamo</button>
        </div>
    </div>
</div>

<!-- 4 Formulario para Depositar -->
<form id="lend" class="box" onsubmit="deployAndDeposit(event)">
    <div class="mb-3">
      <label for="repay-time" class="form-label">Tiempo de devolución en días</label>
      <input type="number" oninput="validateRepayInput(event)" class="form-control" id="repay-time" aria-describedby="emailHelp">
      <div id="emailHelp" class="form-text">Cuántos días tiene el prestatario para devolverte</div>
    </div>
    <div class="mb-3">
      <label for="interest-percentage" class="form-label">Porcentaje de interés</label>
      <input type="number" oninput="validateInterest(event)" class="form-control" id="interest-percentage">
      <div class="form-text">Entre 0 y 100</div>
    </div>
    <div class="mb-3">
        <label for="deposit-to-lend" class="form-label">Máxima cantidad a prestar</label>
        <input type="number" step="0.0001" oninput="validateDeposit(event)" class="form-control" id="deposit-to-lend">
        <div class="form-text">En ETH</div>
    </div>
    <button type="submit" class="btn btn-primary">Desplegar Contrato Inteligente de Préstamo y Depositar</button>
    <div class="mb-3">
        <br/>
        <p id="deployed-contract"></p>
    </div>
</form>

<!-- 5 Formulario para Pedir Prestado -->
<form id="borrow" class="box" onsubmit="borrow(event)">
    <div class="mb-3">
      <label for="lending-borrow-contract" class="form-label">Dirección del contrato de préstamo</label>
      <input type="text" class="form-control" id="lending-borrow-contract" aria-describedby="emailHelp">
      <div id="emailHelp" class="form-text">El contrato de préstamo desplegado</div>
    </div>
    <div class="mb-3">
      <label for="lending-borrow-input" class="form-label">Cantidad a pedir prestado</label>
      <input type="number" step="0.0001" oninput="validateBorrowAmount(event)" class="form-control" id="lending-borrow-input">
     <div class="form-text">En ETH</div>
    </div>
    <button type="submit" class="btn btn-primary">Pedir Prestado</button>
</form>

<!-- 6 Bloque de Devolución -->
<form id="repay" class="box" onsubmit="repay(event)">
    <div class="mb-3">
      <label for="lending-repay-contract" class="form-label">Dirección del contrato de préstamo</label>
      <input type="text" class="form-control" id="lending-repay-contract" aria-describedby="emailHelp">
      <div id="emailHelp" class="form-text">El contrato de préstamo desplegado</div>
    </div>
    <div class="mb-3">
      <label for="repay-input" class="form-label">Cantidad a devolver</label>
      <input type="number" step="0.0001" class="form-control" id="repay-input">
    </div>
    <button type="submit" class="btn btn-primary">Devolver</button>
</form>

Cada sección está marcada con un comentario. En HTML, los comentarios se hacen con <!-- tu comentario -->, y esta información no se muestra en el sitio, solo es visible para ti como desarrollador.

Si aún no lo has hecho, instala Metamask.io en tu navegador y configura una billetera blockchain. Esto será tu puerta de entrada al mundo de las criptomonedas y es esencial para utilizar la Dapp que estás creando.

Este no es un curso completo de desarrollo web, así que no profundizaremos en cada detalle del código. Sin embargo, te guiaremos a través de cada parte para que comprendas lo que estás escribiendo. Sigue los comentarios marcados del 1 al 6:

  1. Barra de Navegación: En este primer bloque, estamos integrando la barra de navegación superior. Esta es una barra oscura ubicada en la parte superior del sitio web, que incluye un enlace a la página principal.
  2. Textos Descriptivos: Aquí añadimos dos textos informativos. El objetivo es orientar a los usuarios sobre cómo interactuar con la aplicación descentralizada (Dapp).
  3. Botones de Acción: Incorporamos tres botones para ofrecer opciones a los usuarios sobre qué acción desean realizar. Al hacer clic en cada uno, se activa una función de JavaScript, como se indica en el atributo onclick="".
  4. Formulario para Depositar: Este bloque forma la primera sección interactiva del sitio, donde los depositantes pueden especificar el plazo de devolución y el porcentaje de interés. Al pulsar el botón para desplegar el contrato inteligente de préstamo y depositar, se ejecutará la función desplegarYDepositar(event). Posteriormente, aparecerá un popup de Metamask para confirmar la creación del contrato inteligente. Aquí es donde se ejecuta el constructor() de tu contrato inteligente de préstamos, como se discutió en secciones anteriores.
  5. Formulario para Pedir Prestado: Tras el despliegue del contrato, los usuarios verán la dirección del mismo, que se compartirá con aquellos que deseen solicitar fondos. Este quinto bloque se muestra al hacer clic en el botón para pedir criptomonedas prestadas. Contiene un formulario donde los prestatarios indican la dirección del contrato de préstamo desplegado y seleccionan la cantidad que desean obtener de los fondos disponibles.
  6. Bloque de Devolución: Se muestra después de que el usuario elija la opción de devolver el préstamo. Aquí, se debe ingresar la dirección del contrato y la cantidad a devolver. Al confirmar estos datos y pulsar el botón de devolución, se iniciará una transacción en la blockchain, visible a través de Metamask, permitiendo al prestatario devolver el préstamo, ya sea parcial o totalmente.

Hemos abordado mucho contenido, pero no te preocupes. Solo necesitas copiar el código y escribirlo en tu editor de código.

Continúa leyendo la siguiente sección para implementar las funciones de JavaScript.

Parte 2: La Fase Final – Implementación del Código JavaScript

Ahora abordaremos la parte final: la implementación del código JavaScript, que es seguramente la sección más emocionante. En PiensaEnCripto, nos encanta JavaScript por su versatilidad y capacidad para crear prácticamente cualquier aplicación.

Escribiremos la sección completa de JavaScript para que la copies manualmente, función por función, y luego explicaremos el código.

Justo después de tu etiqueta de cierre </body> y antes de la etiqueta de cierre </html>, añade este código:

<script type="module">
    // 1
    import { ethers } from "./ethers.js"
    window.ethers = ethers
    import jsonPrestamo from './artifacts/contracts/Lending.sol/Lending.json' assert { type: 'json' }
    const abiPrestamo = jsonPrestamo.abi
    const bytecodePrestamo = jsonPrestamo.bytecode

    // 2
    let tiempoReembolso = null
    let interes = null
    let deposito = null
    let eth = {
        proveedor: null,
        direccion: null,
        firmante: null,
        contratoPrestamo: null,
        montoPrestamo: null,
    }

    // 3
    const configurar = async () => {
        eth.proveedor = new ethers.providers.Web3Provider(window.ethereum)
        eth.direccion = (await eth.proveedor.send("eth_requestAccounts", []))[0]
        eth.firmante = eth.proveedor.getSigner(eth.direccion)
    }
    
    // 4
    window.mostrarPrestar = () => {
        [...document.querySelectorAll('.box')].map(box => box.style.display = 'none')
        document.querySelector('#lend').style.display = 'block'
    }
    window.mostrarPedirPrestado = () => {
        [...document.querySelectorAll('.box')].map(box => box.style.display = 'none')
        document.querySelector('#borrow').style.display = 'block'
    }
    window.mostrarReembolsar = () => {
        [...document.querySelectorAll('.box')].map(box => box.style.display = 'none')
        document.querySelector('#repay').style.display = 'block'
    }
    
    // 5
    window.validarTiempoReembolso = evento => {
        evento.target.value = evento.target.value.replace(/[^0-9]*/g,'');
        tiempoReembolso = Number(evento.target.value)
    }
    window.validarInteres = evento => {
        evento.target.value = evento.target.value.replace(/[^0-9]*/g,'');
        interes = Number(evento.target.value)
    }
    window.validarDeposito = evento => {
        deposito = evento.target.value
    }
    window.validarMontoPrestamo = e => {
        eth.montoPrestamo = e.target.value
    }
    
    // 6
    window.desplegarYDepositar = async e => {
        e.preventDefault()
        if (!tiempoReembolso) return alert('Debe establecer el tiempo de reembolso en días')
        if (!interes) return alert('Debe establecer el porcentaje de interés')
        if (interes < 0 || interes > 100) return alert('El porcentaje de interés debe estar entre 0 y 100')
        if (!deposito) return alert('Debe establecer el monto del depósito')

        const fabrica = new ethers.ContractFactory(abiPrestamo, bytecodePrestamo, eth.firmante)
        eth.contratoPrestamo = await fabrica.deploy(tiempoReembolso, interes, {
            value: window.ethers.FixedNumber.from(deposito) // Esto convierte el número a wei
        })
        console.log('eth.contratoPrestamo', eth.contratoPrestamo)
        document.querySelector('#deployed-contract').innerHTML = 'Desplegando contrato...: ' + eth.contratoPrestamo.address
        console.log('desplegando...')
        await eth.contratoPrestamo.deployTransaction.wait()
        console.log('eth.contratoPrestamo', eth.contratoPrestamo)
        console.log('desplegado!')
        alert('¡Desplegado!')
    }

    // 7
    window.pedirPrestado = async e => {
        e.preventDefault()
        const direccionContrato = document.querySelector('#lending-borrow-contract').value
        const cantidadPrestamo = Number(document.querySelector('#lending-borrow-input').value)
        if (cantidadPrestamo <= 0) return alert('Debe establecer la cantidad a pedir prestada')
        try {
            eth.contratoPrestamo = new ethers.Contract(direccionContrato, abiPrestamo, eth.firmante)
        } catch (e) {
            return alert('Dirección de contrato de préstamo inválida')
        }
        const transaccion = await eth.contratoPrestamo.pedirPrestado(
            window.ethers.FixedNumber.from(String(cantidadPrestamo))
        )
        await transaccion.wait()
        console.log('Préstamo exitoso.')
        alert('¡Préstamo exitoso!')
    }

    // 8
    window.reembolsar = async e => {
        e.preventDefault()
        
        const direccionContrato = document.querySelector('#lending-repay-contract').value
        const cantidadReembolso = Number(document.querySelector('#repay-input').value)
        if (cantidadReembolso <= 0) return alert('Debe establecer la cantidad a reembolsar')
        try {
            eth.contratoPrestamo = new ethers.Contract(direccionContrato, abiPrestamo, eth.firmante)
        } catch (e) {
            return alert('Dirección de contrato de préstamo inválida')
        }
        const transaccion = await eth.contratoPrestamo.reembolsar({
            value: window.ethers.FixedNumber.from(String(cantidadReembolso))
        })
        await transaccion.wait()
        console.log('Reembolso exitoso.')
        alert('¡Reembolso exitoso!')
    }

    // 9
    configurar()
</script>

Cada sección está marcada con un comentario. Los comentarios en JavaScript comienzan con //. Simplemente busca cada número para entender a qué nos referimos:

  • Uso de <script type="módulo">: Iniciamos con la etiqueta <script type="module">, indicando al navegador que estamos incorporando código JavaScript modular. El atributo type="module" es esencial para habilitar el uso de módulos ES6 de JavaScript.
  • Importación de ethers.js: Continuamos con la importación de ethers.js, una librería esencial para interactuar con la blockchain desde el navegador. Esto implica copiar el código desde un enlace específico y crear un archivo llamado ethers.js en nuestro directorio website/. Esta librería nos permite interactuar con nuestro contrato inteligente de préstamos. Para acceder al ABI del contrato, es necesario compilarlo mediante npm hardhat compile y luego copiar la carpeta artifacts/ en nuestro directorio website/.
  • Configuración de Variables: Establecemos diversas variables con let y const, que serán utilizadas más adelante. Por ejemplo, eth es un objeto que contendrá propiedades importantes para nuestro código.
  • Creación de la Función configurar(): Esta función se encarga de establecer la conexión con la blockchain a través de la variable window.ethereum, proporcionada por Metamask.
  • Funciones mostrarPrestar(), mostrarPedirPrestado() y mostrarReembolsar(): Estas funciones tienen como objetivo mostrar y ocultar los formularios correctos al usuario tras hacer clic en los botones correspondientes.
  • Validación de Datos del Usuario: Implementamos funciones para asegurar que los datos introducidos por el usuario sean correctos y estén adecuadamente formateados.
  • Función desplegarYDepositar(): Esta función se activa al pulsar el botón para desplegar el contrato inteligente de préstamos y realizar un depósito. Verifica que los datos sean correctos y procede a desplegar una nueva versión del contrato inteligente.
  • Función pedirPrestado(): Recibe la dirección del contrato y ejecuta el método pedirPrestado() del mismo. Se utiliza window.ethers.FixedNumber.from() para convertir los valores introducidos a un formato que ethers.js pueda procesar.
  • Función reembolsar(): Similar a la función anterior, esta recibe la dirección del contrato, crea una instancia del mismo y ejecuta el método repay(). Se envía ether junto con la ejecución de la función.
  • Ejecución de configurar(): Por último, ejecutamos la función configurar(), que se inicia automáticamente al cargar el sitio web.

¡Eso es todo! La Dapp está lista. Ahora puedes abrir el archivo index.html en tu navegador para interactuar con la aplicación. Pero recuerda, necesitarás configurar un servidor local para que la app funcione correctamente con Metamask.

Para configurar un servidor local, primero instala http-server usando tu terminal:

npm i -g http-server

Luego ejecuta lo siguiente en la carpeta raíz de tu proyecto:

http-server website/

Esto servirá la carpeta del sitio web, creando un servidor local accesible a través de http://localhost:8080.

Interactúa con la Dapp y observa cómo los datos se almacenan permanentemente en la blockchain.

🌟 ¡Enhorabuena! 🌟 Has completado la guía.


💰📊 Gracias por llegar hasta aquí. Sabemos que no es poca cosa, y tu dedicación y esfuerzo en este camino merecen ser celebrados. Nos complace haber compartido contigo estos 3 MINITUTORIALES de Solidity y esperamos que te hayan sido útiles. Continuaremos mejorando y aprendiendo para seguir expandiendo nuestros conocimientos 💪

Piensa en GRANDE, piensa en CRIPTO. 😉🦊

Comparte el artículo

Facebook
Twitter
Telegram
WhatsApp

¿No estás Satisfecho aún? Aquí hay MÁS Sobre

Evádelo Si Puedes, ¡Contenido IRRESISTIBLE!

Deja una respuesta

¡SÍGUENOS!

© 2024 Piensa en GRANDE 🦊 Piensa en CRIPTO.