delta_base_sdk/
events.rs

1//! # Base Layer Events
2//!
3//! This module provides types and utilities for working with events emitted by
4//! the base layer. The events notify clients (e.g. domains) about important
5//! state changes in the network, such as epoch transitions and vault
6//! migrations.
7//!
8//! Events are typically received through a subscription stream established via
9//! the [`BaseRpcClient`](crate::rpc::BaseRpcClient).
10
11use crate::{
12    sdl::SdlUpdate,
13    transactions::{
14        FailureInfo,
15        FailureKind,
16    },
17};
18use primitives::{
19    type_aliases::{
20        Epoch,
21        Planck,
22        TxId,
23    },
24    vault::Address,
25};
26use proto_types::{
27    error::{
28        HashSnafu,
29        MissingDataSnafu,
30        ProtoError,
31    },
32    events::{
33        Event,
34        FailureInfo as ProtoFailureInfo,
35    },
36};
37use snafu::{
38    OptionExt,
39    ResultExt,
40};
41
42/// Event emitted by the base layer to notify clients of state changes
43///
44/// Base layer events are used to communicate important state changes to clients
45/// (e.g. domains). They are streamed from the base layer via RPC and allow
46/// clients to react to changes in the network.
47///
48/// ## Example
49///
50/// ```no_run
51/// use delta_base_sdk::{events::BaseLayerEvent, rpc::BaseRpcClient};
52/// # use tokio_stream::StreamExt;
53///
54/// async fn handle_events() -> Result<(), Box<dyn std::error::Error>> {
55///     let client = BaseRpcClient::new("http://localhost:50051").await?;
56///     let shard_id = 1;
57///     
58///     // Subscribe to events for a specific shard
59///     let mut event_stream = client.stream_base_layer_events(shard_id).await?;
60///     
61///     // Process events as they arrive
62///     while let Some(event_result) = event_stream.next().await {
63///         match event_result {
64///             Ok(event) => eprintln!("Received base layer event: {event:?}"),
65///             Err(e) => eprintln!("Error receiving event: {e}"),
66///         }
67///     }
68///     Ok(())
69/// }
70/// ```
71#[derive(Debug, Clone)]
72pub enum BaseLayerEvent {
73    /// A new epoch has started in the network
74    ///
75    /// This event is emitted when the network transitions to a new epoch.
76    NewEpoch(Epoch),
77
78    /// Update on a [StateDiffList](crate::sdl::StateDiffList) status
79    ///
80    /// Notification about a SDL's status change, including acceptance or
81    /// rejection. This event can also be received when another domain submits
82    /// an SDL crediting our shard.
83    SdlUpdate(SdlUpdate),
84
85    /// A vault has migrated away from the subscribed domain
86    ///
87    /// This event indicates that the vault with the specified [Address] is now
88    /// emigrated and registered with another domain.
89    VaultEmigrated(Address),
90
91    /// A vault has migrated into the subscribed domain
92    ///
93    /// This event indicates that the vault with the specified [Address] is now
94    /// immigrated and registered with this domain.
95    VaultImmigrated(Address),
96
97    /// A vault was credited from a base vault
98    ///
99    /// This event indicates that the vault with the specified [Address] has
100    /// received the specified amount of [Planck]s in native tokens from a
101    /// base vault.
102    CreditFromBase(Address, Planck),
103
104    /// A transaction failed on the base layer
105    TransactionFailed {
106        /// The ID of the transaction that failed
107        tx_id: TxId,
108        /// Information about the failure
109        failure_info: FailureInfo,
110    },
111}
112
113impl TryFrom<proto_types::events::BaseLayerEvent> for BaseLayerEvent {
114    type Error = ProtoError;
115
116    fn try_from(value: proto_types::events::BaseLayerEvent) -> Result<Self, Self::Error> {
117        match value.event.context(MissingDataSnafu { field: "event" })? {
118            Event::SdlUpdate(sdl_update) => Ok(Self::SdlUpdate(sdl_update.try_into()?)),
119            Event::Epoch(epoch) => Ok(Self::NewEpoch(epoch)),
120            Event::VaultEmigrated(address) => Ok(Self::VaultEmigrated(address.try_into()?)),
121            Event::VaultImmigrated(address) => Ok(Self::VaultImmigrated(address.try_into()?)),
122            Event::VaultCredited(value) => Ok(Self::CreditFromBase(
123                value
124                    .address
125                    .context(MissingDataSnafu { field: "address" })?
126                    .try_into()?,
127                value.amount,
128            )),
129            Event::TransactionFailed(transaction_failed) => Ok(Self::TransactionFailed {
130                tx_id: transaction_failed.tx_id.try_into().context(HashSnafu)?,
131                failure_info: FailureInfo {
132                    kind: match transaction_failed.failure_info.context(MissingDataSnafu {
133                        field: "failure_info",
134                    })? {
135                        ProtoFailureInfo::Reason(reason) => FailureKind::Other { reason },
136                        ProtoFailureInfo::WrongEpoch(_) => FailureKind::WrongEpoch,
137                    },
138                    originator_shard: transaction_failed.originator_shard,
139                },
140            }),
141        }
142    }
143}