Saltar al contenido principal

Contrato inteligente Hola Mundo para principiantes - Fullstack

Solidity
Hardhat
Alchemy
contratos inteligentes
despliegue
explorador de bloques
frontend
transacciones
framework
Principiante
nstrike2
25 de octubre de 2021
47 minutos de lectura

Esta guía es para ti si eres nuevo en el desarrollo de la cadena de bloques y no sabes por dónde empezar o cómo desplegar e interactuar con contratos inteligentes. Te guiaremos a través de la creación y el despliegue de un contrato inteligente simple en la red de prueba Goerli utilizando MetaMask (opens in a new tab), Solidity (opens in a new tab), Hardhat (opens in a new tab) y Alchemy (opens in a new tab).

Necesitarás una cuenta de Alchemy para completar este tutorial. Regístrate para obtener una cuenta gratuita (opens in a new tab).

Si tienes preguntas en algún momento, ¡no dudes en comunicarte en el Discord de Alchemy (opens in a new tab)!

Parte 1: Crea y despliega tu contrato inteligente usando Hardhat

Conectarse a la red Ethereum

Hay muchas formas de hacer solicitudes a la cadena de Ethereum. Por simplicidad, usaremos una cuenta gratuita en Alchemy, una plataforma para desarrolladores de cadenas de bloques y API que nos permite comunicarnos con la cadena de Ethereum sin tener que ejecutar un nodo nosotros mismos. Alchemy también tiene herramientas de desarrollo para monitoreo y análisis; las aprovecharemos en este tutorial para entender qué sucede internamente en el despliegue de nuestro contrato inteligente.

Crea tu aplicación y clave API

Una vez que hayas creado una cuenta de Alchemy, puedes generar una clave API creando una aplicación. Esto te permitirá hacer solicitudes a la red de prueba Goerli. Si no estás familiarizado con las redes de prueba, puedes leer la guía de Alchemy para elegir una red (opens in a new tab).

En el panel de Alchemy, busca el menú desplegable Apps en la barra de navegación y haz clic en Create App.

Hello world create app

Dale a tu aplicación el nombre 'Hello World' y escribe una breve descripción. Selecciona Staging como tu entorno y Goerli como tu red.

create app view hello world

Nota: asegúrate de seleccionar Goerli, o este tutorial no funcionará.

Haz clic en Create app. Tu aplicación aparecerá en la tabla de abajo.

Crea una cuenta de Ethereum

Necesitas una cuenta de Ethereum para enviar y recibir transacciones. Usaremos MetaMask, una billetera virtual en el navegador que permite a los usuarios administrar la dirección de su cuenta de Ethereum.

Puedes descargar y crear una cuenta de MetaMask gratis aquí (opens in a new tab). Cuando estés creando una cuenta, o si ya tienes una, asegúrate de cambiar a la "Goerli Test Network" (Red de prueba Goerli) en la parte superior derecha (para que no estemos lidiando con dinero real).

Paso 4: Añadir ether desde un faucet

Para desplegar tu contrato inteligente en la red de prueba, necesitarás algo de ETH falso. Para obtener ETH en la red Goerli, ve a un faucet de Goerli e ingresa la dirección de tu cuenta de Goerli. Ten en cuenta que los faucets de Goerli pueden ser un poco inestables últimamente; consulta la página de redes de prueba para ver una lista de opciones para probar:

Nota: debido a la congestión de la red, esto podría tardar un poco. ``

Paso 5: Comprobar tu saldo

Para comprobar que el ETH está en tu billetera, hagamos una solicitud eth_getBalance (opens in a new tab) utilizando la herramienta sandbox de Alchemy (opens in a new tab). Esto devolverá la cantidad de ETH en nuestra billetera. Para obtener más información, echa un vistazo al breve tutorial de Alchemy sobre cómo usar la herramienta composer (opens in a new tab).

Ingresa la dirección de tu cuenta de MetaMask y haz clic en Send Request. Verás una respuesta que se parece al fragmento de código a continuación.

{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }

Nota: Este resultado está en wei, no en ETH. Wei se utiliza como la denominación más pequeña de ether.

¡Uf! Todo nuestro dinero falso está ahí.

Paso 6: Inicializar nuestro proyecto

Primero, necesitaremos crear una carpeta para nuestro proyecto. Navega a tu línea de comandos e ingresa lo siguiente.

mkdir hello-world
cd hello-world

Ahora que estamos dentro de la carpeta de nuestro proyecto, usaremos npm init para inicializar el proyecto.

Si aún no tienes npm instalado, sigue las instrucciones de instalación de Node.js (opens in a new tab) para instalar Node.js y npm.

Para los fines de este tutorial, no importa cómo respondas a las preguntas de inicialización. Aquí te mostramos cómo lo hicimos como referencia:

¡Aprueba el package.json y estamos listos para continuar!

Paso 7: Descargar Hardhat

Hardhat es un entorno de desarrollo para compilar, desplegar, probar y depurar tu software de Ethereum. Ayuda a los desarrolladores a construir contratos inteligentes y aplicaciones descentralizadas (dapps) localmente antes de desplegarlos en la cadena activa.

Dentro de nuestro proyecto hello-world ejecuta:

npm install --save-dev hardhat

Consulta esta página para obtener más detalles sobre las instrucciones de instalación (opens in a new tab).

Paso 8: Crear un proyecto de Hardhat

Dentro de la carpeta de nuestro proyecto hello-world, ejecuta:

npx hardhat

A continuación, deberías ver un mensaje de bienvenida y la opción de seleccionar lo que quieres hacer. Selecciona "create an empty hardhat.config.js":

Esto generará un archivo hardhat.config.js en el proyecto. Lo usaremos más adelante en el tutorial para especificar la configuración de nuestro proyecto.

Paso 9: Añadir carpetas del proyecto

Para mantener el proyecto organizado, vamos a crear dos carpetas nuevas. En la línea de comandos, navega hasta el directorio raíz de tu proyecto hello-world y escribe:

mkdir contracts
mkdir scripts
  • contracts/ es donde guardaremos el archivo de código de nuestro contrato inteligente hello world
  • scripts/ es donde guardaremos los scripts para desplegar e interactuar con nuestro contrato

Paso 10: Escribir nuestro contrato

Puede que te estés preguntando, ¿cuándo vamos a escribir código? ¡Ha llegado el momento!

Abre el proyecto hello-world en tu editor favorito. Los contratos inteligentes se escriben más comúnmente en Solidity, que es el que usaremos para escribir nuestro contrato inteligente.‌

  1. Navega a la carpeta contracts y crea un nuevo archivo llamado HelloWorld.sol
  2. A continuación se muestra un contrato inteligente Hello World de muestra que usaremos para este tutorial. Copia el contenido a continuación en el archivo HelloWorld.sol.

Nota: Asegúrate de leer los comentarios para entender qué hace este contrato.

Este es un contrato inteligente básico que almacena un mensaje al momento de su creación. Se puede actualizar llamando a la función update.

Paso 11: Conectar MetaMask y Alchemy a tu proyecto

Hemos creado una billetera de MetaMask, una cuenta de Alchemy y hemos escrito nuestro contrato inteligente, ahora es el momento de conectar los tres.

Cada transacción enviada desde tu billetera requiere una firma usando tu clave privada única. Para proporcionar este permiso a nuestro programa, podemos almacenar de forma segura nuestra clave privada en un archivo de entorno. También almacenaremos una clave API para Alchemy aquí.

Para obtener más información sobre el envío de transacciones, consulta este tutorial sobre cómo enviar transacciones usando Web3.

Primero, instala el paquete dotenv en el directorio de tu proyecto:

npm install dotenv --save

Luego, crea un archivo .env en el directorio raíz del proyecto. Añade tu clave privada de MetaMask y la URL HTTP de la API de Alchemy en él.

Tu archivo de entorno debe llamarse .env o no será reconocido como un archivo de entorno.

No lo llames process.env ni .env-custom ni de ninguna otra manera.

Animated walkthrough of getting an Alchemy API key

Tu .env debería verse así:

API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"

Para conectar realmente esto a nuestro código, haremos referencia a estas variables en nuestro archivo hardhat.config.js en el paso 13.

Paso 12: Instalar Ethers.js

Ethers.js es una biblioteca que facilita la interacción y la realización de solicitudes a Ethereum al envolver los métodos estándar JSON-RPC con métodos más fáciles de usar.

Hardhat nos permite integrar complementos (opens in a new tab) para herramientas adicionales y funcionalidad extendida. Aprovecharemos el complemento Ethers (opens in a new tab) para el despliegue de contratos.

En el directorio de tu proyecto escribe:

npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"

Paso 13: Actualizar hardhat.config.js

Hemos añadido varias dependencias y complementos hasta ahora, ahora necesitamos actualizar hardhat.config.js para que nuestro proyecto sepa de todos ellos.

Actualiza tu hardhat.config.js para que se vea así:

Paso 14: Compilar nuestro contrato

Para asegurarnos de que todo funciona hasta ahora, compilemos nuestro contrato. La tarea compile es una de las tareas integradas de Hardhat.

Desde la línea de comandos ejecuta:

npx hardhat compile

Es posible que recibas una advertencia sobre SPDX license identifier not provided in source file, pero no hay de qué preocuparse; ¡con suerte, todo lo demás se ve bien! Si no es así, siempre puedes enviar un mensaje en el Discord de Alchemy (opens in a new tab).

Paso 15: Escribir nuestro script de despliegue

Ahora que nuestro contrato está escrito y nuestro archivo de configuración está listo, es hora de escribir nuestro script de despliegue de contrato.

Navega a la carpeta scripts/ y crea un nuevo archivo llamado deploy.js, añadiendo el siguiente contenido en él:

Hardhat hace un trabajo increíble al explicar qué hace cada una de estas líneas de código en su tutorial de Contratos (opens in a new tab), hemos adoptado sus explicaciones aquí.

const HelloWorld = await ethers.getContractFactory("HelloWorld")

Un ContractFactory en ethers.js es una abstracción utilizada para desplegar nuevos contratos inteligentes, por lo que HelloWorld aquí es una fábrica (opens in a new tab) para instancias de nuestro contrato hello world. Al usar el complemento hardhat-ethers, ContractFactory y Contract, las instancias se conectan al primer firmante (propietario) de forma predeterminada.

const hello_world = await HelloWorld.deploy()

Llamar a deploy() en un ContractFactory iniciará el despliegue y devolverá una Promise que se resuelve en un objeto Contract. Este es el objeto que tiene un método para cada una de las funciones de nuestro contrato inteligente.

Paso 16: Desplegar nuestro contrato

¡Finalmente estamos listos para desplegar nuestro contrato inteligente! Navega a la línea de comandos y ejecuta:

npx hardhat run scripts/deploy.js --network goerli

A continuación, deberías ver algo como:

Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570

Por favor, guarda esta dirección. La usaremos más adelante en el tutorial.

Si vamos a Etherscan de Goerli (opens in a new tab) y buscamos la dirección de nuestro contrato, deberíamos poder ver que se ha desplegado con éxito. La transacción se verá algo así:

La dirección From debe coincidir con la dirección de tu cuenta de MetaMask y la dirección To dirá Contract Creation. Si hacemos clic en la transacción, veremos la dirección de nuestro contrato en el campo To.

¡Felicidades! Acabas de desplegar un contrato inteligente en una red de prueba de Ethereum.

Para entender cómo funciona internamente, naveguemos a la pestaña Explorer en nuestro panel de Alchemy (opens in a new tab). Si tienes varias aplicaciones de Alchemy, asegúrate de filtrar por aplicación y seleccionar Hello World.

Aquí verás un puñado de métodos JSON-RPC que Hardhat/Ethers hicieron internamente por nosotros cuando llamamos a la función .deploy(). Dos métodos importantes aquí son eth_sendRawTransaction (opens in a new tab), que es la solicitud para escribir nuestro contrato en la cadena Goerli, y eth_getTransactionByHash (opens in a new tab), que es una solicitud para leer información sobre nuestra transacción dado el hash. Para obtener más información sobre el envío de transacciones, consulta nuestro tutorial sobre cómo enviar transacciones usando Web3.

Parte 2: Interactúa con tu contrato inteligente

Ahora que hemos desplegado con éxito un contrato inteligente en la red Goerli, aprendamos a interactuar con él.

Crea un archivo interact.js

Este es el archivo donde escribiremos nuestro script de interacción. Usaremos la biblioteca Ethers.js que instalaste previamente en la Parte 1.

Dentro de la carpeta scripts/, crea un nuevo archivo llamado interact.js y añade el siguiente código:

// interact.js

const API_KEY = process.env.API_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS

Actualiza tu archivo .env

Usaremos nuevas variables de entorno, por lo que necesitamos definirlas en el archivo .env que creamos anteriormente.

Necesitaremos añadir una definición para nuestra API_KEY de Alchemy y la CONTRACT_ADDRESS donde se desplegó tu contrato inteligente.

Tu archivo .env debería verse más o menos así:

# .env

API_URL = "https://eth-goerli.alchemyapi.io/v2/<your-api-key>"
API_KEY = "<your-api-key>"
PRIVATE_KEY = "<your-metamask-private-key>"
CONTRACT_ADDRESS = "0x<your contract address>"

Obtén el ABI de tu contrato

El de nuestro contrato es la interfaz para interactuar con nuestro contrato inteligente. Hardhat genera automáticamente un ABI y lo guarda en HelloWorld.json. Para usar el ABI, necesitaremos analizar el contenido añadiendo las siguientes líneas de código a nuestro archivo interact.js:

// interact.js
const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")

Si quieres ver el ABI, puedes imprimirlo en tu consola:

console.log(JSON.stringify(contract.abi))

Para ver tu ABI impreso en la consola, navega a tu terminal y ejecuta:

npx hardhat run scripts/interact.js

Crea una instancia de tu contrato

Para interactuar con nuestro contrato, necesitamos crear una instancia del contrato en nuestro código. Para hacerlo con Ethers.js, necesitaremos trabajar con tres conceptos:

  1. Proveedor (Proveedor): un proveedor de nodo que te da acceso de lectura y escritura a la cadena de bloques.
  2. Firmante (Firmante): representa una cuenta de Ethereum que puede firmar transacciones.
  3. Contrato (Contract): un objeto de Ethers.js que representa un contrato específico desplegado en la cadena.

Usaremos el ABI del contrato del paso anterior para crear nuestra instancia del contrato:

Aprende más sobre Proveedores, Firmantes y Contratos en la documentación de ethers.js (opens in a new tab).

Lee el mensaje de inicio

¿Recuerdas cuando desplegamos nuestro contrato con el initMessage = "Hello world!"? Ahora vamos a leer ese mensaje almacenado en nuestro contrato inteligente e imprimirlo en la consola.

En JavaScript, las funciones asíncronas se utilizan al interactuar con redes. Para aprender más sobre funciones asíncronas, lee este artículo de Medium (opens in a new tab).

Usa el código a continuación para llamar a la función message en nuestro contrato inteligente y leer el mensaje de inicio:

Después de ejecutar el archivo usando npx hardhat run scripts/interact.js en la terminal, deberíamos ver esta respuesta:

The message is: Hello world!

¡Felicidades! Acabas de leer con éxito datos de un contrato inteligente de la cadena de bloques de Ethereum, ¡bien hecho!

Actualiza el mensaje

En lugar de solo leer el mensaje, ¡también podemos actualizar el mensaje guardado en nuestro contrato inteligente usando la función update! Genial, ¿verdad?

Para actualizar el mensaje, podemos llamar directamente a la función update en nuestro objeto Contract instanciado:

Ten en cuenta que en la línea 11, hacemos una llamada a .wait() en el objeto de transacción devuelto. Esto asegura que nuestro script espere a que la transacción sea minada en la cadena de bloques antes de salir de la función. Si no se incluye la llamada a .wait(), es posible que el script no vea el valor actualizado de message en el contrato.

Lee el nuevo mensaje

Deberías poder repetir el paso anterior para leer el valor actualizado de message. ¡Tómate un momento y mira si puedes hacer los cambios necesarios para imprimir ese nuevo valor!

Si necesitas una pista, así es como debería verse tu archivo interact.js en este punto:

¡Ahora solo ejecuta el script y deberías poder ver el mensaje antiguo, el estado de actualización y el nuevo mensaje impresos en tu terminal!

npx hardhat run scripts/interact.js --network goerli

The message is: Hello World!
Updating the message...
The new message is: This is the new message.

Al ejecutar ese script, es posible que notes que el paso Updating the message... tarda un poco en cargar antes de que se cargue el nuevo mensaje. Esto se debe al proceso de minería; si tienes curiosidad por rastrear las transacciones mientras se minan, visita la mempool de Alchemy (opens in a new tab) para ver el estado de una transacción. Si la transacción se descarta, también es útil consultar Etherscan de Goerli (opens in a new tab) y buscar el hash de tu transacción.

Parte 3: Publicar tu contrato inteligente en Etherscan

Hiciste todo el trabajo duro de darle vida a tu contrato inteligente; ¡ahora es el momento de compartirlo con el mundo!

Al verificar tu contrato inteligente en Etherscan, cualquiera puede ver tu código fuente e interactuar con tu contrato inteligente. ¡Empecemos!

Paso 1: Generar una clave API en tu cuenta de Etherscan

Una clave API de Etherscan es necesaria para verificar que eres el propietario del contrato inteligente que estás intentando publicar.

Si aún no tienes una cuenta de Etherscan, regístrate para obtener una cuenta (opens in a new tab).

Una vez que hayas iniciado sesión, busca tu nombre de usuario en la barra de navegación, pasa el cursor sobre él y selecciona el botón My profile (Mi perfil).

En tu página de perfil, deberías ver una barra de navegación lateral. En la barra de navegación lateral, selecciona API Keys. A continuación, presiona el botón "Add" (Añadir) para crear una nueva clave API, nombra tu aplicación hello-world y presiona el botón Create New API Key (Crear nueva clave API).

Tu nueva clave API debería aparecer en la tabla de claves API. Copia la clave API en tu portapapeles.

A continuación, necesitamos añadir la clave API de Etherscan a nuestro archivo .env.

Después de añadirla, tu archivo .env debería verse así:

API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PUBLIC_KEY = "your-public-account-address"
PRIVATE_KEY = "your-private-account-address"
CONTRACT_ADDRESS = "your-contract-address"
ETHERSCAN_API_KEY = "your-etherscan-key"

Contratos inteligentes desplegados con Hardhat

Instalar hardhat-etherscan

Publicar tu contrato en Etherscan usando Hardhat es sencillo. Primero necesitarás instalar el complemento hardhat-etherscan para empezar. hardhat-etherscan verificará automáticamente el código fuente y el ABI del contrato inteligente en Etherscan. Para añadir esto, en el directorio hello-world ejecuta:

npm install --save-dev @nomiclabs/hardhat-etherscan

Una vez instalado, incluye la siguiente declaración en la parte superior de tu hardhat.config.js y añade las opciones de configuración de Etherscan:

Verificar tu contrato inteligente en Etherscan

Asegúrate de que todos los archivos estén guardados y de que todas las variables de .env estén configuradas correctamente.

Ejecuta la tarea verify, pasando la dirección del contrato y la red donde está desplegado:

npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'

Asegúrate de que DEPLOYED_CONTRACT_ADDRESS sea la dirección de tu contrato inteligente desplegado en la red de prueba Goerli. Además, el argumento final ('Hello World!') debe ser el mismo valor de cadena utilizado durante el paso de despliegue en la parte 1.

Si todo va bien, verás el siguiente mensaje en tu terminal:

Successfully submitted source code for contract
contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
for verification on Etherscan. Waiting for verification result...


Successfully verified contract HelloWorld on Etherscan.
https://goerli.etherscan.io/address/<contract-address>#contracts

¡Felicidades! ¡El código de tu contrato inteligente está en Etherscan!

¡Echa un vistazo a tu contrato inteligente en Etherscan!

Cuando navegues al enlace proporcionado en tu terminal, ¡deberías poder ver el código de tu contrato inteligente y el ABI publicados en Etherscan!

¡Guau, lo lograste, campeón! ¡Ahora cualquiera puede llamar o escribir en tu contrato inteligente! ¡Estamos ansiosos por ver qué construyes a continuación!

Parte 4: Integrar su contrato inteligente con el frontend

Al final de este tutorial, sabrá cómo:

  • Conectar una billetera MetaMask a su aplicación descentralizada (dapp)
  • Leer datos de su contrato inteligente utilizando la API de Alchemy Web3 (opens in a new tab)
  • Firmar transacciones de Ethereum utilizando MetaMask

Para esta dapp, utilizaremos React (opens in a new tab) como nuestro marco de frontend; sin embargo, es importante tener en cuenta que no pasaremos mucho tiempo desglosando sus fundamentos, ya que nos centraremos principalmente en aportar la funcionalidad de Web3 a nuestro proyecto.

Como requisito previo, debe tener un nivel de comprensión de principiante sobre React. Si no es así, le recomendamos completar el tutorial oficial de introducción a React (opens in a new tab).

Clonar los archivos de inicio

Primero, vaya al repositorio de GitHub hello-world-part-four (opens in a new tab) para obtener los archivos de inicio de este proyecto y clone este repositorio en su máquina local.

Abra el repositorio clonado localmente. Tenga en cuenta que contiene dos carpetas: starter-files y completed.

  • starter-files: trabajaremos en este directorio, conectaremos la interfaz de usuario a su billetera de Ethereum y al contrato inteligente que publicamos en Etherscan en la Parte 3.
  • completed contiene todo el tutorial completado y solo debe usarse como referencia si se queda atascado.

A continuación, abra su copia de starter-files en su editor de código favorito y luego navegue hasta la carpeta src.

Todo el código que escribiremos vivirá en la carpeta src. Editaremos el componente HelloWorld.js y los archivos JavaScript util/interact.js para darle a nuestro proyecto la funcionalidad de Web3.

Revisar los archivos de inicio

Antes de empezar a programar, exploremos lo que se nos proporciona en los archivos de inicio.

Poner en marcha su proyecto de React

Comencemos ejecutando el proyecto de React en nuestro navegador. La belleza de React es que una vez que tenemos nuestro proyecto ejecutándose en nuestro navegador, cualquier cambio que guardemos se actualizará en vivo en nuestro navegador.

Para poner en marcha el proyecto, navegue hasta el directorio raíz de la carpeta starter-files y ejecute npm install en su terminal para instalar las dependencias del proyecto:

cd starter-files
npm install

Una vez que hayan terminado de instalarse, ejecute npm start en su terminal:

npm start

Al hacerlo, debería abrirse http://localhost:3000/ (opens in a new tab) en su navegador, donde verá el frontend de nuestro proyecto. Debería constar de un campo (un lugar para actualizar el mensaje almacenado en su contrato inteligente), un botón "Connect Wallet" (Conectar billetera) y un botón "Update" (Actualizar).

Si intenta hacer clic en cualquiera de los botones, notará que no funcionan; eso se debe a que todavía necesitamos programar su funcionalidad.

El componente HelloWorld.js

Volvamos a la carpeta src en nuestro editor y abramos el archivo HelloWorld.js. Es muy importante que entendamos todo en este archivo, ya que es el componente principal de React en el que trabajaremos.

En la parte superior de este archivo, notará que tenemos varias declaraciones de importación que son necesarias para que nuestro proyecto funcione, incluida la biblioteca de React, los hooks useEffect y useState, algunos elementos de ./util/interact.js (¡los describiremos con más detalle pronto!) y el logotipo de Alchemy.

A continuación, tenemos nuestras variables de estado que actualizaremos después de eventos específicos.

// HelloWorld.js

//Variables de estado
const [walletAddress, setWallet] = useState("")
const [status, setStatus] = useState("")
const [message, setMessage] = useState("No connection to the network.")
const [newMessage, setNewMessage] = useState("")

Esto es lo que representa cada una de las variables:

  • walletAddress: una cadena que almacena la dirección de la billetera del usuario
  • status: una cadena que almacena un mensaje útil que guía al usuario sobre cómo interactuar con la dapp
  • message: una cadena que almacena el mensaje actual en el contrato inteligente
  • newMessage: una cadena que almacena el nuevo mensaje que se escribirá en el contrato inteligente

Después de las variables de estado, verá cinco funciones no implementadas: useEffect, addSmartContractListener, addWalletListener, connectWalletPressed y onUpdatePressed. Explicaremos qué hacen a continuación:

  • useEffect (opens in a new tab): este es un hook de React que se llama después de que se renderiza su componente. Debido a que se le pasa una propiedad de matriz vacía [] (consulte la línea 4), solo se llamará en el primer renderizado del componente. Aquí cargaremos el mensaje actual almacenado en nuestro contrato inteligente, llamaremos a los oyentes de nuestro contrato inteligente y billetera, y actualizaremos nuestra interfaz de usuario para reflejar si ya hay una billetera conectada.
  • addSmartContractListener: esta función configura un oyente que observará el evento UpdatedMessages de nuestro contrato HelloWorld y actualizará nuestra interfaz de usuario cuando se cambie el mensaje en nuestro contrato inteligente.
  • addWalletListener: esta función configura un oyente que detecta cambios en el estado de la billetera MetaMask del usuario, como cuando el usuario desconecta su billetera o cambia de dirección.
  • connectWalletPressed: se llamará a esta función para conectar la billetera MetaMask del usuario a nuestra dapp.
  • onUpdatePressed: se llamará a esta función cuando el usuario quiera actualizar el mensaje almacenado en el contrato inteligente.

Cerca del final de este archivo, tenemos la interfaz de usuario de nuestro componente.

Si examina este código detenidamente, notará dónde usamos nuestras diversas variables de estado en nuestra interfaz de usuario:

  • En las líneas 6-12, si la billetera del usuario está conectada (es decir, walletAddress.length > 0), mostramos una versión truncada de la walletAddress del usuario en el botón con el ID "walletButton"; de lo contrario, simplemente dice "Connect Wallet" (Conectar billetera).
  • En la línea 17, mostramos el mensaje actual almacenado en el contrato inteligente, que se captura en la cadena message.
  • En las líneas 23-26, usamos un componente controlado (opens in a new tab) para actualizar nuestra variable de estado newMessage cuando cambia la entrada en el campo de texto.

Además de nuestras variables de estado, también verá que las funciones connectWalletPressed y onUpdatePressed se llaman cuando se hace clic en los botones con los ID publishButton y walletButton respectivamente.

Finalmente, abordemos dónde se agrega este componente HelloWorld.js.

Si va al archivo App.js, que es el componente principal en React que actúa como contenedor para todos los demás componentes, verá que nuestro componente HelloWorld.js se inyecta en la línea 7.

Por último, pero no menos importante, revisemos un archivo más que se le proporciona, el archivo interact.js.

El archivo interact.js

Debido a que queremos seguir el paradigma M-V-C (opens in a new tab), querremos un archivo separado que contenga todas nuestras funciones para administrar la lógica, los datos y las reglas de nuestra dapp, y luego poder exportar esas funciones a nuestro frontend (nuestro componente HelloWorld.js).

👆🏽¡Este es el propósito exacto de nuestro archivo interact.js!

Navegue a la carpeta util en su directorio src, y notará que hemos incluido un archivo llamado interact.js que contendrá todas nuestras funciones y variables de interacción de contratos inteligentes y billeteras.

Notará en la parte superior del archivo que hemos comentado el objeto helloWorldContract. Más adelante en este tutorial, descomentaremos este objeto e instanciaremos nuestro contrato inteligente en esta variable, que luego exportaremos a nuestro componente HelloWorld.js.

Las cuatro funciones no implementadas después de nuestro objeto helloWorldContract hacen lo siguiente:

  • loadCurrentMessage: esta función maneja la lógica de cargar el mensaje actual almacenado en el contrato inteligente. Realizará una llamada de lectura al contrato inteligente Hello World utilizando la API de Alchemy Web3 (opens in a new tab).
  • connectWallet: esta función conectará el MetaMask del usuario a nuestra dapp.
  • getCurrentWalletConnected: esta función verificará si una cuenta de Ethereum ya está conectada a nuestra dapp al cargar la página y actualizará nuestra interfaz de usuario en consecuencia.
  • updateMessage: esta función actualizará el mensaje almacenado en el contrato inteligente. Realizará una llamada de escritura al contrato inteligente Hello World, por lo que la billetera MetaMask del usuario tendrá que firmar una transacción de Ethereum para actualizar el mensaje.

Ahora que entendemos con qué estamos trabajando, ¡averigüemos cómo leer de nuestro contrato inteligente!

Paso 3: Leer de su contrato inteligente

Para leer de su contrato inteligente, deberá configurar correctamente:

  • Una conexión API a la cadena de Ethereum
  • Una instancia cargada de su contrato inteligente
  • Una función para llamar a la función de su contrato inteligente
  • Un oyente para observar las actualizaciones cuando cambian los datos que está leyendo del contrato inteligente

Esto puede sonar como muchos pasos, ¡pero no se preocupe! ¡Le guiaremos sobre cómo hacer cada uno de ellos paso a paso! :)

Establecer una conexión API a la cadena de Ethereum

¿Recuerdas cómo en la Parte 2 de este tutorial usamos nuestra clave de Alchemy Web3 para leer de nuestro contrato inteligente? También necesitarás una clave de Alchemy Web3 en tu aplicación descentralizada (dapp) para leer de la cadena.

Si aún no la tienes, primero instala Alchemy Web3 (opens in a new tab) navegando al directorio raíz de tus starter-files y ejecutando lo siguiente en tu terminal:

npm install @alch/alchemy-web3

Alchemy Web3 (opens in a new tab) es un envoltorio (wrapper) alrededor de Web3.js (opens in a new tab), que proporciona métodos de API mejorados y otros beneficios cruciales para hacer tu vida como desarrollador de Web3 más fácil. ¡Está diseñado para requerir una configuración mínima para que puedas comenzar a usarlo en tu aplicación de inmediato!

Luego, instala el paquete dotenv (opens in a new tab) en el directorio de tu proyecto, para que tengamos un lugar seguro donde almacenar nuestra clave API después de obtenerla.

npm install dotenv --save

Para nuestra dapp, usaremos nuestra clave API de Websockets en lugar de nuestra clave API de HTTP, ya que nos permitirá configurar un oyente (listener) que detecte cuándo cambia el mensaje almacenado en el contrato inteligente.

Una vez que tengas tu clave API, crea un archivo .env en tu directorio raíz y añade tu URL de Websockets de Alchemy en él. Después, tu archivo .env debería verse así:

REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<key>

¡Ahora estamos listos para configurar nuestro punto de conexión (endpoint) de Alchemy Web3 en nuestra dapp! Volvamos a nuestro interact.js, que está anidado dentro de nuestra carpeta util, y añadamos el siguiente código en la parte superior del archivo:

// interact.js

require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)

//export const helloWorldContract;

Arriba, primero importamos la clave de Alchemy desde nuestro archivo .env y luego pasamos nuestra alchemyKey a createAlchemyWeb3 para establecer nuestro punto de conexión de Alchemy Web3.

Con este punto de conexión listo, ¡es hora de cargar nuestro contrato inteligente!

Cargar su contrato inteligente Hello World

Para cargar su contrato inteligente Hello World, necesitará la dirección de su contrato y el ABI, los cuales se pueden encontrar en Etherscan si completó la Parte 3 de este tutorial.

Cómo obtener el ABI de su contrato desde Etherscan

Si se saltó la Parte 3 de este tutorial, puede usar el contrato HelloWorld con la dirección 0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab). Su ABI se puede encontrar aquí (opens in a new tab).

Un ABI de contrato es necesario para especificar qué función invocará un contrato, así como para garantizar que la función devuelva los datos en el formato que espera. Una vez que hayamos copiado el ABI de nuestro contrato, guardémoslo como un archivo JSON llamado contract-abi.json en su directorio src.

Su contract-abi.json debe almacenarse en su carpeta src.

Armados con la dirección de nuestro contrato, el ABI y el punto de conexión de Alchemy Web3, podemos usar el método contract (opens in a new tab) para cargar una instancia de nuestro contrato inteligente. Importe el ABI de su contrato en el archivo interact.js y agregue la dirección de su contrato.

// interact.js

const contractABI = require("../contract-abi.json")
const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"

Ahora finalmente podemos descomentar nuestra variable helloWorldContract y cargar el contrato inteligente utilizando nuestro punto de conexión de AlchemyWeb3:

// interact.js
export const helloWorldContract = new web3.eth.Contract(
  contractABI,
  contractAddress
)

Para recapitular, las primeras 12 líneas de su interact.js ahora deberían verse así:

Ahora que tenemos nuestro contrato cargado, ¡podemos implementar nuestra función loadCurrentMessage!

Implementar loadCurrentMessage en su archivo interact.js

Esta función es súper simple. Vamos a hacer una simple llamada asíncrona de Web3 para leer de nuestro contrato. Nuestra función devolverá el mensaje almacenado en el contrato inteligente:

Actualice loadCurrentMessage en su archivo interact.js a lo siguiente:

// interact.js

export const loadCurrentMessage = async () => {
  const message = await helloWorldContract.methods.message().call()
  return message
}

Dado que queremos mostrar este contrato inteligente en nuestra interfaz de usuario, actualicemos la función useEffect en nuestro componente HelloWorld.js a lo siguiente:

// HelloWorld.js

//llamada solo una vez
useEffect(async () => {
  const message = await loadCurrentMessage()
  setMessage(message)
}, [])

Tenga en cuenta que solo queremos que se llame a nuestro loadCurrentMessage una vez durante el primer renderizado del componente. Pronto implementaremos addSmartContractListener para actualizar automáticamente la interfaz de usuario después de que cambie el mensaje en el contrato inteligente.

Antes de sumergirnos en nuestro oyente, ¡revisemos lo que tenemos hasta ahora! Guarde sus archivos HelloWorld.js y interact.js, y luego vaya a http://localhost:3000/ (opens in a new tab)

Notará que el mensaje actual ya no dice "No connection to the network" (Sin conexión a la red). En su lugar, refleja el mensaje almacenado en el contrato inteligente. ¡Genial!

Su interfaz de usuario ahora debería reflejar el mensaje almacenado en el contrato inteligente

Ahora hablando de ese oyente...

Implementar addSmartContractListener

Si recuerda el archivo HelloWorld.sol que escribimos en la Parte 1 de esta serie de tutoriales, recordará que hay un evento de contrato inteligente llamado UpdatedMessages que se emite después de que se invoca la función update de nuestro contrato inteligente (consulte las líneas 9 y 27):

Los eventos de contratos inteligentes son una forma de que su contrato comunique que algo sucedió (es decir, hubo un evento) en la cadena de bloques a su aplicación front-end, que puede estar 'escuchando' eventos específicos y tomar medidas cuando suceden.

La función addSmartContractListener va a escuchar específicamente el evento UpdatedMessages de nuestro contrato inteligente Hello World y actualizará nuestra interfaz de usuario para mostrar el nuevo mensaje.

Modifique addSmartContractListener a lo siguiente:

Desglosemos lo que sucede cuando el oyente detecta un evento:

  • Si ocurre un error cuando se emite el evento, se reflejará en la interfaz de usuario a través de nuestra variable de estado status.
  • De lo contrario, usaremos el objeto data devuelto. data.returnValues es una matriz indexada en cero donde el primer elemento de la matriz almacena el mensaje anterior y el segundo elemento almacena el actualizado. En conjunto, en un evento exitoso, estableceremos nuestra cadena message en el mensaje actualizado, borraremos la cadena newMessage y actualizaremos nuestra variable de estado status para reflejar que se ha publicado un nuevo mensaje en nuestro contrato inteligente.

Finalmente, llamemos a nuestro oyente en nuestra función useEffect para que se inicialice en el primer renderizado del componente HelloWorld.js. En conjunto, su función useEffect debería verse así:

// HelloWorld.js

useEffect(async () => {
  const message = await loadCurrentMessage()
  setMessage(message)
  addSmartContractListener()
}, [])

Ahora que podemos leer de nuestro contrato inteligente, ¡sería genial descubrir cómo escribir en él también! Sin embargo, para escribir en nuestra dapp, primero debemos tener una billetera de Ethereum conectada a ella.

Por lo tanto, a continuación abordaremos la configuración de nuestra billetera de Ethereum (MetaMask) y luego la conectaremos a nuestra dapp.

Paso 4: Configurar su billetera de Ethereum

Para escribir cualquier cosa en la cadena de Ethereum, los usuarios deben firmar transacciones utilizando las claves privadas de su billetera virtual. Para este tutorial, usaremos MetaMask (opens in a new tab), una billetera virtual en el navegador que se usa para administrar la dirección de su cuenta de Ethereum, ya que hace que esta firma de transacciones sea muy fácil para el usuario final.

Si desea comprender más sobre cómo funcionan las transacciones en Ethereum, consulte esta página de la Fundación Ethereum.

Descargar MetaMask

Puede descargar y crear una cuenta de MetaMask de forma gratuita aquí (opens in a new tab). Cuando esté creando una cuenta, o si ya tiene una, asegúrese de cambiar a la "Goerli Test Network" (Red de prueba Goerli) en la parte superior derecha (para que no estemos lidiando con dinero real).

Agregar ether desde un faucet

Para firmar una transacción en la cadena de bloques de Ethereum, necesitaremos algo de ETH falso. Para obtener ETH, puede ir al FaucETH (opens in a new tab) e ingresar la dirección de su cuenta de Goerli, hacer clic en "Request funds" (Solicitar fondos), luego seleccionar "Ethereum Testnet Goerli" en el menú desplegable y finalmente hacer clic en el botón "Request funds" nuevamente. ¡Debería ver ETH en su cuenta de MetaMask poco después!

Comprueba tu saldo

Para comprobar que nuestro saldo está ahí, hagamos una solicitud eth_getBalance (opens in a new tab) utilizando la herramienta sandbox de Alchemy (opens in a new tab). Esto devolverá la cantidad de ETH en nuestra billetera. Después de ingresar la dirección de tu cuenta de MetaMask y hacer clic en "Send Request" (Enviar solicitud), deberías ver una respuesta como esta:

{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

NOTA: Este resultado está en wei, no en ether. Wei se utiliza como la denominación más pequeña de ether. La conversión de wei a ether es: 1 ether = 10¹⁸ wei. Así que si convertimos 0xde0b6b3a7640000 a decimal obtenemos 1*10¹⁸, lo que equivale a 1 ether.

¡Uf! ¡Nuestro dinero falso está todo ahí! 🤑

Paso 5: Conectar MetaMask a su interfaz de usuario

Ahora que nuestra billetera MetaMask está configurada, ¡conectemos nuestra dapp a ella!

La función connectWallet

En nuestro archivo interact.js, implementemos la función connectWallet, que luego podemos llamar en nuestro componente HelloWorld.js.

Modifiquemos connectWallet a lo siguiente:

Entonces, ¿qué hace exactamente este bloque gigante de código?

Bueno, primero, verifica si window.ethereum está habilitado en su navegador.

window.ethereum es una API global inyectada por MetaMask y otros proveedores de billeteras que permite a los sitios web solicitar las cuentas de Ethereum de los usuarios. Si se aprueba, puede leer datos de las cadenas de bloques a las que está conectado el usuario y sugerir que el usuario firme mensajes y transacciones. ¡Consulte la documentación de MetaMask (opens in a new tab) para obtener más información!

Si window.ethereum no está presente, eso significa que MetaMask no está instalado. Esto da como resultado que se devuelva un objeto JSON, donde address devuelto es una cadena vacía, y el objeto JSX status transmite que el usuario debe instalar MetaMask.

Ahora, si window.ethereum está presente, entonces es cuando las cosas se ponen interesantes.

Usando un bucle try/catch, intentaremos conectarnos a MetaMask llamando a window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Llamar a esta función abrirá MetaMask en el navegador, por lo que se le pedirá al usuario que conecte su billetera a su dapp.

  • Si el usuario elige conectarse, method: "eth_requestAccounts" devolverá una matriz que contiene todas las direcciones de cuenta del usuario que se conectaron a la dapp. En conjunto, nuestra función connectWallet devolverá un objeto JSON que contiene la primera address en esta matriz (consulte la línea 9) y un mensaje status que solicita al usuario que escriba un mensaje en el contrato inteligente.
  • Si el usuario rechaza la conexión, entonces el objeto JSON contendrá una cadena vacía para la address devuelta y un mensaje status que refleja que el usuario rechazó la conexión.

Ahora que hemos escrito esta función connectWallet, el siguiente paso es llamarla en nuestro componente HelloWorld.js.

Agregar la función connectWallet a su componente de interfaz de usuario HelloWorld.js

Navegue a la función connectWalletPressed en HelloWorld.js y actualícela a lo siguiente:

// HelloWorld.js

const connectWalletPressed = async () => {
  const walletResponse = await connectWallet()
  setStatus(walletResponse.status)
  setWallet(walletResponse.address)
}

¿Nota cómo la mayor parte de nuestra funcionalidad se abstrae de nuestro componente HelloWorld.js desde el archivo interact.js? ¡Esto es para que cumplamos con el paradigma M-V-C!

En connectWalletPressed, simplemente hacemos una llamada await a nuestra función importada connectWallet, y usando su respuesta, actualizamos nuestras variables status y walletAddress a través de sus hooks de estado.

Ahora, guardemos ambos archivos (HelloWorld.js y interact.js) y probemos nuestra interfaz de usuario hasta ahora.

Abra su navegador en la página http://localhost:3000/ (opens in a new tab) y presione el botón "Connect Wallet" (Conectar billetera) en la parte superior derecha de la página.

Si tiene MetaMask instalado, se le pedirá que conecte su billetera a su dapp. Acepte la invitación para conectarse.

¡Debería ver que el botón de la billetera ahora refleja que su dirección está conectada! Siiiiii 🔥

A continuación, intente actualizar la página... esto es extraño. Nuestro botón de billetera nos pide que conectemos MetaMask, a pesar de que ya está conectado...

Sin embargo, ¡no tema! ¡Podemos solucionar eso fácilmente implementando getCurrentWalletConnected, que verificará si una dirección ya está conectada a nuestra dapp y actualizará nuestra interfaz de usuario en consecuencia!

La función getCurrentWalletConnected

Actualice su función getCurrentWalletConnected en el archivo interact.js a lo siguiente:

Este código es muy similar a la función connectWallet que acabamos de escribir en el paso anterior.

La principal diferencia es que en lugar de llamar al método eth_requestAccounts, que abre MetaMask para que el usuario conecte su billetera, aquí llamamos al método eth_accounts, que simplemente devuelve una matriz que contiene las direcciones de MetaMask actualmente conectadas a nuestra dapp.

Para ver esta función en acción, llamémosla en nuestra función useEffect de nuestro componente HelloWorld.js:

Tenga en cuenta que usamos la respuesta de nuestra llamada a getCurrentWalletConnected para actualizar nuestras variables de estado walletAddress y status.

Ahora que ha agregado este código, intentemos actualizar la ventana de nuestro navegador.

¡Genial! El botón debería decir que está conectado y mostrar una vista previa de la dirección de su billetera conectada, ¡incluso después de actualizar!

Implementar addWalletListener

El paso final en la configuración de la billetera de nuestra dapp es implementar el oyente de la billetera para que nuestra interfaz de usuario se actualice cuando cambie el estado de nuestra billetera, como cuando el usuario se desconecta o cambia de cuenta.

En su archivo HelloWorld.js, modifique su función addWalletListener de la siguiente manera:

Apuesto a que ni siquiera necesita nuestra ayuda para comprender lo que está sucediendo aquí en este punto, pero por motivos de exhaustividad, desglosémoslo rápidamente:

  • Primero, nuestra función verifica si window.ethereum está habilitado (es decir, MetaMask está instalado).
    • Si no lo está, simplemente establecemos nuestra variable de estado status en una cadena JSX que solicita al usuario que instale MetaMask.
    • Si está habilitado, configuramos el oyente window.ethereum.on("accountsChanged") en la línea 3 que escucha los cambios de estado en la billetera MetaMask, que incluyen cuando el usuario conecta una cuenta adicional a la dapp, cambia de cuenta o desconecta una cuenta. Si hay al menos una cuenta conectada, la variable de estado walletAddress se actualiza como la primera cuenta en la matriz accounts devuelta por el oyente. De lo contrario, walletAddress se establece como una cadena vacía.

Por último, pero no menos importante, debemos llamarlo en nuestra función useEffect:

¡Y eso es todo! ¡Hemos completado con éxito la programación de toda la funcionalidad de nuestra billetera! Ahora a nuestra última tarea: ¡actualizar el mensaje almacenado en nuestro contrato inteligente!

Paso 6: Implementar la función updateMessage

Muy bien familia, ¡hemos llegado a la recta final! En el updateMessage de su archivo interact.js, vamos a hacer lo siguiente:

  1. Asegurarnos de que el mensaje que deseamos publicar en nuestro contrato inteligente sea válido
  2. Firmar nuestra transacción utilizando MetaMask
  3. Llamar a esta función desde nuestro componente frontend HelloWorld.js

Esto no tomará mucho tiempo; ¡terminemos esta dapp!

Manejo de errores de entrada

Naturalmente, tiene sentido tener algún tipo de manejo de errores de entrada al comienzo de la función.

Querremos que nuestra función regrese temprano si no hay una extensión de MetaMask instalada, no hay una billetera conectada (es decir, la address pasada es una cadena vacía) o el message es una cadena vacía. Agreguemos el siguiente manejo de errores a updateMessage:

Ahora que tiene un manejo adecuado de errores de entrada, ¡es hora de firmar la transacción a través de MetaMask!

Firmar nuestra transacción

Si ya se siente cómodo con las transacciones tradicionales de Web3 en Ethereum, el código que escribiremos a continuación le resultará muy familiar. Debajo de su código de manejo de errores de entrada, agregue lo siguiente a updateMessage:

Desglosemos lo que está sucediendo. Primero, configuramos los parámetros de nuestras transacciones, donde:

  • to especifica la dirección del destinatario (nuestro contrato inteligente)
  • from especifica el firmante de la transacción, la variable address que pasamos a nuestra función
  • data contiene la llamada al método update de nuestro contrato inteligente Hello World, recibiendo nuestra variable de cadena message como entrada

Luego, hacemos una llamada await, window.ethereum.request, donde le pedimos a MetaMask que firme la transacción. Tenga en cuenta que, en las líneas 11 y 12, estamos especificando nuestro método eth, eth_sendTransaction y pasando nuestro transactionParameters.

En este punto, MetaMask se abrirá en el navegador y le pedirá al usuario que firme o rechace la transacción.

  • Si la transacción es exitosa, la función devolverá un objeto JSON donde la cadena JSX status solicita al usuario que consulte Etherscan para obtener más información sobre su transacción.
  • Si la transacción falla, la función devolverá un objeto JSON donde la cadena status transmite el mensaje de error.

En conjunto, nuestra función updateMessage debería verse así:

Por último, pero no menos importante, necesitamos conectar nuestra función updateMessage a nuestro componente HelloWorld.js.

Conectar updateMessage al frontend HelloWorld.js

Nuestra función onUpdatePressed debería hacer una llamada await a la función importada updateMessage y modificar la variable de estado status para reflejar si nuestra transacción tuvo éxito o falló:

// HelloWorld.js

const onUpdatePressed = async () => {
  const { status } = await updateMessage(walletAddress, newMessage)
  setStatus(status)
}

Es súper limpio y simple. Y adivine qué... ¡¡¡SU DAPP ESTÁ COMPLETA!!!

¡Adelante, pruebe el botón Update (Actualizar)!

Cree su propia dapp personalizada

¡Wooooo, llegó al final del tutorial! Para recapitular, aprendió cómo:

  • Conectar una billetera MetaMask a su proyecto de dapp
  • Leer datos de su contrato inteligente utilizando la API de Alchemy Web3 (opens in a new tab)
  • Firmar transacciones de Ethereum utilizando MetaMask

¡Ahora está completamente equipado para aplicar las habilidades de este tutorial para construir su propio proyecto de dapp personalizado! Como siempre, si tiene alguna pregunta, no dude en comunicarse con nosotros para obtener ayuda en el Discord de Alchemy (opens in a new tab). 🧙‍♂️

Una vez que complete este tutorial, ¡háganos saber cómo fue su experiencia o si tiene algún comentario etiquetándonos en Twitter @alchemyplatform (opens in a new tab)!