En esta publicación, desarrollaré una aplicación descentralizada para operar con un un token ERC-20 acuñable.
El propósito es, de esta manera, comprender el proceso de principio a fin de escribir un contrato inteligente en Solidity y desarrollar un front end específico para interactuar con este contrato.
Nota: esta aplicación no consiste de código listo para producción. Su implemementación es meramente para propósitos de aprendizaje.
En este artículo
Configuración
En esta guía se utiliza npm 7 workspaces para crear un mono repositorio con el fin de simplificar nuestro entorno de desarrollo.
Cree una nueva carpeta y navegue a ella. Además, cree un nuevo proyecto Node con npm init.
Luego edite el package.json:
package.json
Ahora cree las carpetas de los workspaces:
Instale las dependencias del entorno Hardhat:
Ahora instale las dependencias de react-app:
Añada unos scripts que serán útiles:
hardhat-env/package.json
react-app/package.json
package.json
Por último, cree un archivo de configuración de Hardhat e importe los plugins.
En caso de utilizar Metamask necesitaremos o bien asignar en hardhat.config.ts1337 a la chainId de nuestra red local, o bien configurar la chainId de localhost en Metamask a 31337:
hardhat-env/hardhat.config.ts
Redacción del contrato
El contrato inteligente está escrito en Solidity y cumple con las especificaciones del estándar EIP-20.
hardhat-env/contracts/MintableERC20.sol
Cuando se despliegue el contrato, se inicializarán sus variables de estado, dos de las cuales se pasan como argumentos a la función constructora, a saber, nombre y símbolo.
La lógica del contrato es bastante simple.
Solo la dirección del deployer podrá acuñar tokens.
Para transferir tokens, se requiere que el sender posea al menos la misma cantidad de tokens que pretende transferir.
En caso de que una dirección solicite que otra dirección le transfiera una cantidad de tokens, deberá ser previamente autorizada, es decir, antes de que se se llame a transferFrom, la direccion del spender deberá ser aprobada para que pueda recibir del owner la cantidad de tokens solicitada
Tanto las transacciones como las aprobaciones emitirán un Event. Más tarde, utilizaré el evento de transacción para realizar un seguimiento del saldo de la cuenta actual en la aplicación descentralizada.
Testeado del contrato
Estas operaciones pueden entenderse mejor testeando el contrato.
En esta guía plugins de Hardhat de integración con Waffle y Ethers.js se utilizan para escribir las pruebas requeridas con Mocha junto con Chai.
Estos tests están estructurados en tres llamadas describe:
hardhat-env/test/mintableerc20.spec.ts
Ahora ejecute el script test-hardhat:
Como podemos ver, hardhat test compilará el contrato de Solidity y generará tipos de antemano. Esto creará las carpetas artifacts, cache, y typechain-types.
Estamos interesados principalmente en MintableERC20.json en /hardhat-env/artifacts/contracts/MintableERC20.sol, y MintableERC20.ts en /hardhat-env/typechain-types, dado que contienen el ABI y los tipos que han sido generados, respectivamente.
Los utilizaremos en nuestra aplicación de React para interactuar con el contrato inteligente usando Ethers.js.
Para importar los archivos que necesitemos en el workspacehardhat-env a react-app podríamos simplemente utilizar declaraciones import.
Sin embargo, en el caso de que quisiéramos mantener una copia de estos archivos en react-app o simplemente para evitar conflictos de nombres con paquetes instalados en nuestro workspace y los nombres de otros workspaces, podríamos ejecutar un simple script para leer los archivos y escribirlos en react-app para importarlos desde ahí:
hardhat-env/scripts/shareFiles.ts
Para tener tokens a nuestra disposición, tan solo necesitaremos una tarea de Hardhat para acuñar y enviar tokens a una dirección específica.
Esto se logra fácilmente agregando una tarea en la carpeta /hardhat/tasks.
Para simplificar el proceso, se establece una cantidad preestablecida de 100 TFT.
hardhat-env/tasks/mint.ts
Por último, solo es necesario importar este archivo en la configuración de hardhat:
hardhat-env/hardhat.config.ts
Desplegado del contrato
En Hardhat podemos desplegar un contrato en una red localhost. Para iniciar un nodo local en esta aplicación ejecute npm run node, y para desplegar el contrato npm run compile-share-deploy.
El último comando garantizará, además, que nuestra aplicación React tenga la dirección del contrato, los tipos y el ABI actuales después de compilar el archivo de Solidity y desplegarlo en la localhost.
Front-end
Nuestro front end es una aplicación React.js simple escrita en TypeScript que consta de tres componentes principales para mostrar el saldo, realizar transacciones y verificar el historial de transacciones entrantes y salientes.
Para que nuestra DApp funcione, es necesario que se instale una cartera criptográfica en el navegador e inyecte la propiedad ethereum a window.
Si se establece una cuenta diferente o una cadena diferente en nuestro proveedor de Ethereum, la página se volverá a cargar.
react-app/src/utils/hooks/index.ts
Una vez que iniciemos la aplicación de React, se nos solicita que habilitemos Ethereum, lo cual resultará en la obtención de una dirección de cuenta y en la creación de dos contratos.
react-app/src/utils/helpers.ts
Mediante Ethers.js creo dos contratos, uno para realizar operaciones de solo lectura y otro para realizar transacciones.
Para crear ambos contratos, es necesario pasar una dirección de contrato, una interfaz de contrato (o ABI) y un signer o un provider.
Para el contrato de solo lectura se pasa un JsonRpcProvider, y un signer para el contrato que se usará para firmar operaciones para cambiar el estado de la cadena de bloques.
Guardo la dirección de la cuenta y ambos contratos en ContractContext que nos los proporcionará en cualquiera de los componentes que son descendientes de ContractProvider.
Una vez el contexto cuente con estos datos los componentes Balance, TransferFrom y Panels se renderizarán.
Muestran el saldo de TFTs (Test Fungible Token, nuestro token) de la dirección de la cuenta del contexto en el contrato, un formulario para transferir TFTs y un panel con transacciones entrantes y salientes, respectivamente.
Establezco listeners dentro de useEffects en hooks personalizados para mantener los tres componentes principales actualizados con el estado en la cadena de bloques (también TransferFrom, ya que los datos de entrada se validan mediante la librería Formik).
Para mantener el saldo sincronizado con el estado de nuestra cuenta en el mapping_balance de MintableERC20.sol, usamos el evento Transfer en nuestro contrato para crear dos filtros
filterReceived: este filtro mantendrá todas las transacciones a nuestra cuenta (incluyendo el operaciones de acuñado).
filterSent: este filtro mantendrá todas las transacciones desde nuestra cuenta.
Luego configuramos listeners a estos eventos para actualizar el estado del saldo en nuestra aplicación.
react-app/src/utils/hooks/index.ts
Se realiza una operación similar en useTransactions, sin embargo, aquí los listeners establecerán el estado de las transferencias a un array de Events.
react-app/src/utils/hooks/index.ts
Mapeamos estos arrays en Panels para mostrar las transacciones.
A continuación podemos ver el mapeo de nuestras transacciones salientes. La dirección del receptor corrsponde a event.args[1] y la cantidad de TFT enviada corresponde a event.args[2].
react-app/src/components/Panels.tsx
Ahora que toda la lógica está implementada, tan solo ejecute npm run start-app.
Los contratos inteligentes de Olive Oil Trust se implementan con el objeto de adoptar un...
¿Preparado para #buidl?
¿Está interesado en Web3 o en las sinergias entre la tecnología blockchain, la inteligencia artificial y el conocimiento cero?. Entonces, no dude en contactarme por e-mail o en mi perfil de LinkedIn. También me puede encontrar en GitHub.