Contrato inteligente de Hello World para principiantes: Fullstack
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.
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.
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-world2cd 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 contract4entry point: (index.js)5test command:6git repository:7keywords:8author:9license: (ISC)1011About to write to /Users/.../.../.../hello-world/package.json:1213{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 8882888 888 888 888 8883888 888 888 888 88848888888888 8888b. 888d888 .d88888 88888b. 8888b. 8888885888 888 "88b 888P" d88" 888 888 "88b "88b 8886888 888 .d888888 888 888 888 888 888 .d888888 8887888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888910👷 Welcome to Hardhat v2.0.11 👷1112What do you want to do? …13Create a sample project14❯ Create an empty hardhat.config.js15QuitMostrar 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 contracts2mkdir scripts
contratos/
es donde mantendremos nuestro archivo de código del contrato inteligente Hola, mundoscripts/
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
- Vaya a la carpeta
contratos
y cree un nuevo archivo llamadoHelloWorld.sol
- 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#pragma3pragma solidity >=0.7.3;45// 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.html7contract HelloWorld {89 //Emitted when update function is called10 //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);1213 // 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;1617 // 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#constructors19 constructor(string memory initMessage) {2021 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).22 message = initMessage;23 }2425 // 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.
- Siga estas instrucciones(opens in a new tab) para exportar su llave privada
- Abajo se le indica cómo obtener la API URL de HTTP Alchemy
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').HardhatUserConfig3 */45require("dotenv").config()6require("@nomiclabs/hardhat-ethers")78const { API_URL, PRIVATE_KEY } = process.env910module.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")34 // Start deployment, returning a promise that resolves to a contract object5 const hello_world = await HelloWorld.deploy("Hello World!")6 console.log("Contract deployed to address:", hello_world.address)7}89main()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.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const 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:
# .envAPI_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.js2const 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:
- Proveedor: un proveedor de nodos que le dé acceso de lectura y escritura a la cadena de bloques.
- Firmante: representa una cuenta de Ethereum que puede firmar transacciones.
- 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.js23// Provider4const alchemyProvider = new ethers.providers.AlchemyProvider(5 (network = "goerli"),6 API_KEY7)89// Signer10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1112// Contract13const helloWorldContract = new ethers.Contract(14 CONTRACT_ADDRESS,15 contract.abi,16 signer17)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.js23// ...45async 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.js23// ...45async function main() {6 const message = await helloWorldContract.message()7 console.log("The message is: " + message)89 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.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS67const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")89// provider - Alchemy10const alchemyProvider = new ethers.providers.AlchemyProvider(11 (network = "goerli"),12 API_KEY13)1415// signer - you16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1718// contract instance19const helloWorldContract = new ethers.Contract(20 CONTRACT_ADDRESS,21 contract.abi,22 signer23)2425async function main() {26 const message = await helloWorldContract.message()27 console.log("The message is: " + message)2829 console.log("Updating the message...")30 const tx = await helloWorldContract.update("this is the new message")31 await tx.wait()3233 const newMessage = await helloWorldContract.message()34 console.log("The new message is: " + newMessage)35}3637main()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.js23require("dotenv").config()4require("@nomiclabs/hardhat-ethers")5require("@nomiclabs/hardhat-etherscan")67const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env89module.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 Etherscan21 // 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 contract2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address3for verification on Etherscan. Waiting for verification result...456Successfully 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-filesnpm 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.js23import React from "react"4import { useEffect, useState } from "react"5import {6 helloWorldContract,7 connectWallet,8 updateMessage,9 loadCurrentMessage,10 getCurrentWalletConnected,11} from "./util/interact.js"1213import alchemylogo from "./alchemylogo.svg"Mostrar todo
A continuación, tenemos nuestras variables de estado que actualizaremos después de eventos específicos.
1// HelloWorld.js23//State variables4const [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 usuariostatus
: 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.js23//called only once4useEffect(async () => {5 //TODO: implement6}, [])78function addSmartContractListener() {9 //TODO: implement10}1112function addWalletListener() {13 //TODO: implement14}1516const connectWalletPressed = async () => {17 //TODO: implement18}1920const onUpdatePressed = async () => {21 //TODO: implement22}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 eventoUpdatedMessages
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.js23//the UI of our component4return (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>1718 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>19 <p>{message}</p>2021 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>2223 <div>24 <input25 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>3132 <button id="publishButton" onClick={onUpdatePressed}>33 Update34 </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 usuariowalletAddress
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.js23//export const helloWorldContract;45export const loadCurrentMessage = async () => {}67export const connectWallet = async () => {}89const getCurrentWalletConnected = async () => {}1011export 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.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78//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.js23const 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.js2export const helloWorldContract = new web3.eth.Contract(3 contractABI,4 contractAddress5)
A modo de repaso, las primeras 12 lineas de su interact.js
ahora deberían tener este aspecto:
1// interact.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78const contractABI = require("../contract-abi.json")9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"1011export const helloWorldContract = new web3.eth.Contract(12 contractABI,13 contractAddress14)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.js23export const loadCurrentMessage = async () => {4 const message = await helloWorldContract.methods.message().call()5 return message6}
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.js23//called only once4useEffect(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.sol23// Specifies the version of Solidity, using semantic versioning.4// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma5pragma solidity ^0.7.3;67// 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.html9contract HelloWorld {1011 //Emitted when update function is called12 //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);1415 // 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;1819 // 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#constructors21 constructor(string memory initMessage) {2223 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).24 message = initMessage;25 }2627 // 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.js23function 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 cadenamessage
en el mensaje actualizado, resetearemos la cadenanewMessage
, y actualizaremos nuestra variable de estadostatus
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.js23useEffect(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.js23export 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 obj14 } 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 your29 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ónconnectWallet
devolverá un objeto JSON que contine la primeraaddress
de este arreglo (ver la línea 9) y un mensaje destatus
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 destatus
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.js23const 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.js23export 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 your35 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.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 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.js23function 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 estadowalletAddress
es actualizada como la primera cuenta en el arregloaccounts
devuelto por el oyente. De lo contrario,walletAddress
se establece como cadena vacía.
- Si no lo está, simplemente establecemos nuestra variable de estado
Por último, debemos ejecutarlo en nuestra función useEffect
:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)1112 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:
- Asegúrese de que el mensaje que desea publicar en nuestro contrato inteligente sea válido.
- Firmamos la transacción utilizando MetaMask.
- 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.js23export 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 }1011 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.js23//set up transaction parameters4const 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}910//sign the transaction11try {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 be25 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 variableaddress
muestra lo que pasamos a la funcióndata
contiene la ejecución del métodoupdate
de nuestro contrato inteligente Hello World, que recibe la variable de tipo cadenamessage
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.js23export const updateMessage = async (address, message) => {4 //input error handling5 if (!window.ethereum || address === null) {6 return {7 status:8 "💡 Connect your MetaMask wallet to update the message on the blockchain.",9 }10 }1112 if (message.trim() === "") {13 return {14 status: "❌ Your message cannot be an empty string.",15 }16 }1718 //set up transaction parameters19 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 }2425 //sign the transaction26 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 will40 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.js23const 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