Saltar al contenido principal

Eventos

El ejemplo de eventos demuestra cómo publicar eventos desde un contrato. Este ejemplo es una extensión del ejemplo de almacenamiento de datos.

Abrir en Gitpod

Ejecutar el ejemplo

Primero, sigue el proceso de Configuración para tener tu entorno de desarrollo configurado, luego clona la etiqueta v21.6.0 del repositorio soroban-examples:

git clone -b v21.6.0 https://github.com/stellar/soroban-examples

O, salta la configuración del entorno de desarrollo y abre este ejemplo en Gitpod.

Para ejecutar las pruebas del ejemplo, navega al directorio events, y usa cargo test.

cd events
cargo test

Deberías ver la salida:

running 1 test
test test::test ... ok

Código

events/src/lib.rs
const COUNTER: Symbol = symbol_short!("COUNTER");

#[contract]
pub struct IncrementContract;

#[contractimpl]
impl IncrementContract {
/// Increment increments an internal counter, and returns the value.
pub fn increment(env: Env) -> u32 {
// Get the current count.
let mut count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0); // If no value set, assume 0.

// Increment the count.
count += 1;

// Save the count.
env.storage().instance().set(&COUNTER, &count);

// Publish an event about the increment occuring.
// The event has two topics:
// - The "COUNTER" symbol.
// - The "increment" symbol.
// The event data is the count.
env.events()
.publish((COUNTER, symbol_short!("increment")), count);

// Return the count to the caller.
count
}
}

Ref: https://github.com/stellar/soroban-examples/tree/v21.6.0/events

Cómo funciona

Este contrato de ejemplo extiende el ejemplo de incremento publicando un evento cada vez que se incrementa el contador.

Los eventos del contrato permiten a los contratos emitir información sobre lo que su contrato está haciendo.

Los contratos pueden publicar eventos utilizando la función de publicación de eventos de los entornos.

env.events().publish(topics, data);

Temas de eventos

Los temas se definen convenientemente utilizando una tupla. En el código de ejemplo se utilizan dos temas de tipo Symbol.

env.events().publish((COUNTER, symbol_short!("increment")), ...);
consejo

Los temas no tienen que estar hechos del mismo tipo.

Datos del evento

Un evento también contiene un objeto de datos de cualquier valor o tipo, incluyendo tipos definidos por contratos utilizando #[contracttype]. En el ejemplo, los datos son el conteo u32. En el ejemplo, los datos son el conteo u32.

env.events().publish(..., count);

Publicar

Publicar un evento se realiza llamando a la función publish y dándole los temas y datos. La función no retorna nada si tiene éxito, y causa pánico en caso de falla. Las razones de fallo pueden incluir entradas mal formadas (por ejemplo, el conteo de temas excede el límite) y agotar el presupuesto de recursos (TBD). Una vez publicado exitosamente, el nuevo evento estará disponible para aplicaciones que consuman los eventos.

env.events().publish((COUNTER, symbol_short!("increment")), count);
advertencia

Los eventos publicados se descartan si una invocación de contrato falla debido a un pánico, agotamiento del presupuesto, o cuando el contrato retorna un error.

Pruebas

Abre el archivo events/src/test.rs para seguir adelante.

events/src/test.rs
#[test]
fn test() {
let env = Env::default();
let contract_id = env.register_contract(None, IncrementContract);
let client = IncrementContractClient::new(&env, &contract_id);

assert_eq!(client.increment(), 1);
assert_eq!(client.increment(), 2);
assert_eq!(client.increment(), 3);

assert_eq!(
env.events().all(),
vec![
&env,
(
contract_id.clone(),
(symbol_short!("COUNTER"), symbol_short!("increment")).into_val(&env),
1u32.into_val(&env)
),
(
contract_id.clone(),
(symbol_short!("COUNTER"), symbol_short!("increment")).into_val(&env),
2u32.into_val(&env)
),
(
contract_id,
(symbol_short!("COUNTER"), symbol_short!("increment")).into_val(&env),
3u32.into_val(&env)
),
]
);
}

En cualquier prueba, lo primero que siempre se requiere es un Env, que es el entorno de Soroban en el que el contrato se ejecutará.

let env = Env::default();

El contrato se registra con el entorno utilizando el tipo de contrato.

let contract_id = env.register_contract(None, IncrementContract);

Todas las funciones públicas dentro de un bloque impl que está anotado con el atributo #[contractimpl] tienen una función correspondiente generada en un tipo de cliente generado. El tipo de cliente se llamará igual que el tipo de contrato con Client añadido. Por ejemplo, en nuestro contrato el tipo de contrato es IncrementContract, y el cliente se llama IncrementContractClient.

let client = IncrementContractClient::new(&env, &contract_id);

El ejemplo invoca el contrato varias veces.

assert_eq!(client.increment(), 1);

El ejemplo afirma que se publicaron los eventos.

assert_eq!(
env.events().all(),
vec![
&env,
(
contract_id.clone(),
(symbol_short!("COUNTER"), symbol_short!("increment")).into_val(&env),
1u32.into_val(&env)
),
// ...
]
);

Construir el contrato

Para construir el contrato, usa el comando stellar contract build.

stellar contract build

Un archivo .wasm debería generarse en el directorio target:

target/wasm32-unknown-unknown/release/soroban_events_contract.wasm

Ejecutar el contrato

Si tienes stellar-cli instalado, puedes invocar funciones del contrato utilizándolo.

stellar contract invoke \
--wasm target/wasm32-unknown-unknown/release/soroban_events_contract.wasm \
--id 1 \
-- \
increment

La siguiente salida debería ocurrir utilizando el código anterior.

1
#0: event: {"ext":"v0","contractId":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],"type":"contract","body":{"v0":{"topics":[{"symbol":[67,79,85,78,84,69,82]},{"symbol":[105,110,99,114,101,109,101,110,116]}],"data":{"u32":1}}}}

Se genera un único evento #0, que es el evento del contrato que publicó el contrato. El evento contiene los dos temas, cada uno un symbol (mostrado como bytes), y el objeto de datos que contiene el u32.