> ## Documentation Index
> Fetch the complete documentation index at: https://developers.stellar.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Publish events from a smart contract.

The [events example] demonstrates how to publish events from a contract. This example is an extension of the [storing data example].

> [!TIP]
>
> **Whisk Changes**
>
> With the release of Whisk, Protocol 23, the syntax for publishing smart contract events has changed. In order to provide the most up-to-date information, this example has been updated to include the new patterns. Find more detailed information in the \[Rust SDK documentation].

[![Open in Codespaces](https://github.com/codespaces/badge.svg)][open-in-github-codespaces]

[![Open in Codeanywhere](https://codeanywhere.com/img/open-in-codeanywhere-btn.svg)][open-in-code-anywhere]

[open-in-github-codespaces]: https://github.com/codespaces/new?repo=stellar/soroban-examples&editor=web

[open-in-code-anywhere]: https://app.codeanywhere.com/#https://github.com/stellar/soroban-examples

[events example]: https://github.com/stellar/soroban-examples/tree/main/events

[storing data example]: https://developers.stellar.org/docs/build/smart-contracts/getting-started/storing-data.md

[Rust SDK documentation]: https://docs.rs/soroban-sdk/latest/soroban_sdk/_migrating/v23_contractevent/index.html

## Run the Example

First go through the [Setup] process to get your development environment configured, then clone the `main` branch of the `soroban-examples` repository:

[setup]: https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup.md

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
git clone -b main https://github.com/stellar/soroban-examples
```

Or, skip the development environment setup and open this example in [GitHub Codespaces][open-in-github-codespaces] or [Code Anywhere][open-in-code-anywhere].

To run the tests for the example, navigate to the `events` directory, and use `cargo test`.

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
cd events
cargo test
```

You should see the output:

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

## Code

```rust title="events/src/lib.rs" theme={"theme":{"light":"github-light","dark":"github-dark"}}
const COUNTER: Symbol = symbol_short!("COUNTER");

// Define two static topics for the event: "COUNTER" and "increment".
// Also set the data format to "single-value", which means that the event data
// payload will contain a single value not nested into any data structure.
#[contractevent(topics = ["COUNTER", "increment"], data_format = "single-value")]
struct IncrementEvent {
    count: u32,
}

#[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 static topics ("COUNTER", "increment") and actual
        // count as the data payload.
        IncrementEvent { count }.publish(&env);

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

Ref: [https://github.com/stellar/soroban-examples/tree/main/events](https://github.com/stellar/soroban-examples/tree/main/events)

## How it Works

This example contract extends the increment example by publishing an event each time the counter is incremented.

Contract events let contracts emit information about what their contract is doing. Contracts can publish events creating a defined `struct` and `publish`ing it to the smart contract environments.

First, the `#[contractevent]` struct must be defined

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[contractevent(topics = ["COUNTER", "increment"], data_format = "single-value")]
struct IncrementEvent {
    count: u32,
}
```

Then, inside the contract's function, we can create and publish the event with the relevant data.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
IncrementEvent { count }.publish(&env);
```

### Event Topics

Topics can be defined either statically or dynamically. In the sample code two static topics are used, which will be of the `Symbol` type: `COUNTER` and `increment`.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[contractevent(topics = ["COUNTER", "increment"], ...)]
```

> [!TIP]
>
> The topics don't have to be made of the same type.

Topics can also be defined dynamically, inside the struct. In this case, the struct's `snake_case` name will be the first topic. For example, the following event will have two topics: the `Symbol` "increment", followed by an `Address`.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[contractevent]
pub struct Increment {
    #[topic]
    addr: Address,
    count: u32,
}
```

### Event Data

An event also contains a data object of any value or type including types defined by contracts using `#[contracttype]`. In the example the data is the `u32` count. The `data_format = "single-value"` tells the event to publish the data alone, with no surrounding data structure.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[contractevent(..., data_format = "single-value")]
```

Event data will, by default, conform to the data structure in the defined `struct`. The `data_format` can also be specified as `vec` or `single-value`. Again, please refer to the [Rust SDK documentation] for more details.

### Publishing

Publishing an event is done by calling the `publish` function on the created event struct. The function returns nothing on success, and panics on failure. Possible failure reasons can include malformed inputs (e.g. topic count exceeds limit) and running over the resource budget (TBD). Once successfully published, the new event will be available to applications consuming the events.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
IncrementEvent { count }.publish(&env);
```

> [!WARNING]
>
> Published events are discarded if a contract invocation fails due to a panic, budget exhaustion, or when the contract returns an error.

## Tests

Open the [`events/src/test.rs`](https://github.com/stellar/soroban-examples/tree/main/events/src/test.rs) file to follow along.

```rust title="events/src/test.rs" theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[test]
fn test() {
    let env = Env::default();
    let contract_id = env.register(IncrementContract, ());
    let client = IncrementContractClient::new(&env, &contract_id);

    // Assert on the events emitted by the last contract invocation using
    // the contract's event struct defined with #[contractevent] macro to
    // construct the expected event in xdr form for comparison.
    assert_eq!(client.increment(), 1);
    assert_eq!(
        env.events().all(),
        [IncrementEvent { count: 1 }.to_xdr(&env, &contract_id)]
    );

    // Assert on the events emitted by the last contract invocation that
    // were emitted by a contract with a specific contract id. This is
    // useful when your contract might call other contracts that also emit events.
    assert_eq!(client.increment(), 2);
    assert_eq!(
        env.events().all().filter_by_contract(&contract_id),
        [IncrementEvent { count: 2 }.to_xdr(&env, &contract_id)]
    );

    // Assert on the events emitted by the last contract invocation by
    // building a tuple form of the event manually. This is useful
    // when the contract does not define its events using the #[contractevent] macro.
    //
    // Tuple Format: (contract_id: Address, topics: Val, data: Val)
    assert_eq!(client.increment(), 3);
    assert_eq!(
        env.events().all(),
        soroban_sdk::vec![
            &env,
            (
                contract_id,
                (symbol_short!("COUNTER"), symbol_short!("increment")).into_val(&env),
                3u32.into_val(&env)
            ),
        ]
    );
}
```

In any test the first thing that is always required is an `Env`, which is the Soroban environment that the contract will run in.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let env = Env::default();
```

The contract is registered with the environment using the contract type.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let contract_id = env.register(IncrementContract, ());
```

All public functions within an `impl` block that is annotated with the `#[contractimpl]` attribute have a corresponding function generated in a generated client type. The client type will be named the same as the contract type with `Client` appended. For example, in our contract the contract type is `IncrementContract`, and the client is named `IncrementContractClient`.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let client = IncrementContractClient::new(&env, &contract_id);
```

The example invokes the contract several times.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
assert_eq!(client.increment(), 1);
```

The example asserts that the event was published. `env.events().all()` returns a `ContractEvents` value containing all the events published by the last contract invocation, which can be compared in a few different ways.

The event struct defined with `#[contractevent]` can be converted to its XDR form with `to_xdr` and compared directly, which is the most convenient way to assert on events when the contract defines them using the macro.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
assert_eq!(
    env.events().all(),
    [IncrementEvent { count: 1 }.to_xdr(&env, &contract_id)]
);
```

`filter_by_contract` narrows the events down to those emitted by a specific contract address, which is useful when a contract invocation calls into other contracts that also emit events.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
assert_eq!(
    env.events().all().filter_by_contract(&contract_id),
    [IncrementEvent { count: 2 }.to_xdr(&env, &contract_id)]
);
```

The event can also be compared by building its tuple form manually, as `(contract_id, topics, data)`, which is useful when the contract does not define its events using the `#[contractevent]` macro.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
assert_eq!(
    env.events().all(),
    soroban_sdk::vec![
        &env,
        (
            contract_id,
            (symbol_short!("COUNTER"), symbol_short!("increment")).into_val(&env),
            3u32.into_val(&env)
        ),
    ]
);
```

## Build the Contract

To build the contract, use the `stellar contract build` command.

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
stellar contract build
```

A `.wasm` file should be outputted in the `target` directory:

```
target/wasm32v1-none/release/soroban_events_contract.wasm
```

## Run the Contract

If you have [`stellar-cli`] installed, you can deploy the contract and invoke its functions.

### Deploy

> [tab] macOS/Linux
>
> ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract deploy \
>     --wasm target/wasm32v1-none/release/soroban_events_contract.wasm \
>     --alias events_example \
>     --source-account alice \
>     --network testnet
> ```

> [tab] Windows (PowerShell)
>
> ```powershell theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract deploy `
>     --wasm target/wasm32v1-none/release/soroban_events_contract.wasm `
>     --alias events_example `
>     --source-account alice `
>     --network testnet
> ```

### Invoke

> [tab] macOS/Linux
>
> ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract invoke \
>     --id events_example \
>     --source-account alice \
>     --network testnet \
>     -- \
>     increment
> ```

> [tab] Windows (PowerShell)
>
> ```powershell theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract invoke `
>     --id events_example `
>     --source-account alice `
>     --network testnet `
>     -- `
>     increment
> ```

The following output should occur using the code above.

```
📅 CAAA... - Success - Event: [{"symbol":"COUNTER"},{"symbol":"increment"}] = {"u32":1}
1
```

A single event is outputted, which is the contract event the contract published. The event contains the two topics, each a `Symbol`, and the data object containing the `u32`.

[`stellar-cli`]: ../getting-started/setup#install-the-stellar-cli
