Ir al contenido principal

Contrato inteligente de Hello World para principiantes: Fullstack

solidityhardhatalchemycontratos inteligentesimplementaciónblockexplorerfrontendtransacciones
Principiante
nstrike2
25 de octubre de 2021
46 minuto leído minute read

Si es nuevo en el desarrollo de la cadena de bloques y no sabe por dónde empezar o cómo implementar e interactuar con contratos inteligentes, esta guía está hecha a su medida. Exploraremos la creación e implementación de un contrato simple e inteligente en la red de prueba de 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á una cuenta de Alchemy para completar este tutorial. Regístrese para obtener una cuenta gratuita(opens in a new tab).

En cualquier momento que le surjan dudas, ¡no dude en ponerse en contacto con el canal Discord de Alchemy(opens in a new tab).

Parte 1: Cree e implemente su contrato inteligente usando Hardhat

Conéctarse a la red de Ethereum

Hay muchas maneras de hacer solicitudes a la cadena Ethereum. Para simplificar, usaremos una cuenta gratuita en Alchemy, una plataforma de desarrollo de cadena de bloques y una API que nos permite comunicarnos con la cadena Ethereum sin ejecutar un nodo nosotros mismos. Alchemy también tiene herramientas de desarrollo para el control y el análisis. Las abordaremos en este tutorial y así entenderemos los entresijos de nuestra implementación de contratos inteligentes.

Cómo crear su aplicación y clave de API

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

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

Crear la aplicación Hola, mundo

Dele a su aplicación el nombre 'Hello World' y escriba una breve descripción. Seleccione Staging como su entorno y Goerli como su red.

Crear App visualizar Hola, mundo

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

Haga clic en Create app. Su aplicación aparecerá en la siguiente tabla.

Cómo crear una cuenta en Ethereum

Necesita tener una cuenta de Ethereum para enviar y recibir transacciones. Utilizaremos MetaMask, una cartera virtual en el navegador que permite a los usuarios gestionar la dirección de su cuenta de Ethereum.

Puede descargar y crear una cuenta Metamask gratis aquí(opens in a new tab). Cuando esté creando una cuenta, o si ya tiene una, asegúrese de cambiar a la “red de prueba Goerli” en la parte superior derecha (para no operar con dinero real).

Paso 4: Añadir ether de un faucet

Para implementar su contrato inteligente en la red de prueba, necesitará algunos ETH falsos. Para obtener ETH en la red Goerli, vaya a un grifo de Goerli e introduzca la dirección de su cuenta de Goerli. Observe que los grifos de Goerli pueden ser poco fiables recientemente. En la página de prueba de redes verá un listado de opciones para probar:

Nota: debido a la congestión de la red, esto puede llevar un tiempo. ``

Paso 5: Comprobar su balance

Para volver a comprobar que hay ETH en su cartera, hagamos una solicitud eth_getBalance(opens in a new tab) usando la herramienta de compositor de Alchemy(opens in a new tab). Esto devolverá la cantidad de ETH a nuestra cartera. Si desea ampliar esta información, eche un vistazo al breve tutorial de Alchemy sobre cómo usar la herramienta de compositor(opens in a new tab).

Introduzca la dirección de su cuenta de MetaMask y haga clic en Send request. Verás una respuesta que se parece al fragmento de código a continuación.

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

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

¡Fiu! Nuestro dinero de prueba está ahí sano y salvo.

Paso 6: Iniciar nuestro proyecto

En primer lugar, tendremos que crear una carpeta para nuestro proyecto. Vaya a su línea de comandos e introduzca lo siguiente.

1mkdir hello-world
2cd hello-world

Ahora que estamos dentro de nuestra carpeta de proyecto, usaremos npm init a fin de inicializar el proyecto.

Si aún no tiene npm instalado, siga estas instrucciones para instalar Node.js y npm(opens in a new tab).

Para el propósito de este tutorial, no importa cómo responda a las preguntas de inicialización. Así respondimos nosotros, a modo de referencia:

1package name: (hello-world)
2version: (1.0.0)
3description: hello world smart contract
4entry point: (index.js)
5test command:
6git repository:
7keywords:
8author:
9license: (ISC)
10
11About to write to /Users/.../.../.../hello-world/package.json:
12
13{
14 "name": "hello-world",
15 "version": "1.0.0",
16 "description": "hello world smart contract",
17 "main": "index.js",
18 "scripts": {
19 "test": "echo \"Error: no test specified\" && exit 1"
20 },
21 "author": "",
22 "license": "ISC"
23}
Mostrar todo

Apruebe el package.json y listo.

Paso 7: Descargar Hardhat

Hardhat es un entorno de desarrollo para compilar, implementar, probar y depurar su software de Ethereum. Ayuda a los desarrolladores cuando crean contratos inteligentes y dApps localmente antes de la implementación en la cadena real.

Dentro de nuestro proyecto hello-world, ejecute:

1npm install --save-dev hardhat

Revise esta página para obtener más información acerca de las intrucciones de instalación(opens in a new tab).

Paso 8: Crear proyecto Hardhat

Dentro de nuestra carpeta de proyectos hello-world, ejecute:

1npx hardhat

Debería aparecer un mensaje de bienvenida y la opción de seleccionar lo que desee hacer. Seleccione «create an empty hardhat.config.js»:

1888 888 888 888 888
2888 888 888 888 888
3888 888 888 888 888
48888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
5888 888 "88b 888P" d88" 888 888 "88b "88b 888
6888 888 .d888888 888 888 888 888 888 .d888888 888
7888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
9
10👷 Welcome to Hardhat v2.0.11 👷‍
11
12What do you want to do? …
13Create a sample project
14❯ Create an empty hardhat.config.js
15Quit
Mostrar todo

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

Paso 9: Añadir carpetas de proyecto

Para mantener el proyecto organizado, vamos a crear dos carpetas nuevas. En la línea de comandos, vaya al directorio raíz de su proyecto hello-world y escriba:

1mkdir contracts
2mkdir scripts
  • contratos/ es donde mantendremos nuestro archivo de código del contrato inteligente Hola, mundo
  • scripts/ es donde mantendremos los scripts para implementar e interactuar con nuestro contrato

Paso 10: Escribir nuestro contrato

Puede que se esté preguntando que cuándo vamos a escribir el código. ¡Ahora es el momento!

Abra el proyecto hello-world en su editor favorito. Los contratos inteligentes se suelen escribir más comunmente en Solidity, que usaremos para escribir nuestro contrato inteligente

  1. Vaya a la carpeta contratos y cree un nuevo archivo llamado HelloWorld.sol
  2. A continuación se muestra un ejemplo del contrato inteligente de Hello World que usaremos para este tutorial. Copie el contenido a continuación en el archivo HelloWorld.sol.

Nota: asegúrese de leer los comentarios para entender lo que hace este contrato.

1// Especifica la versión de Solidity, utilizando la versión semántica.
2// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity >=0.7.3;
4
5// Defines a contract named `HelloWorld`.
6// Un contrato es una colección de funciones y datos (su estado). Una vez desplegado, un contrato reside en una dirección específica en la blockchain de Ethereum. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
7contract HelloWorld {
8
9 //Emitted when update function is called
10 //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.
11 event UpdatedMessages(string oldStr, string newStr);
12
13 // Declares a state variable `message` of type `string`.
14 // Las variables de estado son variables cuyos valores se almacenan permanentemente en el almacenamiento del contrato. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value.
15 string public message;
16
17 // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation.
18 // Los constructores se utilizan para inicializar los datos del contrato. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
19 constructor(string memory initMessage) {
20
21 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).
22 message = initMessage;
23 }
24
25 // A public function that accepts a string argument and updates the `message` storage variable.
26 function update(string memory newMessage) public {
27 string memory oldMsg = message;
28 message = newMessage;
29 emit UpdatedMessages(oldMsg, newMessage);
30 }
31}
Mostrar todo

Este es un contrato inteligente básico que almacena un mensaje en el momento de la creación. Se puede actualizar activando la función update.

Paso 11: Conectar MetaMask & Alchemy a su proyecto

Hemos creado una billetera de Metamask, una cuenta de Alchemy y escrito nuestro contrato inteligente, ahora es momento de conectarlos entre sí.

Cada transacción enviada desde su cartera requiere una firma con su 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 de API para Alchemy aquí.

Si quiere ahondar sobre el envío de transacciones, consulte este tutorial(opens in a new tab) sobre el envío de transacciones usando web3.

Primero, instale el paquete dotenv en su directorio de proyecto:

1npm install dotenv --save

A continuación, cree un archivo .env en el directorio raíz del proyecto. Añade tu clave privada de MetaMask y la URL de la API de HTTP Alchemy.

Su archivo de entorno debe llamarse .env o no se reconocerá como un archivo de entorno.

No lo nombre process.env o .env-custom ni nada más.

Su .env debería verse así:

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PRIVATE_KEY = "tu-clave privada-metamask"

Para conectarlos efectivamente a nuestro código, vincularemos estas variables en nuestro 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 agrupando métodos JSON-RPC estándar(opens in a new tab) con métodos más fáciles para el usuario.

Hardhat nos permite integrar plugins(opens in a new tab) para obtener herramientas adicionales y una funcionalidad ampliada. Aprovecharemos el complemento Ethers plugin(opens in a new tab) para la implementación por contrato.

En el directorio de su proyecto, teclee:

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

Paso 13: Actualizar hardhat.config.js

Hasta ahora hemos añadido varias dependencias y plugins, por lo que ahora necesitamos actualizar hardhat.config.js para que nuestro proyecto sepa de todas ellas.

Actualice su hardhat.config.js para que muestre el siguiente texto:

1/**
2 * @type import('hardhat/config').HardhatUserConfig
3 */
4
5require("dotenv").config()
6require("@nomiclabs/hardhat-ethers")
7
8const { API_URL, PRIVATE_KEY } = process.env
9
10module.exports = {
11 solidity: "0.7.3",
12 defaultNetwork: "goerli",
13 networks: {
14 hardhat: {},
15 goerli: {
16 url: API_URL,
17 accounts: [`0x${PRIVATE_KEY}`],
18 },
19 },
20}
Mostrar todo

Paso 14: Compilar nuestro contrato

Para asegurarnos de que todo funciona correctamente hasta ahora, compilemos nuestro contrato. La función compile está incluida dentro de las funciones por defecto de hardhat.

Desde la linea de comandos, ejecute:

npx hardhat compile

Es posible que reciba una advertencia sobre el identificador de licencia SPDX no proporcionado en el archivo de origen, pero no hay necesidad de preocuparse por eso, ¡esperemos que todo lo demás se vea bien! Si no es así, siempre puede escribir un mensaje en Alchemy discord(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 momento de escribir nuestro script de implementación del contrato.

Vaya a la carpeta scripts/ y cree un nuevo archivo llamado deploy.js, agregando los siguientes contenidos:

1async function main() {
2 const HelloWorld = await ethers.getContractFactory("HelloWorld")
3
4 // Start deployment, returning a promise that resolves to a contract object
5 const hello_world = await HelloWorld.deploy("Hello World!")
6 console.log("Contract deployed to address:", hello_world.address)
7}
8
9main()
10 .then(() => process.exit(0))
11 .catch((error) => {
12 console.error(error)
13 process.exit(1)
14 })
Mostrar todo

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

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

Una ContractFactory en ethers.js es una abstracción utilizada para implementar nuevos contratos inteligentes, por lo que HelloWorld aquí es una factory(opens in a new tab) para las instancias de nuestro contrato de Hello World. Cuando se utiliza el complemento hardhat-ethers ContractFactory y Contract, las instancias se conectan al primer firmante (propietario) de forma predeterminada.

1const hello_world = await HelloWorld.deploy()

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

Paso 16: Desplegar nuestro contrato

¡Por fin estamos listos para desplegar nuestro contrato inteligente! Desde la linea de comandos, ejecute:

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

Debería mostrarse algo parecido a esto:

Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570

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

Si vamos a la dirección Ropsten etherscan(opens in a new tab) y buscamos la dirección de nuestro contrato, podremos comprobar que se ha desplegado correctamente. El objeto de la transacción tendrá un aspecto parecido a esto:

La dirección from debe coincidir con la dirección de su cuenta de MetaMask y la dirección To dirá Contrcat creation. Si hacemos clic en la transacción, veremos la dirección de nuestro contrato en el campo to.

¡Felicidades! Acabas de implementar un contrato inteligente en una red de pruebas de Ethereum.

Para entender lo que está pasando internamente, vayamos a la pestaña de Explorer en nuestro panel Alchemy(opens in a new tab). Si tienes varias aplicaciones de Alchemy, asegúrate de filtrar por aplicación y selecciona Hello World.

Aquí verás un puñado de métodos JSON-RPC que Hardhat/Ethers hizo bajo el capó para 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, consulte nuestro tutorial sobre el envío de transacciones utilizando Web3.

Parte 2: Interactúa con tu contrato inteligente

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

Crear un archivo interact.js

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

Dentro de la carpeta scripts/, cree un nuevo archivo llamado interact.js y agregue el siguiente código:

1// interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS

Actualiza tu archivo .env

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

Tendremos que añadir una definición para nuestra Alchemy API_KEY y la CONTRACT_ADDRESS donde se desplegó su contrato inteligente.

Su archivo .env debería tener un aspecto similar a esto:

# .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>"

Toma tu contrato ABI

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, tendremos que analizar el contenido añadiendo las siguientes líneas de código a nuestro archivo interact.js:

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

Si quiere ver la ABI puede hacerlo en su propia consola:

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

Para ver su ABI impreso en la consola, vaya a su terminal y ejecute:

npx hardhat run scripts/interact.js

Crea una instancia de tu contrato

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

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

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

1// interact.js
2
3// Provider
4const alchemyProvider = new ethers.providers.AlchemyProvider(
5 (network = "goerli"),
6 API_KEY
7)
8
9// Signer
10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
11
12// Contract
13const helloWorldContract = new ethers.Contract(
14 CONTRACT_ADDRESS,
15 contract.abi,
16 signer
17)
Mostrar todo

Obtenga más información sobre proveedores, firmantes y contratos en la documentación de ethers.js(opens in a new tab).

Lee el mensaje de inicio

Recuerda cuando implementamos nuestro contrato con el initMessage = "¡Hola mundo!" ? 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 las redes. Para obtener más información sobre las funciones asíncronas, lea este artículo de medium(opens in a new tab).

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

1// interact.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8}
9main()
Mostrar todo

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

1The message is: Hello world!

¡Felicidades! Acaba de leer con éxito los datos de los contratos inteligentes de la cadena de bloques de Ethereum, ¡así se hace!

Actualizar el mensaje

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

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

1// interact.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8
9 console.log("Updating the message...")
10 const tx = await helloWorldContract.update("This is the new message.")
11 await tx.wait()
12}
13main()
Mostrar todo

Tenga en cuenta que en la línea 11, hacemos una llamada a .wait() en el objeto de transacción devuelto. Esto garantiza que nuestro script espere a que la transacción se mine en la cadena de bloques antes de salir de la función. Si la llamada .wait() no está incluida, es posible que el script no vea el valor message actualizado en el contrato.

Lee el nuevo mensaje

Deberías poder repetir el paso anterior para leer el valor actualizado del mensaje. ¡Tómese un momento y vea si puede hacer los cambios necesarios para imprimir ese nuevo valor!

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

1// interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
6
7const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
8
9// provider - Alchemy
10const alchemyProvider = new ethers.providers.AlchemyProvider(
11 (network = "goerli"),
12 API_KEY
13)
14
15// signer - you
16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
17
18// contract instance
19const helloWorldContract = new ethers.Contract(
20 CONTRACT_ADDRESS,
21 contract.abi,
22 signer
23)
24
25async function main() {
26 const message = await helloWorldContract.message()
27 console.log("The message is: " + message)
28
29 console.log("Updating the message...")
30 const tx = await helloWorldContract.update("this is the new message")
31 await tx.wait()
32
33 const newMessage = await helloWorldContract.message()
34 console.log("The new message is: " + newMessage)
35}
36
37main()
Mostrar todo

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

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

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

Mientras ejecuta ese script, puede notar que el paso Actualice el mensaje... tarda un tiempo en cargarse antes de que se cargue el nuevo mensaje. Eso se debe al proceso de minería; si tiene curiosidad sobre el seguimiento de las transacciones mientras se están minando, visite el mempool de Alchemy(opens in a new tab) para ver el estado de una transacción. Si se elimina la transacción, también es útil comprobar Goerli Etherscan(opens in a new tab) y buscar el hash de su transacción.

Parte 3: Publica tu contrato inteligente en Etherscan

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

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

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

Se necesita una clave API de Etherscan para verificar que es el propietario del contrato inteligente que está 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 haya iniciado sesión, busque su nombre de usuario en la barra de navegación, pase el cursor sobre él y seleccione el botón Mi perfil.

En tu página de perfil, deberías ver una barra de navegación lateral. En la barra de navegación lateral, seleccione Teclas API. A continuación, presione el botón "Añadir" para crear una nueva clave de API, nombre su aplicación hello-worldy presione el botón Crear nueva clave de API.

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

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

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

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

Contratos inteligentes desplegados por Hardhat

Instalar hardhat-etherscan

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

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

Una vez instalado, incluya la siguiente declaración en la parte superior de su hardhat.config.js, y agregue las opciones de configuración de Etherscan:

1// hardhat.config.js
2
3require("dotenv").config()
4require("@nomiclabs/hardhat-ethers")
5require("@nomiclabs/hardhat-etherscan")
6
7const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env
8
9module.exports = {
10 solidity: "0.7.3",
11 defaultNetwork: "goerli",
12 networks: {
13 hardhat: {},
14 goerli: {
15 url: API_URL,
16 accounts: [`0x${PRIVATE_KEY}`],
17 },
18 },
19 etherscan: {
20 // Your API key for Etherscan
21 // Obtain one at https://etherscan.io/
22 apiKey: ETHERSCAN_API_KEY,
23 },
24}
Mostrar todo

Verifique su contrato inteligente en Etherscan

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

Ejecute la tarea verificar, pasando la dirección del contrato y la red a donde está desplegada:

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

Asegúrese de que DEPLOYED_CONTRACT_ADDRESS sea la dirección de su contrato inteligente implementado en la red de pruebas de Goerli. Además, el argumento final ('¡Hola mundo!' ) 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:

1Successfully submitted source code for contract
2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
3for verification on Etherscan. Waiting for verification result...
4
5
6Successfully verified contract HelloWorld on Etherscan.
7https://goerli.etherscan.io/address/<contract-address>#contracts

¡Felicidades! ¡Tu código de contrato inteligente está en Etherescan!

¡Echa un vistazo a tu contrato inteligente en Etherscan!

Cuando navegue al enlace proporcionado en su terminal, ¡debería poder ver su código de contrato inteligente y ABI publicado en Etherscan!

Wahooo - ¡lo hiciste campeón! ¡Ahora cualquiera puede llamar o escribir a tu contrato inteligente! ¡Estamos deseando ver lo que construyes a continuación!

Parte 4 - Integración de su contrato inteligente con la interfaz

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

  • Conecta una cartera MetaMask a tu dapp
  • Leer los datos de su contrato inteligente utilizando la API Alchemy Web3(opens in a new tab)
  • Firmar transacciones de Ethereum usando MetaMask

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

Como requisito previo, debes tener una comprensión de React a nivel principiante. Si no, recomendamos completar el tutorial oficial Introducción a React(opens in a new tab).

Clone los archivos de inicio

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

Abra el repositorio clonado localmente. Tenga en cuenta que contiene dos carpetas: archivos de inicio y completado.

  • archivos de inicio- estaremos trabajando en este directorio, conectaremos la interfaz de usuario a su billetera Ethereum y al contrato inteligente que publicamos en Etherscan en Parte 3.
  • completedContiene todo el tutorial completado y solo debe usarse como referencia si te quedas 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 permanecerá en la carpeta src. Editaremos el componente HelloWorld.js y los archivos JavaScript util/interact.js para dar a nuestro proyecto la funcionalidad Web3.

Echa un vistazo a los archivos iniciales

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

Ejecute su proyecto de react

Comencemos por ejecutar el proyecto React en nuestro navegador. La belleza de React es que, una vez que tenemos nuestro proyecto corriendo en el navegador, cualquier cambio que guardemos será actualizado en vivo en el navegador.

Para ejecutar 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 abrir http://localhost:3000/(opens in a new tab) en su navegador, donde verá la interfaz de nuestro proyecto. Debe consistir en un campo (un lugar para actualizar el mensaje almacenado en su contrato inteligente), un botón "Conectar cartera" y un botón "Actualizar".

Si intentas hacer clic en cualquiera de los botones, te darás cuenta de que no funcionan, eso es porque todavía tenemos que programar su funcionalidad.

El componente HelloWorld.js

Volvamos a la carpeta src de nuestro editor y abramos el archivo HelloWorld.js. Es muy importante que entendamos todo en este archivo, ya que es el componente principal en 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 se ejecute, incluida la biblioteca React, los ganchos useEffect y useState, algunos elementos del ./util/interact.js (¡los describiremos con más detalle pronto!), y el logotipo de Alchemy.

1// HelloWorld.js
2
3import React from "react"
4import { useEffect, useState } from "react"
5import {
6 helloWorldContract,
7 connectWallet,
8 updateMessage,
9 loadCurrentMessage,
10 getCurrentWalletConnected,
11} from "./util/interact.js"
12
13import alchemylogo from "./alchemylogo.svg"
Mostrar todo

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

1// HelloWorld.js
2
3//State variables
4const [walletAddress, setWallet] = useState("")
5const [status, setStatus] = useState("")
6const [message, setMessage] = useState("No connection to the network.")
7const [newMessage, setNewMessage] = useState("")

Esto es lo que representa cada una de las variables:

  • walletAddress: 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 el dapp
  • mensaje - 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 lo que hacen a continuación:

1// HelloWorld.js
2
3//called only once
4useEffect(async () => {
5 //TODO: implement
6}, [])
7
8function addSmartContractListener() {
9 //TODO: implement
10}
11
12function addWalletListener() {
13 //TODO: implement
14}
15
16const connectWalletPressed = async () => {
17 //TODO: implement
18}
19
20const onUpdatePressed = async () => {
21 //TODO: implement
22}
Mostrar todo
  • useEffect(opens in a new tab)- este es un gancho de React que se llama después de renderizar su componente. Debido a que tiene una matriz vacía [] prop pasada a ella (ver línea 4), solo se llamará en el primer renderizado del componente. Aquí cargaremos el mensaje actual almacenado en nuestro contrato inteligente, llamaremos a nuestros oyentes de contrato inteligente y billetera, y actualizaremos nuestra interfaz de usuario para reflejar si una billetera ya está conectada.
  • addSmartContractListener- esta función configura un oyente que vigilará el evento UpdatedMessages de nuestro contrato de 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 cartera MetaMask del usuario, como cuando el usuario desconecta su cartera o cambia de dirección.
  • connectWalletPressed- esta función se llamará para conectar la cartera MetaMask del usuario a nuestra dapp.
  • onUpdatePressed - esta función se llamará 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.

1// HelloWorld.js
2
3//the UI of our component
4return (
5 <div id="container">
6 <img id="logo" src={alchemylogo}></img>
7 <button id="walletButton" onClick={connectWalletPressed}>
8 {walletAddress.length > 0 ? (
9 "Connected: " +
10 String(walletAddress).substring(0, 6) +
11 "..." +
12 String(walletAddress).substring(38)
13 ) : (
14 <span>Connect Wallet</span>
15 )}
16 </button>
17
18 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>
19 <p>{message}</p>
20
21 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>
22
23 <div>
24 <input
25 type="text"
26 placeholder="Update the message in your smart contract."
27 onChange={(e) => setNewMessage(e.target.value)}
28 value={newMessage}
29 />
30 <p id="status">{status}</p>
31
32 <button id="publishButton" onClick={onUpdatePressed}>
33 Update
34 </button>
35 </div>
36 </div>
37)
Mostrar todo

Si escaneas este código con cuidado, te darás cuenta de 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 del usuario walletAddress en el botón con el ID "walletButton;", de lo contrario, simplemente dice "Connect Wallet".
  • 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, utilizamos 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.

Por último, abordemos dónde se ha añadido este componente HelloWorld.js.

Si va al archivo App.js, que es el componente principal de 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, echemos un vistazo a un archivo más proporcionado para usted, el archivo interact.js.

El archivo interact.js

Debido a que queremos prescribir 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 nuestro 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!

Vaya a la carpeta util en su directorio src, y se dará cuenta de que hemos incluido un archivo llamado interact.js que contendrá todas nuestras funciones y variables de interacción de contratos inteligentes y cartera.

1// interact.js
2
3//export const helloWorldContract;
4
5export const loadCurrentMessage = async () => {}
6
7export const connectWallet = async () => {}
8
9const getCurrentWalletConnected = async () => {}
10
11export const updateMessage = async (message) => {}
Mostrar todo

Notará en la parte superior del archivo que hemos comentado el objeto helloWorldContract. Más adelante en este tutorial, dejaremos de comentar 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. Hará una llamada read al contrato inteligente de 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 comprobará 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. Hará una llamada write al contrato inteligente de Hello World, por lo que la cartera de MetaMask del usuario tendrá que firmar una transacción de Ethereum para actualizar el mensaje.

Ahora que entendemos con qué estamos trabajando, ¡vamos a averiguar cómo leer de nuestro contrato inteligente!

Paso 3: Lea de su contrato inteligente

Para leer de su contrato inteligente, tendrá que configurar con éxito:

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

Parecen demasiados pasos, ¡pero no te preocupes! ¡Te indicaremos como realizar cada uno de ellos paso a paso! :)

Establecer una conexión API con la cadena Ethereum

Recuerdas cómo en el apartado 2 de este tutorial, utilizamos nuestra llave de Alchemy Web3 para leer desde nuestro contrato inteligente(opens in a new tab)? También necesitaras una llave de Alchemy Web3 en tu dapp para leer de la cadena de bloques.

Si aún no lo tienes, instala primero Alchemy(opens in a new tab) ingresando al directorio de raíz de starter-files y ejecutando el siguiente código en tu terminal:

1npm install @alch/alchemy-web3

Alchemy Web3(opens in a new tab) está construido sobre Web3(opens in a new tab), de esta manera proporciona metodos mejorados de la API y otros beneficios importantes para que tu vida como desarrollador de Web3 sea mucho más fácil. Se diseñó para requerir una configuración mínima, por lo que puede comenzar a usarla en su aplicación de inmediato.

Luego, instala la librería dotenv(opens in a new tab) en tu directorio de proyectos, así tendremos un lugar seguro para almancenar nuestra clave de la API una vez que lo obtengamos.

1npm install dotenv --save

Para nuestra dapp, utilizaremos nuestra clave para la API de Websockets en lugar de nuestra clave para la API HTTP, ya que nos permitirá configurar un listener que detectará cuando el mensaje guardado en el contrato inteligente cambie.

Una vez que tengas la clave de la API, crea un archivo .env en tu directorio de raíz y agregale tu url de Alchemy Websockets. Acontinuación, tu archivo .env debería verse así:

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

¡Estamos listos para poner en marcha nuestra Alchemy Web3 endpoint en nuestra dapp! Volvamos de nuevo a nuestro interact.js, que se encuentra anidada dentro de nuestro archivo util y agrega el siguiente código al comienzo del archivo:

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8//export const helloWorldContract;

Primero importamos la clave Alchemy de nuestro archivo .env y luego pasamos nuestro alchemyKey a createAlchemyWeb3 para establecer nuestra Alchemy Web3 endpoint.

Una vez listo el endpoint, ¡es momento de cargar nuestro Contrato inteligente!

Carga de tu Contrato inteligente Hello World

Para cargar tu Contrato inteligente Hello World, necesitaras la dirección del contrato y ABI, puedes encontrar ambos en Etherscan si has completado previamente la parte 3 de este tutorial.

Cómo obtener tu contrato ABI de Etherscan

Si omitiste la parte 3 de este tutorial, puedes utilizar el contrato Hello World con dirección 0x6f3f635A9762B47954229Ea479b4541eAF402A6A(opens in a new tab). Puedes encontrar su ABI aquí(opens in a new tab).

Se necesita un contrato ABI para especificar qué función utilizará un contrato y para asegurar que la función devolverá datos en el formato esperado. Una vez que hayamos copiado nuestro contrato ABI, lo guardaremos como un archivo JSON que se llamará contract-abi.json en tu directorio src.

Tu contrato -abi.json deberá ser guardado en tu carpeta src.

Con la dirección de nuestro contrato ABI y AlchemyWeb3 endpoint, podemos utilizar el contract method(opens in a new tab) para cargar una instancia de nuestro Contrato inteligente. Importa tu contrato ABI dentro del archivo interact.js y agrega la direción de tu contratro.

1// interact.js
2
3const contractABI = require("../contract-abi.json")
4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"

Ahora podemos suprimir comentario de nuestra variable helloWorldContract, y cargar el contrato inteligente utilizando nuestro AlchemyWeb3 endpoint:

1// interact.js
2export const helloWorldContract = new web3.eth.Contract(
3 contractABI,
4 contractAddress
5)

A modo de repaso, las primeras 12 lineas de tu interact.js ahora deberían verse así:

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8const contractABI = require("../contract-abi.json")
9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
10
11export const helloWorldContract = new web3.eth.Contract(
12 contractABI,
13 contractAddress
14)
Mostrar todo

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

Implementación de loadCurrentMessage en tu archivo interact.js

Esta función es muy sencilla. Haremos una simple llamada async Web3 para así leer de nuestro contrato. Nuestra función devolverá el mensaje almacenado en el contrato inteligente:

Actualiza el loadCurrentMessage en tu archivo interact.js de la siguiente manera:

1// interact.js
2
3export const loadCurrentMessage = async () => {
4 const message = await helloWorldContract.methods.message().call()
5 return message
6}

Dado que queremos mostrar este contrato inteligente en nuestra UI, actualizemos la función useEffect en nuestro componente HelloWorld.js de la siguiente manera:

1// HelloWorld.js
2
3//called only once
4useEffect(async () => {
5 const message = await loadCurrentMessage()
6 setMessage(message)
7}, [])

Recuerda, solo queremos que nuestro loadCurrentMessage sea llamado una sola vez durante la primera renderización del componente. Pronto implementaremos addSmartContractListener para que actualice automáticamente la UI cada vez que cambie el mensaje en el contrato inteligente.

Antes de profundizar sobre nuestro listener, ¡revisemos lo que tenemos hasta ahora! Guarda tus archivos HelloWorld.js y interact.js y luego ve a http://localhost:3000/(opens in a new tab)

Notaras que el mensaje actual ya no dice "Sin conexión a la red". En su lugar reflejará el mensaje almacenado en el contrato inteligente. ¡Fantástico!

Ahora, tu UI debería reflejar el mensaje almacenado en el contrato inteligente

Hablando del listener...

Implementa addSmartContractListener

Si haces memoria, en el archivo HelloWorld.sol que redactamos en la parte 1 de esta serie de tutoriales(opens in a new tab), recordaras que existe un evento de contrato inteligente que se llama UpdatedMessages y que se emite una vez que la función update es utilizada (ver lineas 9 y 27):

1// HelloWorld.sol
2
3// Specifies the version of Solidity, using semantic versioning.
4// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
5pragma solidity ^0.7.3;
6
7// Defines a contract named `HelloWorld`.
8// Un contrato es una colección de funciones y datos (su estado). Una vez desplegado, un contrato reside en una dirección específica en la blockchain de Ethereum. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 //Emitted when update function is called
12 //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.
13 event UpdatedMessages(string oldStr, string newStr);
14
15 // Declares a state variable `message` of type `string`.
16 // Las variables de estado son variables cuyos valores se almacenan permanentemente en el almacenamiento del contrato. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value.
17 string public message;
18
19 // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation.
20 // Los constructores se utilizan para inicializar los datos del contrato. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) {
22
23 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).
24 message = initMessage;
25 }
26
27 // A public function that accepts a string argument and updates the `message` storage variable.
28 function update(string memory newMessage) public {
29 string memory oldMsg = message;
30 message = newMessage;
31 emit UpdatedMessages(oldMsg, newMessage);
32 }
33}
Mostrar todo

Los eventos del contrato inteligente son una forma de comunicar que ha pasado algo (es decir, hubo un event) en la blockchain que repercutió en tu aplicación front-end. Podría tratarse de un 'listening' para eventos específicos y , al mismo tiempo, sobre las medidas de acción a implementar cuando ocurren.

La función addSmartContractListener prestará atención a nuestro evento de contrato inteligente Hello World UpdatedMessages, y actualizará nuestro UI para que muestre el nuevo mensaje.

Modifica addSmartContractListener de la siguiente manera:

1// HelloWorld.js
2
3function addSmartContractListener() {
4 helloWorldContract.events.UpdatedMessages({}, (error, data) => {
5 if (error) {
6 setStatus("😥 " + error.message)
7 } else {
8 setMessage(data.returnValues[1])
9 setNewMessage("")
10 setStatus("🎉 Your message has been updated!")
11 }
12 })
13}
Mostrar todo

Desmenucemos lo que pasa cuando el listener detecta un evento:

  • Si ocurre un error al momento de emitir el evento, este se verá reflejado en la UI a través de nuestra variable de estado status.
  • Caso contrario, utilizaremos el objeto devuelto data. data.returnValues es un arreglo indexado a cero donde el primer elemento almancena el mensaje anterior, mientras que el segundo elemento almacena el mensaje actualizado. En conjunto, en un evento eficaz colocaremos nuestro string message al mensaje actualizado, resetearemos el string newMessage, y actualizaremos nuestra variable de estado status para que refleje que un nuevo mensaje se ha publicado en nuestro contrato inteligente.

Por último, ejecutemos nuestro listener en nuestra función useEffect para que se inicialize en la primera devolución del componente HelloWorld.js. Entonces, tu función useEffect debería verse así:

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7}, [])

Ahora que podemos leer de nuestro contrato inteligente, también ¡sería genial descifrar cómo podemos escribirlo! Sin embargo, para escribir en nuestra dapp, primero debemos tener una wallet de Ethereum conectada a la misma.

Por lo tanto, la próxima cuestión que abordaremos será crear nuestra wallet de Ethereum (MetaMask) para luego conectarla con nuestra dapp.

Paso 4: Crea tu wallet de Ethereum

Para poder escribir en la chain de Ethereum, los usuarios deben firmar las transacciones utilizando la clave privada de su wallet virtual. Para este tutorial, utilizaremos MetaMask(opens in a new tab), una billetera virtual en el browser que se utiliza para administrar la dirección de tu cuenta de Ethereum, ya que hace que la firma de esta transacción sea bastante simple para el usuario final.

Si quiere más información sobre cómo funcionan las transacciones en Ethereum, eche un vistazo a esta página de Ethereum Foundation.

Descargar MetaMask

Puede descargar y crear una cuenta Metamask gratis aquí(opens in a new tab). Cuando estes creando una cuenta, o si ya tienes una, asegurate de cambiar de “Goerli Test Network” en la parte superior (para que no se trate de dinero real).

Añada ether a partir de un grifo

Necesitaremos algunos Eth falsos para poder firmar una transacción en la blockchain de Ethereum. Para obtener Eth puedes ir a FaucETH(opens in a new tab) e ingresar la direcciíon de tu cuenta Goerli, haz click en “Request funds”, luego selecciona “Ethereum Testnet Goerli” y por último, haz click en el botón “Request funds” nuevamente. Debería ver el Eth en su cuenta de MetaMask poco después.

Revisa tu saldo

Para verificar que nuestro saldo esté ahí, realicemos una solicitud eth_getBalance(opens in a new tab) usando la herramienta de compositor de Alchemy(opens in a new tab). Esto devolverá la cantidad de Eth en nuestra billetera. Después de introducir la dirección de su cuenta de Metamask y hacer clic en «Send Request» (Enviar Solicitud), debería ver una respuesta como esta:

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

NOTA: Este resultado esta en wei, no en eth. Wei se usa como la denominación más pequeña de Ether. La conversión de wei a eth es: 1 eth = 10¹⁸ wei. Entonces si convertimos 0xde0b6b3a7640000 a decimal, obtenemos 1*10¹⁸, que equivale a 1 eth.

¡Fiu! Nuestro dinero de prueba está ahí sin problemas. 🤑

Paso 5: Conectate a tu UI en MetaMask

Ahora que nuestra billetera de MetaMask está configurada, vamos a conectar nuestra dapp a ella.

Función connectWallet

En nuestro archivo interact.js, implementaremos la función connectWallet, el cual podremos ejecutar en nuestro componente HelloWorld.js posteriormente.

Procedamos a modificar connectWallet como se muestra a continuación:

1// interact.js
2
3export const connectWallet = async () => {
4 if (window.ethereum) {
5 try {
6 const addressArray = await window.ethereum.request({
7 method: "eth_requestAccounts",
8 })
9 const obj = {
10 status: "👆🏽 Write a message in the text-field above.",
11 address: addressArray[0],
12 }
13 return obj
14 } catch (err) {
15 return {
16 address: "",
17 status: "😥 " + err.message,
18 }
19 }
20 } else {
21 return {
22 address: "",
23 status: (
24 <span>
25 <p>
26 {" "}
27 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>
28 You must install MetaMask, a virtual Ethereum wallet, in your
29 browser.
30 </a>
31 </p>
32 </span>
33 ),
34 }
35 }
36}
Mostrar todo

Entonces ¿Qué hace exactamente este extenso código?

Bueno, en primer lugar, verifica que window.ethereum este habilitado en tu 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 es aprobado, puede leer información de la blockchain a la que está conectado el usuario y sugerir que este firme mensajes y transacciones. Revise 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 resulta en la devolución de un objeto JSON, donde el address devuelto es una cadena vacía y el objeto JSX status muestra que el usuario debe instalar MetaMask.

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

Al utilizar un try/catch loop, intentaremos conectarnos a MetaMask ejecutando

window.ethereum.request({ method: "eth_requestAccounts" });. La invocación de esta función abrirá MetaMask en el navegador, donde se le solicitará al usuario conectar su billetera a su dapp.

  • Si el usuario decide conectarse, method: "eth_requestAccounts" devolverá un arreglo que contiene todas las direcciones de cuenta del usuario que se conectaron a la dapp. De igual manera, nuestra función connectWallet devolverá un objeto JSON que contine la primera address de este arreglo (ver la línea 9) y un mensaje de status que solicita al usuario escribir un mensaje al contrato inteligente.
  • Si el usuario rechaza la conexión, el objeto JSON tendrá una cadena vacía para la address devuelta y un mensaje de status donde se refleje que el usuario rechazó la conexión.

Una vez escrita la función connectWallet, el siguiente paso es ejecutarlo en nuestro componente HelloWorld.js.

Agrega la función connectWallet a tu componente de UI HelloWorld.js

Dirígitete a la función onnectWalletPressed en HelloWorld.js, y actualizalo de la siguiente manera:

1// HelloWorld.js
2
3const connectWalletPressed = async () => {
4 const walletResponse = await connectWallet()
5 setStatus(walletResponse.status)
6 setWallet(walletResponse.address)
7}

¿Notas como gran parte de nuestra funcionalidad se abstrae de nuestro componente HelloWorld.js del archivo interact.js? ¡Esto es así para cumplir con el paradigma M-V-C!

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

Guardemos ambos archivos (HelloWorld.js and interact.js) y probemos nuestra UI.

Abre tu navegador con el link http://localhost:3000/(opens in a new tab), y presiona el botón "Conectar Wallet" en el margen superior derecho de la página.

Si tiene MetaMask instalado, se le debería solicitar conectar su billetera a su dapp. Acepte la invitación para establecer la conexión.

Deberías poder visualizar que, ahora, el botón de wallet muestra que tu dirección está conectada. ¡Fantástico!

A continuación, pruebe actualizar la página... esto es extraño. Nuestro botón de billetera nos está solicitando conectar MetaMask, aunque ya está conectado...

¡No tengas miedo! Podemos solucionarlo facilmente, implementando getCurrentWalletConnected (¿entiendes?). Esto verificará si ya existe una dirección conectada a nuestra dapp y, en consecuencia, actualizará nuestra UI.

Función getCurrentWalletConnected

Actualiza tu función getCurrentWalletConnected en el archivo interact.js como se muestra a continuación:

1// interact.js
2
3export const getCurrentWalletConnected = async () => {
4 if (window.ethereum) {
5 try {
6 const addressArray = await window.ethereum.request({
7 method: "eth_accounts",
8 })
9 if (addressArray.length > 0) {
10 return {
11 address: addressArray[0],
12 status: "👆🏽 Write a message in the text-field above.",
13 }
14 } else {
15 return {
16 address: "",
17 status: "🦊 Connect to MetaMask using the top right button.",
18 }
19 }
20 } catch (err) {
21 return {
22 address: "",
23 status: "😥 " + err.message,
24 }
25 }
26 } else {
27 return {
28 address: "",
29 status: (
30 <span>
31 <p>
32 {" "}
33 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>
34 You must install MetaMask, a virtual Ethereum wallet, in your
35 browser.
36 </a>
37 </p>
38 </span>
39 ),
40 }
41 }
42}
Mostrar todo

Este código es bastante similar a la función connectWallet que escribimos en el paso anterior.

La principal diferencia es que, en vez de llamar al método eth_requestAccount, que abre MetaMask para que el usuario conecte su billetera, aquí llamamos al método eth_accounts, que simplemente devuelve un arreglo que contiene las direcciones de MetaMask que se encuentran conectadas a nuestra dapp.

Para ver esta función, lo podemos ejecutar en nuestra función useEffect de nuestro componente HelloWorld.js:

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11}, [])
Mostrar todo

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

Una vez que agregues este código, refrescaremos la ventana de nuestro navegador.

¡Magnífico! 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 la página.

Implementa addWalletListener

El último paso en la configuración de la billetera de dapp es implementar el oyente de billetera para que nuestra interfaz se actualice cuando el estado de la billetera cambie, por ejemplo, cuando el usuario se desconecte o cambie de cuenta.

En tu archivo HelloWorld.js, modifica tu función addWalletListener como se ve a continuación:

1// HelloWorld.js
2
3function addWalletListener() {
4 if (window.ethereum) {
5 window.ethereum.on("accountsChanged", (accounts) => {
6 if (accounts.length > 0) {
7 setWallet(accounts[0])
8 setStatus("👆🏽 Write a message in the text-field above.")
9 } else {
10 setWallet("")
11 setStatus("🦊 Connect to MetaMask using the top right button.")
12 }
13 })
14 } else {
15 setStatus(
16 <p>
17 {" "}
18 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>
19 You must install MetaMask, a virtual Ethereum wallet, in your browser.
20 </a>
21 </p>
22 )
23 }
24}
Mostrar todo

En este punto, apuesto a que ni siquiera necesitas de nuestra ayuda para entender lo que ocurre. Pero, para ser más exhaustivos, desglosémoslo rápidamente:

  • Primero, nuestra función verifica si window.ethereum está habilitado (esto es si MetaMask está instalado).
    • Si no lo está, simplemente establecemos nuestra variable de estado status a una cadena JSX que solicite al usuario instalar MetaMask.
    • Si está habilitado, configuramos el oyente window.ethereum.on("accountsChanged") en la línea 3, que escucha cambios de estado en la billetera de MetaMask, incluyendo cuando el usuario conecte una cuenta adicional a la dapp, cambie de cuenta o desconecte una cuenta. Si hay por lo menos una cuenta conectada, la variable de estado walletAddress es actualizada como la primera cuenta en el arreglo accounts devuelto por el oyente. De lo contrario, walletAddress se establece como cadena vacía.

Por último, debemos ejecutarlo en nuestra función useEffect:

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11
12 addWalletListener()
13}, [])
Mostrar todo

¡Y eso es todo! Hemos terminado de programar toda la funcionalidad de nuestra wallet exitosamente! Nuestra última tarea es actualizar el mensaje almacenado en nuestro contrato inteligente.

Paso 6: Implementa la función updateMessage

¡Hemos legado final! En el updateMessage de tu archivo interact.js, haremos lo siguiente:

  1. Asegurate de que el mensaje que deseas publicar en nuestro contrato inteligente sea válido
  2. Firmamos la transacción utilizando MetaMask
  3. Ejecuta esta función desde nuestro componente frontend HelloWorld.js

No nos llevará mucho ¡terminemos esta dapp!

Manejo de errores de entrada

Tiene sentido tener algún chequeo de error en el input al comienzo de la función.

Queremos que nuestra función retorne rápido si, no existe una extensión de MetaMask instalada, no existe una wallet conectada (i.e. the address es un string vacío), o message también es un string vacío. Agreguemos el siguiente chequeo de error a updateMessage:

1// interact.js
2
3export const updateMessage = async (address, message) => {
4 if (!window.ethereum || address === null) {
5 return {
6 status:
7 "💡 Connect your MetaMask wallet to update the message on the blockchain.",
8 }
9 }
10
11 if (message.trim() === "") {
12 return {
13 status: "❌ Your message cannot be an empty string.",
14 }
15 }
16}
Mostrar todo

Ahora que cuenta con un chegueo de error adecuado ¡ha llegado el momento de firmar la transacción con MetaMask!

Firma de nuestra transacción

Si ya estás cómodo con las transacciones tradicionales web3 de Ethereum, el código que escribiremos a continuación te resultará bastante familiar. Debajo de tu código de chequeo de error, agrega lo siguiente a pdateMessage:

1// interact.js
2
3//set up transaction parameters
4const transactionParameters = {
5 to: contractAddress, // Required except during contract publications.
6 from: address, // must match user's active address.
7 data: helloWorldContract.methods.update(message).encodeABI(),
8}
9
10//sign the transaction
11try {
12 const txHash = await window.ethereum.request({
13 method: "eth_sendTransaction",
14 params: [transactionParameters],
15 })
16 return {
17 status: (
18 <span>
19{" "}
20 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
21 View the status of your transaction on Etherscan!
22 </a>
23 <br />
24 ℹ️ Once the transaction is verified by the network, the message will be
25 updated automatically.
26 </span>
27 ),
28 }
29} catch (error) {
30 return {
31 status: "😥 " + error.message,
32 }
33}
Mostrar todo

Veamos qué es lo que ocurre. Primero, establezcamos los parámetros para nuestra transacción:

  • to especifica la dirección del receptor (nuestro contrato inteligente)
  • from muestra quién firma la transacción, la variable address muestra lo que pasamos a la función
  • data contiene la ejecución del método update de nuestro contrato inteligente Hello World, que recibe la variable de tipo cadena message como una entrada

Luego, realizamos una ejecución en espera, window.ethereum.request, donde le pedimos a MetaMask que firme la transacción. Ten en cuenta que, en las lineas 11 y 12, especificamos nuestro método ETH eth_sendTransaction y le pasamos transactionParameters.

En este punto, MetaMask se abrirá en el navegador y solicitará al usuario firmar o rechazar la transacción.

  • Si la transacción tiene éxito, la función devolverá un objeto de JSON donde el string JSX status le pedirá al usuario que revise Etherscan para obtener más información sobre su transacción.
  • Si la transacción falla, la función devolverá un objeto JSON donde el string status transmitirá el mensaje de error.

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

1// interact.js
2
3export const updateMessage = async (address, message) => {
4 //input error handling
5 if (!window.ethereum || address === null) {
6 return {
7 status:
8 "💡 Connect your MetaMask wallet to update the message on the blockchain.",
9 }
10 }
11
12 if (message.trim() === "") {
13 return {
14 status: "❌ Your message cannot be an empty string.",
15 }
16 }
17
18 //set up transaction parameters
19 const transactionParameters = {
20 to: contractAddress, // Required except during contract publications.
21 from: address, // must match user's active address.
22 data: helloWorldContract.methods.update(message).encodeABI(),
23 }
24
25 //sign the transaction
26 try {
27 const txHash = await window.ethereum.request({
28 method: "eth_sendTransaction",
29 params: [transactionParameters],
30 })
31 return {
32 status: (
33 <span>
34{" "}
35 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
36 View the status of your transaction on Etherscan!
37 </a>
38 <br />
39 ℹ️ Once the transaction is verified by the network, the message will
40 be updated automatically.
41 </span>
42 ),
43 }
44 } catch (error) {
45 return {
46 status: "😥 " + error.message,
47 }
48 }
49}
Mostrar todo

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

Conecta updateMessage al frontend HelloWorld.js

Nuestra función onUpdatePressed deberá realizar una ejecución en espera a la función importada updateMessage y modificar la variable de estado status para que muestre si funcionó o falló nuestra transacción:

1// HelloWorld.js
2
3const onUpdatePressed = async () => {
4 const { status } = await updateMessage(walletAddress, newMessage)
5 setStatus(status)
6}

Es simple y claro. Y adivina qué... ¡TU DAPP ESTÁ TERMINADA!

¡Adelante, ve a probar el botón Update!

Crea tu propia dapp personalizada

¡Bravo! ¡Has llegado al final del tutorial! A modo de repaso, esto es lo que aprendiste:

  • como conectar una wallet de MetaMask a tu proyecto dapp
  • Leer los datos de su contrato inteligente utilizando la API Alchemy Web3(opens in a new tab)
  • Firmar transacciones de Ethereum usando MetaMask

Ya estás preparado para aplicar los conocimientos que adquiriste en este tutorial, y así, crear tu propio proyecto dapp personalizado! De más está decir que, ante cualquier duda, puedes pedirnos ayuda en AlchemyDiscord(opens in a new tab). 🧙‍♂️

Cuando finalice este tutorial, cuéntenos cómo fue su experiencia o comparta algún comentario etiquetándonos en Twitter @alchemyplatform(opens in a new tab).

¿Le ha resultado útil este tutorial?