Por Fran Palacios
En este artículo, utilizaremos los términos “Eventos” y “Registros” para referirnos al mismo concepto. En Ethereum, se prefiere uno u otro término según el contexto (por ejemplo, “registros” en la EVM y “eventos” en Solidity). Sin embargo, como ambos términos son interdependientes y consecuencia uno del otro, los utilizaremos indistintamente para referirnos a lo mismo.
Los eventos en Solidity se pueden considerar como una abstracción de los registros (logs) que la EVM utiliza para almacenar información en una estructura de datos indexada; conocida como Bloom Filters1. Esta estructura permite buscar información de manera eficiente, lo que permite a los nodos ligeros (aquellos que no almacenan todo el historial de la blockchain) consultar los registros.
Al escribir un contrato inteligente en Solidity, es posible declarar y emitir eventos. La pregunta es, ¿para qué sirven los eventos si desde un contrato no podemos suscribirnos a ellos ni recibir actualizaciones de otros contratos que emiten eventos?
Los eventos en Solidity tienen la siguiente sintaxis:
contract MySmartContract {
event UserRegistered(uint256 userId)
event WinnerSelected(address indexed winnerAddress)
function registerUser() public {
emit UserRegistered(10);
}
function selectWinner() public {
emit WinnerSelected(0x2d3052db3062d60643682b1272d00a6bf4a1f5e6);
}
}
Para declarar eventos en Solidity, es necesario darles un nombre y definir sus parámetros. Por convención, se suele utilizar el nombre de la función que emite el evento, invertido. Para emitir un evento, simplemente se llama a la función emit
junto con el nombre del evento y los parámetros definidos. Es importante tener en cuenta que algunos de los parámetros pueden llevar la palabra reservada indexed
, mientras que otros no. En una sección posterior veremos el uso de esta.
Volviendo a la pregunta, para qué queremos emitir eventos a los cuales no podemos suscribirnos? La respuesta es que queremos suscribirnos a esos eventos desde fuera de la red. Es importante tener en cuenta que una red blockchain es como un circuito cerrado, y no es tan fácil traer datos desde fuentes externas como si se tratara de una petición HTTP, ni tampoco enviar datos a servidores específicos. Es cierto que tampoco puedes suscribirte directamente a un evento, ya que la red no te envía una notificación de que ha ocurrido algo, sino que, simplemente cuando ocurre un evento, la EVM añade ese registro (log) al nodo, que luego puede ser consultado de diversas formas.
Para este ejemplo usaremos la herramienta composer de Alchemy. Como sabemos los nodos exponen un endpoint JSON-RPC al cuál podemos hacerle peticiones POST, en concreto, usaremos el método eth_getLogs
para obtener aquellos logs pertenecientes a un contrato determinado que haya emitido algun evento. En principio, sólo es obligatorio poner la dirección del contrato como parámetro, pero es importante añadir el rango de bloques para evitar sobrecargar el nodo haciendo una búsqueda muy extensa.
Por ejemplo:
Esto nos daría como respuesta algo así:
{
"jsonrpc": "2.0",
"id": 0,
"result": [
{
"address": "0x62a6ef16f4c7605e89f6ebbc38dac89bc7aacd0b",
"blockHash": "0x6df099a0fecc4d4cfae06b691aba295c7de3fde09ffa527eaa71920bbbe4ba35",
"blockNumber": "0x81f06f",
"data": "0x00000000000000000000000033ffcccda2a9f07ab03191ebdf8ec0ad5edc6ac000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009d3052db3062d60643682b1272d00a6bf4a6f5e6",
"logIndex": "0x37",
"removed": false,
"topics": [
"0x5ba0ddd8e72e3c5c274414bd1ef0dec9ae5220e0f6f534d859043e2a52f0319f"
],
"transactionHash": "0xda633d761ae7683cb75de343ef6651daed5f8b78ce9d0e622345c4f392087a7c",
"transactionIndex": "0x19"
},
// ...
]
}
Así a simple vista no es legible, ni aporta mucho… Luego veremos casos de uso.
¡Claro! Además del rango de bloques, puedes agregar topics a la consulta de los logs. Los topics son un array que contiene los argumentos del evento emitido, pero se agregan en un formato específico. Por ejemplo, si queremos filtrar por el evento UserRegistered
, deberíamos agregar al topic el hash resultante de aplicar la función keccak256
a su forma canónica2 (puedes utilizar esta herramienta para obtener el valor).
"topics": ["0x6b1da47e6cb6a4952c75fff4300f06caf20aa8269a4a398f315562926c5bed39"]
Además de filtrar por los nombres de eventos, también podemos filtrar por los valores de sus argumentos. En este caso, es donde entra en juego la palabra reservada indexed
que vimos anteriormente. Como máximo, se pueden tener 3 argumentos indexados, lo que significa que estos valores se agregarán en formato hexadecimal al array de topics. En el caso de UserRegistered
, no podríamos utilizar esta funcionalidad, ya que su argumento no es indexado. Sin embargo, en el evento WinnerSelected
, sí podríamos hacerlo. Para ello, deberíamos agregar tanto el hash como la dirección del contrato al topic correspondiente, quedando el array de topics de la siguiente manera:
"topics":[
// hash
"0x1d4c260f1824cd028e6c9e6e31c3a0b94f2513e7a641113ec759d382f9bdd5a1",
// address
"0x0000000000000000000000002d3052db3062d60643682b1272d00a6bf4a1f5e6"
]
Los eventos en Solidity son muy útiles para actualizar la interfaz de usuario (UI) de una dApp3. Esto se debe a que, mediante librerías como web3js o ethers, podemos consultar los logs a través del proveedor4 y tomar acciones en tiempo real en función de los eventos recibidos. De esta forma, podemos tener una UI siempre actualizada con la última información disponible en la blockchain.
Por ejemplo:
const filter = {
// Supongamos que es la dirección de MySmartContract
address: "0x000...123",
topics: [
utils.id("WinnerSelected(address)") // Aplica la función hash keccak256
]
}
provider.on(filter, (log, event) => {
// Esta lógica se ejecutará cada vez que se emita el evento WinnerSelected
})
Lo cierto es que no es trivial filtrar logs ni hacer este tipo de consultas a la red. Si necesitamos realizar consultas con filtros más complejos, por ejemplo, primero tendríamos que procesar y almacenar los datos en una base de datos específica y luego hacer consultas en SQL, Mongo u otra herramienta. Por suerte, existe una solución para este problema: The Graph.
Otro caso de uso es almacenar datos en un sistema externo al contrato o los logs, en donde TheGraph es muy útil. Se suele usar para marketplaces de NFT, donde se necesita mantener un listado de los NFT en que estan en venta con ciertas propiedades (lo que implica un preprocesamiento de los datos). Para conseguirlo solo necesitamos emitir eventos de comprar o poner en venta NFTs, sin tener la necesidad de guardar esas listas en el contrato, lo que sería más costoso.
Para usar The Graph, simplemente le indicamos la dirección del contrato y el ABI5, y él se encarga de consultar esos eventos constantemente y ejecutar una función “mapper” específica (definida por nosotros) para guardarlos en su nodo. Luego, el nodo de The Graph nos expone un endpoint de GraphQL para consultar los datos agregados de la forma que deseemos.
Documentación de eventos en solidity
Bloom Filters: https://es.wikipedia.org/wiki/Filtro_de_Bloom ↩︎
Forma canónica: Se refiere al nombre de la función seguido de los tipos de sus parámetros. Por ejemplo la function foo(boolean bar){}
sería foo(boolean)
↩︎
DAPP: Aplicación web descentralizada. ↩︎
Proveedor: Es una entidad que hace de intermediaro entre el cliente de JS y un nodo de ethereum. En otras palabras, es una abstracción a la conexión a la red de Ethereum. Si usas web3js o ethers puedes configurar como proveedor a Infura, Metamask, Alchemy… entre otros. ↩︎
ABI: Es un JSON que describe todos los métodos, variables y eventos definidos en un contrato inteligente. ↩︎
¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.
Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?
Pero espera 🖐 que tenemos un conflicto interno. A nosotros las newsletter nos parecen 💩👎👹 Por eso hemos creado la LEAN LISTA, la primera lista zen, disfrutona y que suena a rock y reggaeton del sector de la programación. Todos hemos recibido newsletters por encima de nuestras posibilidades 😅 por eso este es el compromiso de la Lean Lista