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 su 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). Once deployed, a contract resides at a specific address on the Ethereum blockchain. 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ñada su 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 = "your-metamask-private-key"

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 tiene que preocuparse por eso, ¡esperemos que todo lo demás esté 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) a 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:

npx hardhat run 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 Goerli 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 en la dirección To figuraráContract 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 tiene varias aplicaciones de Alchemy, asegúrese de filtrar por aplicación y seleccione Hello World.

Aquí verá un puñado de métodos JSON-RPC que Hardhat/Ethers hizo internamente 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: Interactuar con su 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 instaló 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

Actualizar su 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>"

Utilizar su contrato ABI

Nuestro contrato es la interfaz para interactuar con nuestro contrato inteligente. Hardhat genera automáticamente una ABI y la guarda en HelloWorld.json. Para usar la 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 impresa en la consola, vaya a su terminal y ejecute:

npx hardhat run scripts/interact.js

Crear una instancia de su 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 le dé 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).

Leer el mensaje de inicio

¿Recuerda cuando implementamos 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 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 acttivar la función message 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! No está mal, ¿verdad?

Para actualizar el mensaje, podemos activar directamente 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, activamos .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 activación .wait() no está incluida, es posible que el script no vea el valor message actualizado en el contrato.

Leer el nuevo mensaje

Debería poder repetir el paso anterior para leer el valor actualizado del message. ¡Dedique unos instantes y vea si puede hacer los cambios necesarios para imprimir ese nuevo valor!

A modo de ayuda visual, así es como debería ser su 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!

npx hardhat run 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 Updating the message... 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 la zona de espera 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: Publicar su contrato inteligente en Etherscan

Ya ha hecho la parte más dura para dar vida a su 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 tiene una cuenta de Etherscan, regístrese para obtenela(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 su página de perfil, debería 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. Copie la clave de la API en su portapapeles.

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

Después de añadirlo, su archivo .env debería tener este aspecto:

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ñadirlo, 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 añada 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

Verificar 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 ('Hello World!' ) debe ser el mismo valor de cadena utilizado durante el paso de despliegue en la parte 1.

Si todo va bien, aparecerá el siguiente mensaje en su 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! ¡Su código de contrato inteligente está en Etherescan!

¡Eche un vistazo a su 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!

Yuhuuuu ¡logrado, campeón! ¡Ahora cualquiera puede llamar o escribir a su contrato inteligente! ¡Estamos deseando ver lo que construye a continuación!

Parte 4: Integrar su contrato inteligente con el frontend

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

  • Conectar una cartera MetaMask a su 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 esta DApp, usaremos React(opens in a new tab) como nuestro marco de frontend; sin embargo, es importante tener en cuenta que no pasaremos demasiado tiempo desglosando sus fundamentos, ya que nos centraremos principalmente en llevar la funcionalidad Web3 a nuestro proyecto.

Como requisito previo, debe conocimientos de React a nivel principiante. De lo contrario, le 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: starter-files y completed.

  • starter-files: estaremos trabajando en este directorio, conectaremos la interfaz de usuario a su cartera 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 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.

Echar 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 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 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 activa después de representar su componente. Debido a que tiene una propiedad de de matriz vacía [] integrada (ver línea 4), solo se activará en la primera representación del componente. Aquí cargaremos el mensaje actual almacenado en nuestro contrato inteligente, activaremos nuestros oyentes de contrato inteligente y cartera, y actualizaremos nuestra interfaz de usuario para reflejar si una cartera ya está conectada.
  • addSmartContractListener: esta función configura un oyente que vigilará 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 cartera MetaMask del usuario, como cuando el usuario desconecta su cartera o cambia de dirección.
  • connectWalletPressed: esta función se activará 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 escanea este código con cuidado, observará dónde usamos nuestras diversas variables de estado en nuestra interfaz de usuario:

  • En las líneas 6-12, si la cartera 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 figura «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 el contenido del campo de texto.

Además de nuestras variables de estado, también verá que las funciones connectWalletPressed y onUpdatePressed se activan cuando se hace clic en los botones con las 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 aparece en la línea 7.

Por último, pero no por ello menos importante, echemos un vistazo a otro archivo que le proporcionamos, el archivo interact.js.

El archivo interact.js

Dado que queremos prescribir el paradigma M-V-C(opens in a new tab), deberemos tener un archivo por separado que contenga todas nuestras funciones para administrar la lógica, los datos y las reglas de nuestra DApp, para luego 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

Observará 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á leer read el 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 escritura 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 desde nuestro contrato inteligente!

Paso 3: Leer desde su contrato inteligente

Para leer desde 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 activar 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 se preocupe! ¡Le indicaremos cómo realizar cada uno de ellos paso a paso! :)

Establecer una conexión API con la cadena Ethereum

Si lo recuerda, 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 necesitara una clave de Alchemy Web3 en tu DApp para leer desde la cadena de bloques.

Si aún no la tienes, instálela primero Alchemy Web3(opens in a new tab) navegando al directorio raíz de su starter-files y ejecutando el siguiente código en su 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.

Seguidamente, instale la biblioteca dotenv(opens in a new tab) en su directorio de proyectos, así tendremos un lugar seguro donde almacenar nuestra clave de la API una vez que la 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 oyente que detectará cuando el mensaje guardado en el contrato inteligente cambie.

Una vez que tenga la clave de la API, cree un archivo .env en su directorio de raíz y añadele su URL de Alchemy Websockets. A continuación, su archivo .env debería tener este aspecto:

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

¡Estamos listos para poner en marcha nuestra terminal Alchemy Web3 en nuestra DApp! Volvamos de nuevo a nuestro interact.js, que se encuentra anidado dentro de nuestro archivo util y añada 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 terminal Alchemy Web3.

Una vez lista la terminal, ¡es momento de cargar nuestro contrato inteligente!

Cargar su contrato inteligente Hello World

Para cargar su contrato inteligente Hello World, necesitará la dirección del contrato y ABI, puede encontrar ambos en Etherscan si ha completado previamente la Parte 3 de este tutorial.

Cómo obtener su contrato ABI de Etherscan

Si omitió la Parte 3 de este tutorial, puede 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 su directorio src.

Deberá guardar su contrato -abi.json en su carpeta src.

Con la dirección de nuestro contrato ABI y la terminal AlchemyWeb3, podemos utilizar el contract method(opens in a new tab) para cargar una instancia de nuestro contrato inteligente. Importe su contrato ABI dentro del archivo interact.js y añada la direción de su contrato.

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

Ahora podemos suprimir comentarios de nuestra variable helloWorldContract, y cargar el contrato inteligente utilizando nuestra terminal AlchemyWeb3:

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

A modo de repaso, las primeras 12 lineas de su interact.js ahora deberían tener este aspecto:

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!

Implementar loadCurrentMessage en su archivo interact.js

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

Actualice el loadCurrentMessage en su 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}, [])

Recuerde, solo queremos que nuestro loadCurrentMessage se active una sola vez durante la primera representación del componente. Pronto implementaremos addSmartContractListener para que actualice automáticamente la IU cada vez que cambie el mensaje en el contrato inteligente.

Antes de profundizar sobre 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: «Sin conexión a la red». En su lugar reflejará el mensaje almacenado en el contrato inteligente. ¡Fantástico!

Ahora, su IU debería reflejar el mensaje almacenado en el contrato inteligente

Con respecto al oyente...

Implementar addSmartContractListener

Si hace memoria, en el archivo HelloWorld.sol que escribimos en la Parte 1 de esta serie de tutoriales(opens in a new tab), recordará que existía un evento de contrato inteligente llamado UpdatedMessages y que se emite una vez que se utiliza la función update (ver líneas 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). Once deployed, a contract resides at a specific address on the Ethereum blockchain. 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, que se produjo un evento) en la cadena de bloques que repercutió en su aplicación frontend. Podría tratarse de un «oyente» para eventos específicos y, al mismo tiempo, implementar las medidas de acción oportunas cuando ocurren.

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

Modifique 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 oyente detecta un evento:

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

Por último, ejecutaremos nuestro oyente en nuestra función useEffect para que se inicialice en la primera representación del componente HelloWorld.js. Entonces, su función useEffect debería tener el siguiente aspecto:

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

Ahora que podemos leer desde nuestro contrato inteligente, lo siguiente sería descifrar cómo podemos escribirlo. Sin embargo, para escribir en nuestra DApp, primero debemos tener una cartera de Ethereum conectada a la misma.

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

Paso 4: Crear su cartera de Ethereum

Antes de escribir nada en la cadena de Ethereum, los usuarios deben firmar las transacciones utilizando la clave privada de su cartera virtual. Para este tutorial, utilizaremos MetaMask(opens in a new tab), una cartera virtual en el navegador que se utiliza para administrar la dirección de su 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 esté creando una cuenta, o si ya tiene una, asegúrese 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 cadena de bloques de Ethereum. Para obtener Eth, puede ir a FaucETH(opens in a new tab) e introducir la dirección de su cuenta Goerli, haga clic en «Request funds», luego seleccionar «Ethereum Testnet Goerli» y, por último, hacer clic en el botón «Request funds» nuevamente. Debería ver el Eth en su cuenta de MetaMask poco después.

Revisar su 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, en la 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 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 información de la cadena de bloques 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 bucle de intentar/atrapar, intentaremos conectarnos a MetaMask ejecutando window.ethereum.request({ method: "eth_requestAccounts" });(opens in a new tab). 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á una matriz 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 ejecutarla en nuestro componente HelloWorld.js.

Añada la función connectWallet a su componente de IU HelloWorld.js

Navegue hasta la función onnectWalletPressed en HelloWorld.js, y actualícela 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}

¿Nota cómo 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 IU.

Abra su navegador con el enlace http://localhost:3000/(opens in a new tab), y pulse el botón «Connect 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ía poder visualizar que, ahora, el botón de la cartera muestra que su 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 tenga miedo! Podemos solucionarlo fácilmente, implementando getCurrentWalletConnected (¿vale?). Esto verificará si ya existe una dirección conectada a nuestra DApp y, en consecuencia, actualizará nuestra IU.

La función getCurrentWalletConnected

Actualice su 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 en acción, la podemos ejecutar en el 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 añada este código, refrescaremos la ventana de nuestro navegador.

¡Biennnnnn! 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.

Implementar 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 su archivo HelloWorld.js, modifique su 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

Llegados a este punto, apuesto a que ni siquiera necesita 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 cartera exitosamente! Nuestra última tarea es actualizar el mensaje almacenado en nuestro contrato inteligente.

Paso 6: Implementar la función updateMessage

Vale, y con esto ¡hemos llegado final! En el updateMessage de su archivo interact.js, haremos lo siguiente:

  1. Asegúrese de que el mensaje que desea publicar en nuestro contrato inteligente sea válido.
  2. Firmamos la transacción utilizando MetaMask.
  3. Ejecutamos esta función desde nuestro componente frontend HelloWorld.js.

No nos llevará mucho ¡terminemos esta DApp!

Manejo de errores de entrada

Obviamente, deberíamos hacer alguna comprobación de errores al principio de la función.

Queremos que nuestra función retorne rápido si, no existe una extensión de MetaMask instalada, no existe una cartera conectada (p. ej., la address es una cadena vacía), o message también es una cadena vacía. Añadamos el siguiente control de errores 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 control de errores adecuado ¡ha llegado el momento de firmar la transacción con MetaMask!

Firma de nuestra transacción

Si se siente cómodo con las transacciones tradicionales en Web3 de Ethereum, el código que escribiremos a continuación le resultará bastante familiar. Debajo de su código de control de errores, añada 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. Tenga en cuenta que, en las líneas 11 y 12, especificamos nuestro método ETH eth_sendTransaction y le pasamos nuestros 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 la cadena 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 la cadena status transmitirá el mensaje de error.

Entonces, nuestra función updateMessage debería tener el siguiente aspecto:

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.

Conectar 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 adivine qué... ¡SU DAPP ESTÁ TERMINADA!

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

Crear su propia DApp personalizada

¡Bravo! ¡Ha llegado al final del tutorial! A modo de repaso, esto es lo que hemos aprendido:

  • Cómo conectar una cartera de MetaMask a su proyecto de 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 ha adquirido en este tutorial, ¡y así, crear su propio proyecto de DApp personalizada! Está de más decir que, ante cualquier duda, siempre puede pedirnos ayuda en el canal Discord de Alchemy(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).

Última edición: @wackerow(opens in a new tab), 7 de mayo de 2024

¿Le ha resultado útil este tutorial?