Ethereum para desarrolladores web. Parte 2

En este post, crearemos una DApp completa desde cero. No vamos a profundizar sobre los pasos involucrados, este post le ayudará a identificar todos los componentes involucrados en la construcción de una aplicación descentralizada. En los próximos posts, nos centraremos en cada una de las diferentes partes, pero siempre puede consultar este post
para comprender cómo encaja cada sección en el panorama general.

nuestra dapp

Crearemos un contador global implementado como DApp. Los usuarios deben ser
capaz de ver el valor del contador y aumentarlo enviándole una transacción. Aunque esta aplicación no tiene usos prácticos, será de ayuda revisar cada uno de los componentes de una DApp.

nuestros requerimientos

Nuestra aplicación tendrá un solo contador como su estado y permitirá que cualquier usuario de Ethereum pueda aumentarlo mediante una transacción. Al mismo tiempo, cualquier usuario que acceda a la DApp debe poder ver actualizaciones en tiempo real del valor del contador. La aplicación no gestionará ningún ETH y no tendrá ningún tipo de control de acceso. Siempre que un usuario pueda acceder a la DApp, debería poder interactuar libremente con ella.

el smart contract

Los smart contract de Ethereum son pequeños programas implementados en la cadena de bloques Ethereum. Cada contrato tiene su propio código y estado interno. En esta aplicación, almacenaremos el contravalor en el estado del contrato. El contrato también proporcionará un getter para cualquier cliente para consultar fácilmente su valor, así como un método para aumentarlo enviando una transacción
al contrato:

pragma solidity ^0.5.0;
 contract Counter {
    uint256 public value;
    function increase() public {
       value = value + 1;
   }
 }

El smart contract también debe proporcionar un evento para que los clientes escuchen en tiempo real para actualizar su estado. Cada transacción en Ethereum puede activar opcionalmente uno o más eventos, y un cliente puede suscribirse a un conjunto particular de ellos, como un medio para ser notificado de cualquier cambio en un contrato.

pragma solidity ^0.5.0;
  contract Counter {
     uint256 public value;
     event Increased(uint256 newValue);
     function increase() public {
       value = value + 1;
       emit Increased(value);
    }
}

Ahora, esta implementación muestra todas las formas básicas en las que un contrato inteligente proporciona una interfaz para que un cliente interactúe con él:
1. Un getter para consultar el estado interno del contrato Está autogenerado por el uso de la palabra clave public al declarar el campo. Consultar un contrato es rápido y no implica enviar una transacción, por lo que no requiere gas o incluso tener una cuenta de Ethereum.
2. Una función para modificar el estado del contrato, increase. Estas funciones requieren que se envíe una transacción, que requiere que ETH sea ejecutado. Como tal, requieren tener una cuenta con fondos.
3. El evento Increased , para escuchar actualizaciones del estado del contrato. Algun cliente puede solicitar suscribirse a un conjunto de eventos de un contrato, para recibir notificaciones de cualquier cambio. Entraremos en detalles sobre los contratos inteligentes en el próximo post. Por ahora, con estos conceptos construiremos nuestra DApp.

la arquitectura

Construiremos una DApp tradicional para este ejemplo. La aplicación estará respaldada por un contrato inteligente en la red Ethereum, que actuará como una base de datos distribuida para los suarios de la Dapp. El front-end se configurará como una aplicación JavaScript normal del lado del cliente. Para el pegamento entre el front-end de JavaScript y la red Ethereum, confiaremos en un navegador web habilitado para web3. Este es un tipo de navegador que permite al usuario conectarse no solo a Internet sino también a la red Ethereum a través de un nodo de su propia elección. También administra las claves privadas del usuario y la firma de transacciones. El navegador habilitado para web3 más popular es MetaMask, que no es un navegador per se pero es un complemento, que proporciona todas las funciones necesarias. MetaMask realiza un seguimiento de las claves privadas del usuario, permite conexiones a una lista de nodos predefinidos o a uno personalizado, y solicita al usuario que acepte cualquier transacción que la página actual esté intentando enviar en su nombre.
Desde la perspectiva del desarrollador, MetaMask inyecta un proveedor de conexión global a un nodo Ethereum e intercepta todas las transacciones para firmarlas con sus propias claves.

creando el entorno

Antes de comenzar a construir la DApp, también configuraremos nuestro entorno de desarrollo como las herramientas necesarias para interactuar y probar nuestra DApp.

herramientas de desarrollo

La configuración básica de nuestro entorno de desarrollo será la necesaria para construir
una aplicación javascript simple del lado del cliente. Asegúrese de tener nodejs y npm
instalado en su máquina de desarrollo para comenzar.
Arrancaremos nuestra aplicación confiando en el paquete create-react-app.
Este es un paquete, proporcionado por el equipo de desarrollo de Facebook, que inicializa una nueva aplicación web React preconfigurada. Esto nos permitirá ahorrar tiempo de configuración y centrarse en la creación de DApp.

npm init react-app counter-app

En cuanto a las bibliotecas específicas de Ethereum, si bien hay muchos frameworks de desarrollo disponibles, nos ceñiremos al mínimo para este ejemplo. La única biblioteca de JavaScript relacionada con Ethereum que usaremos es web3.js. Esta es una biblioteca cuyo desarrollo está respaldado por la fundación Ethereum y es considerado por muchos como la biblioteca canónica de facto.

npm install web3@1.2.0

Con respecto a la cadena de herramientas de construcción de Ethereum, nos centraremos nuevamente en el conjunto mínimo de herramientas necesarias. Primero instalaremos el compilador Solidity, para compilar el smart contract. Asegúrese de instalar la versión 0.5.0 o superior.

$ solc ––version
 solc, the solidity compiler commandline interface
 Version: …

Luego, para configurar pruebas automatizadas para nuestra aplicación, instalaremos Ganache. Ganache es un proceso que expone una interfaz similar a un nodo Ethereum y simula una cadena de bloques Ethereum completa por sí misma. Es particularmente útil en desarrollo de entornos o para ejecutar pruebas unitarias.

npm install -g ganache-cli

De forma predeterminada, Ganache extrae inmediatamente un nuevo bloque por cada transacción enviada, eliminando el tiempo de espera hasta que se confirme una transacción. Esto facilita su uso como back-end mientras se codifica, pero tenga en cuenta que un entorno Ganache será drásticamente diferente, especialmente en términos de experiencia de usuario, a uno real.

navegador web3

Ahora configuraremos un navegador habilitado para web3, usando MetaMask. Recuerda que hay otros navegadores compatibles con web3, pero en el momento de escribir este artículo, MetaMask es la mforma más popular de interactuar con DApps.
Comience instalando el complemento MetaMask para su navegador: es compatible con Chrome, Firefox, Opera y Brave. Después de la instalación, MetaMask le pedirá que cree una contraseña para cifrar sus cuentas y le presentará la frase secreta de respaldo. Asegurate que escriba esta frase: en caso de que pierda su billetera MetaMask, puede regenerar usando esta frase. De lo contrario, todos los fondos contenidos en él se perderán irremediablemente.

Tenga mucho cuidado al instalar metamask. La mayoría del software relacionado con la administración de claves de usuario o transacciones es propensa a estar sujeta a ataques de phishing. Asegúrese siempre de estar accediendo al sitio oficial de metamask al realizar la descarga.

El siguiente paso es depositar fondos en su cuenta para interactuar con contratos inteligentes. Para los ejemplos a lo largo de estos posts, usaremos la red de prueba Rinkeby (o testnet). Ethereum tiene varias redes de prueba (Ropsten, Rinkeby, Kovan y Goerli), cada uno con sus propias características e identificado por un ID numérico único:
Ropsten (id 3) es la única red de prueba basada en pruebas de trabajo, lo que la convierte en el más similar a mainnet, pero tampoco es muy confiable. Como no se está haciendo mucho trabajo real en la red, los tiempos de bloqueo son impredecibles, y la red es altamente susceptible a ataques.
Rinkeby (id 4) es una red de prueba basada en prueba de autoridad, lo que significa
que hay un conjunto de nodos confiables que tienen la autoridad para agregar nuevos bloques a la cadena de bloques. Esto lo hace mucho más estable y confiable que Ropsten. Sin embargo, debido a las limitaciones del algoritmo de consenso utilizado, solo los clientes de Geth pueden conectarse a Rinkeby.
Kovan (id 42) es similar a Rinkeby, en que es una prueba de autoridad- basada en testnet, pero su algoritmo de consenso es compatible con el cliente Parity en lugar de Geth.
• Goerli (id 6) es la red de prueba más reciente configurada. Utiliza prueba de
autoridad también, con la ventaja de que es compatible con ambos Clientes de Geth y Parity.
Hay varios faucets en línea para obtener testnet ETH para probar. Utilice uno de para solicitar fondos para una de las cuentas que acaba de crear en MetaMask.

¿Cómo encontraste el proceso de incorporación en metamask? Si crees que fue
complicado o un poco largo, ahora piensa en tus usuarios. todos los usuarios primerizos de ethereum que necesitan pasar por un proceso similar, con la carga adicional de tener que firmar en un interchage para comprar Ether real en la mainnet para interactuar con su aplicación, que a menudo requiere un proceso KYC completo. Esta es la razón por la que la incorporación de usuarios es un desafío en ethereum. Existen técnicas para mitigar este problema, como no requerir a sus usuarios para tener una cuenta de ethereum hasta que sea absolutamente necesario, o flujos de incorporación alternativos basados en metatransacciones. Más adelante veremos todo esto.

construyendo nuestra dapp

Comenzaremos a construir desde la plantilla create-react-app. Asegúrate de haber ejecutado todos los pasos en la sección “Creando el entorno“; debe tener una aplicación JavaScript simple con un puñado de archivos en la carpeta src, construido alrededor de index.js. Para verificar que todo está funcionando sin problemas, ejecute npm run start y abra su navegador en localhost:3000. Usted debería ver la pantalla inicial predeterminada del paquete create-react-app, incluida un logo react.

compilando el smart contract

Nuestra DApp estará respaldada por un único contrato inteligente, Counter. Crea una carpeta llamada contracts en la raíz de su proyecto y agregue un archivo llamado Counter.sol:

// contracts/Counter.sol
pragma solidity ^0.5.0;
   contract Counter {
     uint256 public value;
     event Increased(uint256 newValue);
     function increase() public {
       value = value + 1;
       emit Increased(value);
   }
}

Vamos a profundizar más en los contratos inteligentes en el próximo post, pero por ahora puede comenzar a identificar las piezas importantes del contrato:
• El estado del contrato, valor, definido como un entero sin signo de 256 bits, el tamaño predeterminado en Solidity
• El getter de acceso value, generado por el uso de public en la declaración del campo
• La función increase para incrementar el valor mediante una transacción.
• El evento increase utilizado para señalar cuando se ha producido una modificación de valor.
Puede probar que el contrato está bien ejecutando el compilador de solidity en él:

$ solc contracts/Counter.sol
 Compiler run successful, no output requested.

Necesitamos especificar el formato en el que queremos generar la compilación. Estamos
interesado especialmente en la especificación de la interfaz pública del contrato, o ABI
(Interfaz binaria de la aplicación), que es cómo se comunicará nuestra aplicación javascript con el contrato. También queremos el código binario, por lo que podemos implementar el contrato en la red si es necesario. Podemos solicitar al compilador Solidity que genere estos dos en un solo archivo JSON que luego podemos usar:

Los flags necesarios para generar esta información desde el compilador pueden cambiar dependiendo de la versión con la que esté trabajando. el código anterior funciona para Solidity 0.5.1

solc --pretty-json --combined-json=abi,bin --overwrite \
 -o ./build/contracts contracts/Counter.sol

El comando anterior generará un archivo /build/contracts/combined.json con todos la
salida de compilación. Échale un vistazo y pronto lo usaremos para interactuar con nuestro contrato.

conectando a la red via web3

Como se mencionó anteriormente, usaremos web3.js para conectarnos a la red Ethereum. Esto requiere un proveedor web3, que es un pequeño objeto que sabe a qué nodo conectarse para realizar llamadas a contratos inteligentes y enviar transacciones a la red. En otra palabras, como su nombre lo indica, el proveedor proporciona una conexión a un nodo Ethereum y, desde él, a toda la red. Dependiendo de la biblioteca con la que esté trabajando, el proveedor a veces se combina con el firmante. El firmante es otro componente que tiene la responsabilidad de firmar transacciones con las claves del usuario, en el caso de que las claves no sean administradas por un nodo local. Este es el caso de la mayoría de las Dapps, ya que su usuario promedio no tendrá un nodo en ejecución, pero dependen de uno público. Debido a esto, el proveedor web3 inyectado por MetaMask actúa tanto como proveedor y como firmante. Revisaremos estas diferencias en profundidad más adelante en los siguientes posts.
Se puede acceder cómodamente al proveedor web3 inyectado por MetaMask desde el código a través de Web3.givenProvider. Puede verificar esta propiedad para saber si MetaMask está habilitado en el navegador de sus usuarios y para crear un nuevo objeto web3 si está disponible. Podemos mantener esta lógica en un archivo network.js en nuestra aplicación:

// src/eth/network.js
import Web3 from 'web3';
let web3;
export function getWeb3() {
if (!web3) {
   web3 = new Web3(Web3.givenProvider);
  }
  return web3;
}

El objeto web3 creado tiene una gran cantidad de métodos disponibles, la mayoría de ellos
en el espacio de nombres web3.eth. Por ejemplo, podemos consultar la lista de cuentas del usuario. (codigo abajo) y recuperar el predeterminado en uso, que es el primero de la lista.

// src/eth/network.js
export async function getAccount() {
  const web3 = getWeb3();
  const accounts = await web3.eth.getAccounts();
  return accounts[0];
}

Sin embargo, este método no funcionará para navegadores que se ejecuten en modo privacy. El modo privacy restringe el acceso a las cuentas de usuario hasta que el usuario aprueba la aplicación para recuperar las cuentas mantenidas en MetaMask. Para desbloquear esto, debemos trabajar con un objeto ethereum y habilítarlo:

export async function getAccount() {
const accounts = await window.ethereum.enable();
return accounts[0];
}

La llamada asincrónica a ethereum.enable volverá una vez que el usuario haya concedido su aprobación en MetaMask. Tenga en cuenta que MetaMask recordará la aprobación del usuario, por lo que se les solicita que respondan solo la primera vez:

Los usuarios deben aceptar la conexion de la Dapp en Metamask

Ahora que tenemos un objeto web3 configurado, así como el acceso a las cuentas del usuario, los usaremos para construir nuestra interfaz para el contrato inteligente de Counter implementado en la Red Ethereum.

la interfaz del contrato

Para interactuar con nuestro contrato desde la aplicación, necesitamos tres cosas:
• Una conexión a la red Ethereum donde se encuentra nuestro contrato desplegado.
• La dirección del contrato en la red
• La especificación de las funciones públicas del contrato, también conocida como
ABI (interfaz binaria de aplicación)
El primer elemento se trata en la sección anterior y está encapsulado por el objeto web3
que aprovisionamos. En cuanto al segundo elemento, usaremos una instancia ya implementada
en la red Rinkeby en la siguiente dirección:

0x1D2561D18dD2fc204CcC8831026d28375065ed53

Recuerde que todo en blockchain es público e indeleble, por lo que una vez se implementa el contrato, se pone a disposición del público para que todos los usuarios interactúen y
no se puede borrar. Esto significa que podemos usar libremente esta instancia para probar la DApp.

En cuanto a la ABI, haremos uso de la salida generada por el compilador anteriormente.
Copie el archivo json de salida en un archivo Artifacts.json en una nueva carpeta contract en su aplicación src. Ahora podemos analizarlo para obtener el ABI y crear una instancia del nuevo contrato web3.

// src/contracts/Counter.js
import Artifacts from './Artifacts.json';
export default function Counter(web3, address, options = {}) {
 const name = "contracts/Counter.sol:Counter";
 const artifact = Artifacts.contracts[name];
 const abi = JSON.parse(artifact.abi);
 return new web3.eth.Contract(abi, address, options);
}

La abstracción del contrato web3 es un objeto que actúa como fachada del contrato de Ethereum. Expone métodos javascript para todas sus funciones públicas, que obtienen la
traduccion de las llamadas y transacciones a la red bajo el capó. Lo estaremos usando
en la siguiente sección para interactuar realmente con el contrato. Ahora que tenemos nuestra función de fábrica que puede crear un nuevo contrato de contador dada una dirección, la usaremos para recuperar el contrato implementado:

// src/contracts/Counter.js
import { getWeb3, getAccount } from '../eth/network.js';
export async function getDeployed() {
 const web3 = getWeb3();
 const from = await getAccount();
 const addr = "0x1D2561D18dD2fc204CcC8831026d28375065ed53";
 return Counter(web3, addr, { from });
}

Ahora que tenemos los medios para interactuar con el contrato implementado, podemos construir el componente visual React para nuestra interfaz de usuario.

interactuando con nuestro smart contract

Ahora construiremos progresivamente nuestro componente visual Counter para permitir a los usuarios de nuestra DApp interactuar con él. Comenzaremos recuperando el valor actual del contador, luego proporcionaremos un medio para enviar una transacción para modificar su estado y luego suscribirse a actualizaciones en tiempo real.

escribiendo nuestro componente

Comencemos creando un archivo components/counter.js . Este sera un componente React vacio por ahora.

// src/components/Counter.js
import React, { Component } from 'react';
class Counter extends Component {
 render() {
  return (
    <div>Counter be here</div>
   );
  }
}

Este componente recibirá de la App.js el componente raiz de la instancia del contrato Counter. Será responsabilidad de la aplicación recuperar dicha instancia e inyectar el componente visual Contador una vez que esté listo. Modifiquemos el archivo src/App.js que estaba autogenerado por create-react-app para cargar la instancia del contrato.

import './App.css';
import React, { Component } from 'react';
import Counter from './components/Counter';
import { getDeployed } from './contracts/Counter';
class App extends Component {
 state = { counter: null };
 async componentDidMount() {
  const counter = await getDeployed();
  this.setState({ counter });
}
render() {
 const { counter } = this.state;
 return (
   <div className="App">
     { counter && <Counter contract={counter} /> }
   </div>
  );
}
}
export default App;

Confiamos en el evento React componentDidMount para cargar la instancia del contrato Counter y almacenarlo en el estado del componente. Solo cuando esta instancia está disponible, renderiza el componente visual Counter.

A estas alturas, es posible que se haya dado cuenta de que nos falta la gestión de errores en esta Dapp. Por ejemplo, no estamos manejando el caso en el que el usuario no tienen metamask, o si la dirección del contrato es incorrecta, o si la conexión se pierde. Esta es una decisión deliberada, ya que el objetivo es centrarse en la funcionalidad y proporcionar una descripción general rápida de lo que constituye un Dapp. En los próximos posts, a medida que profundizamos en cada tema, también cubriremos todo lo que pueda potencialmente ir mal.

En este punto, debería poder ejecutar su aplicación a través de npm start y probar que todo se está renderizando correctamente. Asegúrese de tener MetaMask instalado, desbloqueado, y conectado a la red Rinkeby. Ahora que tenemos todas nuestras aplicaciones conectadas, es hora de enfocarnos en el componente visual Counter
en sí.

consultando el estado del contrato

Comenzaremos mostrando el valor del contrato Counter en nuestro componente.
Dado que no cambiaremos la instancia de Counter durante la vida de nuestro
componente, podemos simplemente recuperar ese valor cuando el componente React está montado.

// src/components/Counter.js
async componentDidMount() {
 const counter = this.props.contract;
 const initialValue = await counter.methods.value().call();
 this.setState({ value: initialValue });
}

Tenga en cuenta la llamada a la instancia del contrato de contador para recuperar el valor inicial. La API Web3 para la llamada puede parecer incómoda, pero tiene una razón fundamental:
• La propiedad de métodos otorga acceso a todos los métodos públicos definidos
en el ABI del contrato. No se establecen en la propia instancia del contrato para evitar choques con otros métodos específicos del objeto contrato web3.
• La llamada value () no consulta la red, sino simplemente construye una llamada al método. Si la función requería algún parámetro, tendría que ser suministrado aquí.
• La invocación de call () finalmente emite la consulta a la cadena de bloques. Revisaremos en el próximo post la diferencia entre consultar un método y emitir una transacción, pero por ahora, todo lo que necesitamos saber es que esa llamada () se usa cuando queremos recuperar datos de un contrato.

Una vez que hemos establecido el valor inicial en el estado del componente, finalmente podemos renderizarlo a nuestros usuarios:

render() {
 const { value } = this.state;
 if (!value) return "Loading";
 return (
  <div>
   <div>Counter value: { value.toString() }</div>
  </div>
 );
}

En este punto, debería poder volver a cargar su aplicación en su navegador y ver
el valor de la instancia de Contador en la red Rinkeby.

Puede verificar el valor mostrado con el informado por un explorador de blockchain, como etherscan. Etherscan es un explorador de blockchain, una web que proporciona una interfaz visual para direcciones y transacciones, y está disponible para mainnet y la mayoría de las redes de prueba. Busque la dirección del contrato, y en la pestaña Leer Contrato, podrá comprobar el valor del contador.

Nuestro siguiente paso será permitirle al usuario incrementar el valor del contador, emitiendo una transacción.

enviando una transaccion

Comencemos escribiendo una función para enviar una transacción para llamar a la función increase en el contrato contract.

increaseCounter() {
  const counter = this.props.contract;
  return counter.methods.increase().send();
}

Después de la sección anterior, la llamada para enviar la transacción debería ser más familiar. Tenga en cuenta que en este caso estamos usando send() en lugar de call(). Esto es porque necesitamos enviar una transacción para afectar el estado del contrato, en lugar de solo consultar datos de la red. Ahora podemos conectar este método a un botón en nuestra interfaz y probarlo.

render() {
const { value } = this.state;
if (!value) return "Loading";
return (
 <div>
  <div>Counter value: { value.toString() }</div>
   <button onClick={() => this.increaseCounter()}>
   Increase counter
  </button>
 </div>
);
}


Si intenta esto, será recibido con el cuadro de diálogo de Metamask para confirmar una transacción,que debería parecerse más o menos a esto:

Confirmacion de Metamask

Si actualiza la página unos segundos después de confirmar la transacción, debe ver el nuevo valor del contador. Sin embargo, para evitar tener que volver a cargar la página para actualizar value, volveremos a consultar el valor después de que se confirme la transacción.

increaseCounter() {
const counter = this.props.contract;
 return counter.methods.increase().send()
  .on('receipt', async () => {
  const value = await counter.methods.value().call();
 this.setState({ value });
});
}

El método send() devuelve un emisor de eventos, que nos permite escuchar diferentes
eventos durante la vida de una transacción. Por ahora, solo nos interesa el evento cuando
la transacción se extrae, es decir, se incluye en un bloque de la cadena. Este evento se conoce como receipt, ya que corresponde a cuando el objeto receipt de la transacción está disponible. Si queremos, también podríamos verificar cuándo la transacción se envió realmente a un nodo, o cuándo ha alcanzado un número razonable de confirmaciones.
Ahora, incluso si el código actualiza el valor del contador, debemos dejar que el usuario sepa lo que está pasando. La confirmación de la transacción tarda varios segundos, que
es definitivamente más de lo que normalmente requiere una aplicación web 2.0. Realizaremos un seguimiento del estado de la transacción para mostrar un simple “Awaiting transaction” para que el usuario sepa lo que está pasando y deshabilite el botón mientras tanto. También nos ocuparemos del caso en el que la transacción falle,
por lo que no inhabilitamos el botón de forma permanente. Por supuesto, en una DApp real, es posible que desee proporcionar mejores señales visuales.

increaseCounter() {
 const counter = this.props.contract;
 this.setState({ increasing: true, error: null });
 return counter.methods.increase().send()
 .on('receipt', async () => {
  const value = await counter.methods.value().call();
  this.setState({ value, increasing: false });
 })
 .on('error', (error) => {
  this.setState({ error, increasing: false })
 });
}
render() {
 const { value, increasing, error } = this.state;
 if (!value) return "Loading";
return (
 <div className="Counter">
  <div>Counter value: { value.toString() }</div>
  <button
    disabled={!!increasing}
    onClick={() => this.increaseCounter()}>
      Increase counter
  </button>
  <div>{ increasing && "Awaiting transaction" }</div>
  <div>{ error && error.message }</div>
 </div>
 );
}

Como alternativa a esperar a que se extraiga la transacción, podríamos también actualizar el valor de manera optimista. Una actualización optimista es una técnica que se utiliza en muchas aplicaciones asincrónicas más allá de Dapps, que consiste en asumir que una transacción realizada por el usuario tendrá éxito y se actualizará inmediatamente el valor del lado del cliente. De esta forma, el usuario percibe que la aplicación reacciona casi instantáneamente y puede seguir interactuando con ella y tiene comentarios inmediatos sobre el resultado de sus acciones.

Si bien esta solución es lo suficientemente buena para que un solo usuario interactúe con un contrato, se queda corta cuando hay varios usuarios involucrados. Puede probar esto abriendo el sitio web desde dos ventanas de navegador diferentes: los cambios realizados en una ventana no afectar a la otra, salvo que se vuelva a consultar el contrato recargando la página. Para resolver este problema, nos basaremos en el último concepto que introdujimos en la interfaz del contrato: Los eventos.

monitorizando actualizaciones via eventos

La interfaz pública de un contrato no se compone únicamente de funciones públicas. Un contrato puede también emiten eventos personalizados sobre ciertas transacciones. Nuestro contrato Contract emite un evento que se llama Increased cada vez que se llama a la función increase, e incluye un nuevo valor del contador como argumento.
Para monitorear todas las instancias de este evento, nos suscribiremos cuando el componente monta y actualiza el estado del componente en consecuencia:

async componentDidMount() {
 const counter = this.props.contract;
 const initialValue = await counter.methods.value().call();
 this.setState({ value: initialValue });
 counter.events.Increased()
  .on('data', (event) => {
   const value = event.returnValues.newValue;
   this.setState({ value });
  });
}

Tenga en cuenta que aquí nos referimos a la propiedad counter.events en lugar de counter.methods como lo hicimos antes. Aquí, el emisor de eventos dispara un evento de datos cada vez se encuentra un nuevo evento e incluye los argumentos del evento.
Además, al actualizar el estado del componente en cada evento, ya no necesitamos consultar el estado del contrato siempre que se confirma una transacción. El controlador de eventos en IncrementCounter se puede simplificar a lo siguiente:

.on('receipt', async () => {
  this.setState({ increasing: false });
})

Con esta nueva configuración, ahora puede recibir actualizaciones en tiempo real sobre un contrato, independientemente de dónde se originó el cambio de estado. Vuelva a intentar abrir dos ventanas del navegador y aumentamos el contador de uno de ellos, y verá cómo se refleja el cambio en ambos una vez confirmada la transacción.

desplegando la dapp

Como habrá notado, nuestra aplicación de muestra se ejecuta exclusivamente en el lado del cliente. Toda la lógica tiene lugar en el navegador, y la cadena de bloques se utiliza como back-end para realizar tareas simples como cálculos y persistencia en un estado compartido entre todos los usuarios, actuando como una capa de consenso en el estado del contador. Esto hace que la implementación sea sencilla, ya que la DApp necesita
solo ser alojada como un sitio estático.

conclusion

En este post, hemos pasado por el proceso de desarrollo de una DApp simple, proporcionando a nuestros usuarios una interfaz básica basada en web para un solo contrato inteligente. Nosotros hemos explorado cómo leer el estado de un contrato y le hemos enviado transacciones y hemos monitoreado eventos para actualizaciones en tiempo real. Hemos creado toda nuestra aplicación basándonos en solo dos bibliotecas: web3js interactuando con la red Ethereum y React como framework de presentación. Dado el ritmo al que cambian las bibliotecas y los marcos tanto en javascript como en
Ecosistemas Ethereum, el objetivo ha sido (y será a lo largo de estos posts) utilizar tan pocas dependencias como sea posible y céntrese en los conceptos detrás de la construcción de una DApp en lugar de sobre las API específicas de las herramientas del momento. Por supuesto, esto no significa que no debe confiar en tales herramientas al crear su propia DApp, ya que pueden ser de gran utilidad y ayuda. Asegúrese de revisar OpenZeppelin, Truffle, Buidler, Etherlime, Embark, Clevis, y todo lo que esté disponible para cuando lea estos posts. Con todo, este post debería haber ayudado a dar una descripción general de todo el proceso de desarrollo de los componentes de una DApp. Hemos pasado por alto los despliegues de los propios contratos, así como la gestión de cuentas y ETH en general. No hemos cubierto muchos casos extremos o situaciones de error que surgen cuando se trata con una cadena de bloques. Sin embargo, a lo largo de estos posts, profundizaremos en todos estos temas, además de otros nuevos y más avanzados, y revisaremos cada paso de la construcción de una DApp en ejemplos más interesantes.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s