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}