Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Events

Filter and decode contract events. Public events come from the node; private events come from the PXE (they require decryption).

Runnable Example

  • examples/event_logs.rs — reads both public and private events from a sample contract.

Public Events

use aztec_rs::events::{get_public_events, PublicEventFilter};

let filter = PublicEventFilter::new(contract_address, from_block, to_block)
    .with_event::<MyEvent>();

let result = get_public_events::<MyEvent>(&node, filter).await?;
for ev in result.events {
    println!("{:?}", ev.data);
}

The node’s get_public_logs endpoint backs this; aztec-contract decodes the field vector into your typed struct.

Private Events

Private events are encrypted and delivered via notes; the PXE decrypts them for any account whose key is registered.

use aztec_rs::wallet::{EventMetadataDefinition, PrivateEventFilter};

let events = wallet.get_private_events(&event_metadata, filter).await?;
for ev in events {
    /* decoded via PrivateEventMetadata */
}

If no registered account can decrypt a given log, the decoder simply returns no result — that is not an error.

Edge Cases

  • Chain re-orgs: previously observed events may be replaced; UIs should reconcile after each block sync rather than cache forever.
  • Missing recipient keys: private events are skipped rather than failing; register the relevant account first (Pxe::register_account).
  • Block range bounds: wide ranges can be slow on the node; paginate by block when possible.

Full Runnable Example

Source: examples/event_logs.rs — reads both public and private events from a sample contract.

//! Emit and read public and private events.

#![allow(clippy::print_stdout, clippy::wildcard_imports)]

mod common;

use common::*;

fn example_event0_metadata() -> EventMetadataDefinition {
    EventMetadataDefinition {
        event_selector: event_selector_from_signature("ExampleEvent0(Field,Field)"),
        abi_type: AbiType::Struct {
            name: "ExampleEvent0".to_owned(),
            fields: vec![
                AbiParameter {
                    name: "value0".to_owned(),
                    typ: AbiType::Field,
                    visibility: None,
                },
                AbiParameter {
                    name: "value1".to_owned(),
                    typ: AbiType::Field,
                    visibility: None,
                },
            ],
        },
        field_names: vec!["value0".to_owned(), "value1".to_owned()],
    }
}

fn example_event1_metadata() -> EventMetadataDefinition {
    EventMetadataDefinition {
        event_selector: event_selector_from_signature("ExampleEvent1((Field),u8)"),
        abi_type: AbiType::Struct {
            name: "ExampleEvent1".to_owned(),
            fields: vec![
                AbiParameter {
                    name: "value2".to_owned(),
                    typ: AbiType::Struct {
                        name: "AztecAddress".to_owned(),
                        fields: vec![AbiParameter {
                            name: "inner".to_owned(),
                            typ: AbiType::Field,
                            visibility: None,
                        }],
                    },
                    visibility: None,
                },
                AbiParameter {
                    name: "value3".to_owned(),
                    typ: AbiType::Integer {
                        sign: "unsigned".to_owned(),
                        width: 8,
                    },
                    visibility: None,
                },
            ],
        },
        field_names: vec!["value2".to_owned(), "value3".to_owned()],
    }
}

#[tokio::main]
async fn main() -> Result<(), aztec_rs::Error> {
    let Some((wallet, account1)) =
        setup_wallet_with_accounts(TEST_ACCOUNT_0, &[TEST_ACCOUNT_1]).await
    else {
        return Err(aztec_rs::Error::InvalidData(format!(
            "node not reachable at {}",
            node_url()
        )));
    };
    let account2 = imported_complete_address(TEST_ACCOUNT_1).address;
    wallet.pxe().register_sender(&account2).await?;

    let (contract_address, artifact, _) =
        deploy_contract(&wallet, load_test_log_artifact(), vec![], account1).await?;

    let private_tx = send_call(
        &wallet,
        build_call(
            &artifact,
            contract_address,
            "emit_encrypted_events",
            vec![
                abi_address(account2),
                AbiValue::Array(vec![
                    AbiValue::Field(Fr::from(1u64)),
                    AbiValue::Field(Fr::from(2u64)),
                    AbiValue::Field(Fr::from(3u64)),
                    AbiValue::Field(Fr::from(4u64)),
                ]),
            ],
        ),
        account1,
    )
    .await?;

    let public_tx = send_call(
        &wallet,
        build_call(
            &artifact,
            contract_address,
            "emit_unencrypted_events",
            vec![AbiValue::Array(vec![
                AbiValue::Field(Fr::from(11u64)),
                AbiValue::Field(Fr::from(12u64)),
                AbiValue::Field(Fr::from(13u64)),
                AbiValue::Field(Fr::from(14u64)),
            ])],
        ),
        account1,
    )
    .await?;

    let private_block = wallet
        .node()
        .get_tx_receipt(&private_tx)
        .await?
        .block_number
        .ok_or_else(|| aztec_rs::Error::InvalidData("tx missing block number".into()))?;
    let public_block = wallet
        .node()
        .get_tx_receipt(&public_tx)
        .await?
        .block_number
        .ok_or_else(|| aztec_rs::Error::InvalidData("tx missing block number".into()))?;

    let private_events = wallet
        .get_private_events(
            &example_event0_metadata(),
            PrivateEventFilter {
                contract_address,
                from_block: Some(private_block),
                to_block: Some(private_block + 1),
                scopes: vec![account1, account2],
                ..Default::default()
            },
        )
        .await?;

    let public_events = get_public_events(
        wallet.node(),
        &example_event1_metadata(),
        PublicEventFilter {
            from_block: Some(public_block),
            to_block: Some(public_block + 1),
            ..Default::default()
        },
    )
    .await?;

    println!("Contract address:   {contract_address}");
    println!("Private tx hash:    {private_tx}");
    println!("Public tx hash:     {public_tx}");
    println!("Private events:     {}", private_events.len());
    println!("Public events:      {}", public_events.events.len());

    Ok(())
}

References