Presentando Olive Oil Trust: front end

Ver en GitHub

Esta publicación es parte de una serie de publicaciones sobre Olive Oil Trust:

Desarrollar una interfaz de usuario que simplifique nuestras interacciones con los contratos inteligentes de Olive Oil Trust es clave para aumentar la actividad en la cadena de suministro y mejorar la visibilidad de sus miembros.

Esta aplicación debe poder solicitar datos relevantes al subgrafo en cada escenario de caso de uso y proporcionarlos al usuario.

En esta publicación, presentaré una aplicación de Next.js que da soporte a los miembros y a los clientes de Olive Oil Trust y reduce la complejidad de todas las tareas a realizar.

En este artículo

Enfoque

La interfaz de usuario que presento en esta publicación es en esencia una herramienta para interactuar con los contratos de Olive Oil Trust en la cadena de bloques y para recopilar y representar datos del subgrafo.

Esta interfaz tiene dos secciones principales: management y explore.

Por un lado, la sección de management reúne todas las páginas que tienen por objeto interactuar con los contratos a través de los casos de uso que implementan los contratos, por ejemplo, crear un tipo de token, acuñar un lote de tokens, etc.

Dado que se necesita una cuenta para firmar transacciones en la cadena de bloques, todas las rutas en /management están restringidas a usuarios conectados a la aplicación.

Además, la mayoría de estas rutas se restringen a miembros de Olive Oil Trust que desempeñan un rol específico.

Por ejemplo, no tiene sentido que a una planta embotelladora se le dé la opción de certificar tokens, o a un certificador de acuñar tokens.

Por otro lado, la sección explore reúne todas las páginas que tienen por objeto mostrar información de las principales entidades de Olive Oil Trust (certificados, escrows, tokens y tipos de tokens) de todos los usuarios.

Esta sección no requiere conectarse a la aplicación; sin embargo, al conectarse, también se puede interactuar con los escrows /explorar/escrows (realizar un pago, revertir o cerrar un escrow) para facilitar el descubrimiento y la adquisición de nuevos tokens.

En la sección explore también hay rutas dinámicas para mostrar páginas únicas de cada uno de los tokens, tipos de tokens y certificados.

Por ejemplo, /explore/tokens mostrará una lista de tarjetas de tokens pero /explore/tokens/0x84641... mostrará los datos principales sobre el lote de tokens con el identificador "0x84641...".

La URL de una página de un token podría codificarse en un código QR e imprimirse en la etiqueta del producto final para que un cliente final pueda navegar hasta ella y verificar los datos del producto, incluido las trazas de ese producto hasta su origen.

He elegido Next.js para desarrollar esta aplicación porque permite utilizar diferentes estrategias de obtención de datos por página de manera sencilla, además de muchas otras cosas: enrutamiento anidado, optimizaciones integradas, etc.

Mi implementación se basa en una arquitectura hexagonal donde el estado global está fuera del núcleo y persiste globalmente usando Redux Toolkit.

La mayor parte de la implementación se divide en dos features independientes, management y explore, que representan las dos secciones de la página web.

Por lo tanto, cada uno de ellos tiene su propio núcleo, componentes de interfaz de usuario y fuentes de datos.

Finalmente, el núcleo es donde se define la lógica de negocio, es decir, entidades, repositorios, casos de uso y adaptadores.

Obtención de datos

Utilizar Next.js junto con TheGraph Client nos permite elegir entre diferentes alternativas a la hora de obtener datos del subgrafo.

TheGraph Client crea un artefacto de tiempo de ejecución para ejecutar consultas al subgrafo que podemos usar en una fuente de datos en el lado del cliente, sin embargo, también exporta un SDK que podemos utilizar para ejecutar nuestras consultas en el lado del servidor.

Por lo tanto, podemos combinar ambos para obtener datos con hooks en el lado del cliente, o renderizar previamente del lado del servidor, o actualizar o crear contenido en tiempo de ejecución con regeneración estática incremental.

Aunque, en todos los casos se podría usar solo uno de estos métodos para obtener datos, CSR, SSR e ISR se utilizan para tratar tres escenarios diferentes que requieren que los datos se obtengan de manera diferente con el objeto de optimizar la experiencia del usuario.

Todas las paginas en /explore (con la excepción de/explore/escrows) contienen tres versiones de los componentes para elegir, una para cada método de obtención de datos.

Por lo tanto, descomente el componente que mejor se adapte a su estrategia de obtención de datos.

Renderizado del lado del cliente

Páginas en management, así como la página/explorar/escrows, requieren renderizado del lado del cliente con un intervalo de sondeo con el objeto de seguir obteniendo datos periódicamente para que las nuevas transacciones se reflejen en la página sin realizar más solicitudes al servidor.

Por lo tanto, cuando un usuario navega a una de esas páginas, el servidor entregará la página almacenada en caché desde que se construyó la aplicación.

Una vez que el cliente recibe la página y ejecuta React.js, se llamará al hook para obtener los datos.

Por ejemplo, si un cliente final va a /management/my-tokens y se conecta a la aplicación, MyTokensPage.tsx renderizará el componente MyTokensByAccount que llamará al hook useTokensByAccount para obtener tokens por dirección de cuenta.

Podemos ver la implementación de ese hook a continuación:

next-app/src/features/management/ui/hooks/useTokensByAccount.tsx

La función callback para obtener datos en useTokensByAccount solo llama a un interactor, que es el caso de uso de obtener tokens por cuenta.

Podemos ver el diagrama de flujo del hook useTokensByAccount abajo:

Como podemos ver, los interactores llaman a un método de un repositorio para obtener una entidad, que luego se adapta a una interfaz genérica.

El repositorio se inyecta al interactor como una abstracción de una clase de fuente de datos, donde se implementa ese repositorio.

Veremos en interactores cómo se hace esta inyección de dependencias.

Renderizado del lado del servidor

Utilizo el renderizado del lado del servidor en las páginas en explore, excluyendo rutas dinámicas y /explore/escrows.

Se espera que estas páginas contengan todos los datos disponibles de una entidad en particular (tokens, certificados, etc.), por lo que es razonable mostrar solo los datos que existen en el momento de la solicitud.

Es decir, no hay necesidad de seguir obteniendo datos periódicamente, pero los datos deben obtenerse a demanda.

Dado que este proyecto utiliza Next.js 12, esta obtención de datos se realiza mediante la función getServerSideProps.

Next.js 13 introduce una manera sencilla de obtener datos (ya sea dinámicamente, estáticamente y en revalidación) que funciona en un directorio específico llamado app.

Podemos ver a continuación la utilización de getServerSideProps para la ruta /explore/tokens:

next-app/src/pages/explore/tokens.tsx

En getServerSideProps podemos usar la función de GraphClient getBuiltGraphSDK la cuál devuelve un objeto SDK que recopila métodos para ejecutar las consultas definidas en next-app/.graphclientrc.yaml.

Luego, se adaptan los datos, se forma un objeto de estado y se devuelve de modo que cuando un usuario solicita esta página, el servidor llamará a esta función, la página se generará sobre la marcha y se entregará al navegador.

A continuación podemos ver el diagrama de flujo de getServerSideProps:

Regeneración estática incremental

Las páginas dinámicas muestran datos que son inmutables y algunos otros que puede que se actualicen periódicamente (como los metadatos).

Por lo tanto, es razonable utilizar ISR para obtener datos en el momento de la compilación y volver a generar la página por solicitud como máximo una vez cada intervalo de revalidación en el lado del servidor.

Además, si hubiera mucha actividad dentro de la cadena de suministro, la regeneración estática incremental nos permitiría manejar de manera eficiente una cantidad de páginas dinámicas que aumentarían significativamente.

getStaticProps y getStaticPaths se deben exportar para habilitar ISR en una página dinámica en Next.js 12.

Podemos ver a continuación la implementación de estas dos funciones en el componente de la página del token:

next-app/src/pages/explore/tokens/[id].tsx

Como podemos ver, se invoca en getStaticProps el método SDK asociado a la consulta que queremos realizar.

Una vez que se devuelven los datos, se adaptan y se forma un estado de token y se devuelve junto con un número en revalidate.

Este es el número en segundos que le indicará a Next.js el intervalo de tiempo en el que se puede volver a generar una página como máximo una vez si hay una solicitud.

getStaticPaths, a su vez, devolverá todos los identificadores que se esperan y recurrirá a blocking si una identificación no está incluida en esta lista, lo que renderizaría la página del lado del servidor.

A continuación podemos ver el diagrama de flujo de estas dos funciones:

Estructura de archivos

El esquema de la estructura de archivos del workspace next-app se muestra a continuación:

Next.js espera una carpeta llamada pages, en el nivel raíz o en src/, que junto con app y features conforman las carpetas principales de nuestra aplicación.

App

La carpeta next-app/src/app contiene toda la lógica que no se trata de ninguna feature ni de ninguna página, sino de la aplicación en general.

Esta carpeta contiene la store de Redux, así como los estados globales, los estilos globales y los componentes de layout.

Uno de esos componentes es Header.tsx que contiene el botón de conexión y desconexión. Este botón llama a los métodos connectWallet y disconnectWallet al hacer clic.

connectWallet crea una solicitud para conectarse a la billetera del usuario y, en última instancia, establece datos sobre la cuenta, la identificación de la cadena y el contrato (si el usuario es miembro), y también almacena en un estado global variables sobre el estado de la conexión.

La aplicación utilizará estos datos para implementar restricciones de acceso y mensajes de estado en los componentes de pages que veremos en el siguiente apartado.

Pages

En Next.js la carpeta pages contiene componentes de React.js que se representarán en una ruta en función de su nombre de archivo.

Por ejemplo, el siguiente componente se renderizará cuando un usuario navegue a la ruta /management/my-tokens:

next-app/src/pages/management/my-tokens.tsx

Como podemos ver, este componente renderizará diferentes componentes dependiendo de si el usuario está logueado, y si lo está, si el usuario es miembro, y si el usuario es miembro, si es un certificador o no.

Esto se debe a que hay un acceso restringido a esta página, pero también a que no todos los usuarios registrados harán que la aplicación ejecute la misma consulta al subgrafo o muestre los mismos casos de uso al usuario (para interactuar con los contratos).

Features

Como mencioné anteriormente, la lógica de esta aplicación está contenida principalmente en dos features independientes: management y explore. También hay una carpeta shared que incluye código compartido entre las dos features.

A continuación podemos ver la estructura de archivos de la feature management:

En las siguientes secciones repasaré las carpetas principales en next-app/src/features/management para explicar su propósito.

Core

core recopila la lógica de negocio de la feature. Se compone de entidades, repositorios, interactores y adaptadores.

Cuando se llama a un hook en un componente de la interfaz de usuario, invocará al menos un caso de uso, o interactor, que interactuará con un repositorio para obtener una entidad.

Esa entidad se adaptará a otra interfaz a través de una función de adaptador.

Entidades

Las entidades son interfaces que representan los objetos que deben devolverse desde los adaptadores.

Dado que esta aplicación usa TheGraph Client para solicitar datos al subgrafo, se aprovechan los tipos de TypeScript que ofrece para las entidades que devuelve.

Por lo tanto, los adaptadores adaptarán un tipo de la consulta de TheGraph Client a una entidad definida en la carpeta entities.

Podemos ver a continuación la interfaz que debe devolver el adaptador de tokens:

next-app/src/features/management/core/entities/MyTokens.tsx

Como vemos, hay entidades que se importan de la carpeta shared. Esto se debe al uso de estas entidades en ambas features de la aplicación.

Repositorios

Un repositorio es una interfaz que define todos los métodos que son necesarios para lidiar con todos los casos de uso de una feature en particular.

Estos métodos se implementan en una clase de fuente de datos fuera del núcleo.

Por ejemplo, podemos ver el repositorio MyTokensRepository en la función management con sus dos métodos resaltados a continuación:

next-app/src/features/management/core/repositories/MyTokens.repository.tsx
Interactores

Los interactores son el resultado de la implementación de los casos de uso en la feature correspondiente.

Por ejemplo, el interactor queryTokensByAccountWithDep se utiliza para obtener tokens por cuenta (mediante el hook useTokensByAccount).

Los hooks usan interactores que ya tienen las dependencias inyectadas para mantener la lógica de negocio independiente de la infraestructura.

Esta inyección se realiza en next-app/src/features/management/core/interactors/index.ts

Podemos ver resaltado a continuación la inyección de una instancia de MyTokensDataSource a queryTokensByAccount:

next-app/src/features/management/core/interactors/index.ts

La lógica de los interactores se puede encontrar en la misma carpeta. A continuación se muestra el código para el interactor queryTokensByAccount:

next-app/src/features/management/core/interactors/queryTokensByAccount.interactor.ts

Como podemos ver, este interactor espera una entidad MyTokensRepository que se utiliza para llamar a su método getTokensByAccount, pero el interactor no depende de su implementación.

Con esta arquitectura limpia, cualquier cambio que se pueda hacer en la infraestructura no afectará la lógica comercial, es decir, el código en core.

Adaptadores

Los adaptadores son funciones cuyo único propósito es adaptar un objeto de una interfaz a otra. Esto es muy útil como una forma de evitar que posibles cambios en el objeto que se solicita afecten al resto del código.

Por ejemplo, si la interfaz de la entidad del token en el subgrafo cambiara, es posible que solo tengamos que editar el adaptador (suponiendo que el objeto devuelto por el adaptador permanezca con la misma interfaz).

Podemos ver en el bloque de código anterior que el interactor queryTokensByAccount pasa los datos de la solicitud a tokensByAccountAdapter en la línea 9.

A continuación se muestra cómo esta función obtiene un objeto de tipo TokensByAccountQuery y devuelve otro con la interfaz Tokens (o null si la cuenta en cuestión no dispone de tokens en Olive Oil Trust).

next-app/src/features/management/core/adapters/tokensByAccount.adapter.ts

Fuentes de datos

Las fuentes de datos en esta aplicación son fundamentalmente la implementación de los métodos de los repositorios.

Utilizo clases de TypeScript para aprovechar su cláusula implements y verificar que la clase se ajusta a la interfaz del repositorio.

Por ejemplo, podemos ver a continuación la fuente de datos que implementa los métodos para consultar tokens en management:

next-app/src/features/management/dataSources/adapters/myTokens.datasource.ts

La función execute obtiene un documento (que es la plantilla literal con la consulta que genera TheGraph Client) y las variables que espera la consulta (que se pasan al método de la fuente de datos).

Útiles

La carpeta utils en cada feature contiene constantes, interfaces y funciones auxiliares que se usan en el código de esa feature, pero también una carpeta con todas las consultas que se usan en las fuentes de datos de esa feature.

Interfaz de usuario

Todos los componentes de React.js, así como los hooks para una carpeta en particular, se incluyen en una carpeta denominada ui al nivel de la feature.

Estos componentes obtienen objetos de estado (con datos y un campo de error) de los hooks y representan componentes de acuerdo con las acciones del usuario y el estado de la solicitud.

Por ejemplo, como podemos ver a continuación MyTokensByAccount.tsx llama al hook useTokensByAccount para obtener un objeto TokensState:

next-app/src/features/management/ui/tokens/MyTokensByAccount.tsx

Ese hook llama al interactor y devuelve el estado de la solicitud, lo que hace que el componente muestre una de los siguientes opciones:

  • un mensaje de error si ha habido un error al ejecutar la consulta.

  • la lista de tarjetas de tokens y botones para los casos de uso si la solicitud se ejecuta sin errores.

  • un mensaje de carga mientras error sea null.

Interactuando con la interfaz de usuario

Como dijimos anteriormente en esta serie de publicaciones, Olive Oil Trust aprovecha el estándar multitoken ERC-1155 para administrar una o múltiples transferencias de lotes a la vez.

También se permite la creación y certificación de uno o múltiples tipos de tokens.

Eso significa que es posible que se deba pasar una gran cantidad de datos a la función del contrato adecuada para realizar la transacción.

Por ejemplo, podemos ver a continuación los argumentos esperados por el método mintBatch de una abstracción de TypeScript de un contrato de una planta embotelladora:

Entonces, para que ese miembro acuñe 4 lotes de tokens de botellas de aceite de oliva en Olive Oil Trust, se debe realizar una transacción como la siguiente:

hardhat-env/tasks/setState.ts

Como podemos ver, es muy conveniente, desde una perspectiva de UX, reducir la cantidad de complejidad que le queda al usuario para realizar una tarea como esta.

En las siguientes secciones, mostraré cómo he intentado lograrlo mientras introduzco los principales casos de uso en Olive Oil Trust.

Adoptaré los roles de un certificador, una planta embotelladora, dos fabricantes de botellas, una almazara y un distribuidor para plantear estos escenarios.

Certificando tipos de token

Una vez que un miembro crea un tipo de token, el subgrafo lo extraerá, procesará y almacenará para que la interfaz de usuario pueda consultarlo.

Cuando un certificador inicie sesión y vaya a /management/my-certificates, verá un botón habilitado para certificar tipos de token.

En la imagen inferior, el certificador está creando dos certificados, uno para el aceite de oliva Picual y otro para las botellas de 750 ml:

Como podemos ver, el certificado 5544876543211C certifica dos tipos diferentes de botellas de vidrio de 750 ml (esos tipos son de diferentes fabricantes).

Veremos más adelante en acuñando tokens cómo el uso de este certificado le permitirá al acuñador seleccionar tokens de diferentes tipos al momento de acuñar un nuevo token.

La lista desplegable del elemento select de los tipos de tokens contiene todos los tipos de token creados hasta ese momento en Olive Oil Trust y agrupados por roles:

Una vez que se agregan ambos certificados y se hace clic en "Certify" (certificar), la billetera inyectada le pedirá al usuario que confirme o revierta la transacción, es decir, las certificaciones:

Si las transacciones se confirman, ambos certificados aparecerán en la lista tras la siguiente obtención de datos:

Estos certificados estarán disponibles (junto con otros certificados y tipos de token) en las opciones de la lista desplegable al crear un nuevo tipo de token.

Añadiendo tipos de token

La creación de un tipo de token es la forma en la que un miembro establece una forma irreversible de acuñar un token dependiente de ese tipo; por lo tanto, siendo una forma de dar instrucciones para su creación.

Si una planta embotelladora fuese a acuñar tokens de botellas de un tipo de aceite de oliva nuevo, tendría que crear un tipo de token para ese tipo de botellas de aceite de oliva.

Digamos que quiere crear un tipo de aceite de oliva virgen extra en botellas de alta calidad de 750 ml.

Podríamos decirle al contrato que las instrucciones para ese tipo tendrán que obligar al acuñador a usar algún tipo de aceite de oliva y de botella en particular, o especificar un certificado en su lugar.

Designar un certificado permitiría al acuñador utilizar cualquier tipo de token (que haya sido ya certificado con ese mismo certificado) para la creación del token.

Eso permitiría al acuñador comprar tokens de uno o de múltiples tipos, de una o de múltiples fuentes para la creacion de un token, siempre que el certificado de ese tipo de token lo permita.

Cuando la planta embotelladora inicie sesión, vaya a /management/my-token-types y haga clic en "Add Token Types" (añadir tipos de tokens), aparecerá un formulario para la creación de uno o varios tipos de tokens.

En la siguiente imagen, se están creando dos tipos, uno para una botella de plástico de 5l de aceite de oliva virgen extra suave, y otro para una botella de vidrio de 750 ml de aceite de oliva virgen extra intenso:

La lista desplegable del elemento select del id de las instrucciones contiene todos los tipos de token y certificados creados hasta ese momento en Olive Oil Trust y agrupados por tipo:

Una vez que se agreguen los tipos y se confirme y complete la transacción, la empresa embotelladora podrá acuñar tokens de ese tipo.

Sin embargo, primero deberá comprar los tokens que necesita, siguiendo las instrucciones del tipo de token correspondiente.

Comprando tokens

El rol de una planta embotelladora depende de la adquisición de otros tokens para acuñar tokens de botellas de aceite de oliva, a saber, tokens de botellas y de aceite de oliva.

El proceso de compra de tokens es a través de escrows, que obtienen diferentes estados según las interacciones del vendedor y el comprador con ellos.

El usuario tiene que ir a /explore/escrows para comprar tokens, donde se encuentran todos los escrows que se han creado en la cadena de suministro.

Los escrows que no están cerrados ni revertidos mostrarán botones interactuables si el usuario ha iniciado sesión y se espera que considere un escrow de ese vendedor.

Por ejemplo, en esta aplicación las plantas embotelladoras solo podrán comprar tokens de los fabricantes de botellas y de las almazaras de aceite de oliva porque su rol es el siguiente en la larga cadena de valor del aceite de oliva.

Digamos que una planta embotelladora está interesada en comprar tokens de botellas de dos proveedores diferentes, y tokens de aceite de oliva de una sola almazara.

Tendría que esperar hasta que los escrows correspondientes estén activos para interactuar con ellos.

Estos escrows serían similares a los siguientes:

Una vez que haga clic en "Make payment" (Hacer pago) en uno de estos escrows, aparecerá el siguiente formulario:

La dirección de la billetera del comprador que se solicita será la dirección a la que se enviarán los fondos que se depositan en el contrato del escrow si se cancela el pago, o si se revierte el escrow.

Si hace clic en "Make payment" en el formulario, la dirección es válida y la transacción (por un importe equivalente al precio del escrow) se confirma, se depositará ether en el contrato del escrow y la tarjeta del escrow se actualizará:

Una vez que se ha depositado ether en un escrow, un usuario que ha iniciado sesión podrá ver e interactuar con ese escrow en /management/my-participations.

Podemos ver que el estado del escrow cambió a "EtherDeposited" y que hay un campo "Buyer candidate" con el nombre (o la dirección) del usuario que realizó el pago.

Ese pago se representa en el campo "Ether balance".

En esta etapa, el escrow solo será interactuable para el vendedor y el candidato a comprador, es decir, ninguna otra planta embotelladora podrá depositar ether.

El vendedor verá, ya sea en /management/my-escrows o en /explore/escrows, que su escrow muestra las opciones para revertir y cerrar:

De cualquier manera, se realizará una transacción una vez que se confirme a través de la billetera inyectada.

En el caso de "Revert", los fondos se transferirán de vuelta al candidato a comprador y los tokens de vuelta al vendedor.

Mientras que en el caso de "Close", los fondos se transferirán al vendedor y los tokens al comprador.

La tarjeta de escrow reflejará estos cambios a partir de entonces:

Como podemos ver, la tarjeta muestra que el saldo de ether ahora es cero, así como el saldo de tokens, y que el estado del escrow es "Cerrado".

El comprador verá ahora esos tokens en /management/my-tokens cuando inicie sesión.

Acuñando tokens

En /management/my-tokens, justo encima de las tarjetas de tokens, hay un botón para cada acción relacionada con los tokens.

Los botones se habilitarán si el acuñador tiene los tokens apropiados para esa acción en concreto.

Al hacer clic en "Mint" (acuñar) aparecerá un formulario.

Según el tipo que se elija en la lista desplegable en la parte superior, la lista de instrucciones será diferente, así como las opciones que se dan a los campos seleccionados.

Elegir un tipo de token diferente probablemente obligará al acuñador a elegir diferentes tokens a partir de los cuales se acuña el nuevo token.

Esta es la razón por la que cada vez que se elige un tipo diferente en la lista desplegable, las opciones de tokens dadas a la acuñación pueden variar.

Además, la cantidad total de unidades a acuñar se utilizará para calcular automáticamente la cantidad de tokens necesarias para cada instrucción.

Esa cantidad aparece reflejada pero también está disponible para establecerse en el elemento input al hacer clic, y así facilitar el proceso.

La siguiente imagen muestra un ejemplo de un formulario donde ya se añadieron a la lista 3 lotes de 100 tokens cada uno y se está completando un cuarto:

Como podemos ver arriba, se han escogido dos lotes de dos fabricantes distintos desde donde obtener las botellas de cristal.

La lista desplegable del elemento de selección de lote para la instrucción de la botella contiene todos los lotes propiedad del acuñador que han sido certificados (por el certificado en las instrucciones del tipo de token seleccionado) hasta ese momento en Olive Oil Trust, y agrupados por tipo:

Como podemos ver hay cuatro opciones que son los cuatro lotes acuñados por los fabricantes de botellas (dos cada uno).

Una vez que se añaden los tokens, el usuario hace clic en "Mint tokens" (acuñar tokens) y se completa la transacción, aparecerán los nuevos lotes de tokens.

Empaquetando tokens

Los integrantes de la fase industrial de la larga cadena de valor del aceite de oliva (plantas envasadoras y distribuidoras) venden unidades industriales (palets) en lugar de unidades comerciales.

Digamos que la empresa embotelladora quiere crear dos palets que contengan cuatro lotes diferentes de 30 tokens cada uno.

Podemos ver a continuación una recreación de dicha operación:

La lista desplegable del elemento select del id del lote contiene todos los lotes propiedad del usuario que se puedan empaquetar creados hasta ese momento en Olive Oil Trust y agrupados por tipo:

La lista de arriba no contendrá lotes propiedad del usuario que no hayan sido acuñados por el usuario.

Por ejemplo, no se permitirá en esta app que una planta embotelladora pueda empaquetar tokens de aceite de oliva o de botellas de cristal aunque sean de su propiedad, sino solamente tokens de botellas de aceite de oliva.

Si el usuario continúa con la operación y completa la transacción, la cantidad de unidades que hayan sido empaquetadas se transferirán al contrato de unidad industrial, es decir, ya no aparecerán en la lista de tokens.

Además, la lista incluirá nuevas unidades, que serán los dos tokens de unidades industriales que se acaban de acuñar:

Depositando tokens

Una vez un miembro posea los tokens apropiados para depositarlos en el escrow, podrá hacer clic en "Deposit" (depositar).

Podemos ver a continuación como depositar dos de las unidades industriales recién creadas y fijar un precio de 0.1 ETH:

Una vez que se han depositado los tokens, es decir, una vez que el usuario ha hecho clic en "Deposit" y ha completado la transacción, debe esperar a que un comprador deposite ether para poder cerrar el escrow.

También puede revertir la operación en cualquier momento.

El proceso de interacción con un escrow se recrea en comprando tokens.

Desempaquetando tokens

Si un distribuidor adquiere tokens de unidades industriales de una planta embotelladora, tiene que desempaquetarlos para obtener la propiedad de los tokens en el palé.

Digamos que un distribuidor ha comprado los dos palets depositados anteriormente.

Para desempaquetarlos tendría que hacer clic en "Unpack" (desempaquetar) en /management/my-tokens.

Luego, aparecerá un formulario como el que se muestra a continuación, donde se pueden seleccionar los tokens a desempaquetar en una lista desplegable.

Para completar la acción, simplemente haga clic en "Unpack" (desempaquetar) y acepte la transacción.

El distribuidor verá (una vez que se hayan recuperado los datos) los cuatro lotes contenidos en ambos palés:

Todos los lotes contienen 60 unidades que son la suma de todos los lotes en las fichas de unidades industriales envasadas por la planta embotelladora.

Trazando tokens

Como vimos anteriormente, a cada lote de tokens en esta aplicación se le asignará una página que contendrá toda la información disponible respecto a ese lote.

El componente React.js de esta página se muestra en regeneración estática incremental

En la parte inferior de la página renderizada hay información sobre la ascendencia del token.

Por ejemplo, podemos ver abajo la ascendencia del lote 1072583 acuñado por Bottling Company:

Esta imagen es útil para ver qué lotes de tokens se usaron para acuñar cada token en cada nivel del árbol.

Dado que utilizamos un certificado de botella de vidrio de 750 ml para acuñar piezas del tipo Extra Virgin Intense Olive Oil 750 ml Glass Bottle (Aceite de Oliva Virgen Extra Intenso Botella de Vidrio de 750 ml), pudimos utilizar botellas de vidrio de 750 ml de dos tipos diferentes, ambos certificados por el mismo certificado.

Conclusión

En esta serie de publicaciones he intentado explicar cómo crear un conjunto completo de contratos que implementen todos los casos de uso presentes en la larga cadena de valor del aceite de oliva.

Además, se ha introducido un subgrafo que extrae datos de estos contratos, los procesa a través de funciones de mapeo y los almacena.

Finalmente, se presenta una aplicación de front end donde estos datos pueden ser fácilmente consultados y el usuario pueda interactuar con los contratos de manera eficiente.


Artículos relacionados

zk-SNARK

Cómo desarrollar una DApp de conocimiento cero

Esta entrada ofrece una introducción a cómo desarrollar una aplicación capaz de generar y validar...

Microservicios

Propuesta de una aplicación bancaria con arquitectura basada en microservicios

Aplicación bancaria de arquitectura basada en microservicios que incluye aplicaciones de back end, front end,...

DeFi

Guía de principio a fin para crear una DApp específica para un token ERC-20

Aplicación descentralizada para operar con un token ERC-20 acuñable

DeFi

Cómo crear una aplicación de analítcas de DEXs

Introducción a la obtención y representación de datos de DEXs utilizando TheGraph y React.js

Trazabilidad

Presentando Olive Oil Trust

Introducción a una serie de artículos acerca de Olive Oil Trust


¿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.