1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! SOF observer engine and plugin framework.
//!
//! External users should usually start from [`crate::runtime`] and [`crate::framework`].
//!
//! # Start The Packaged Runtime
//!
//! ```no_run
//! use sof::runtime::ObserverRuntime;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), sof::runtime::RuntimeError> {
//! ObserverRuntime::new().run_until_termination_signal().await
//! }
//! ```
//!
//! # Attach One Plugin
//!
//! ```no_run
//! use async_trait::async_trait;
//! use sof::{
//! event::TxKind,
//! framework::{ObserverPlugin, PluginConfig, PluginHost, TransactionEvent, TxCommitmentStatus},
//! runtime::ObserverRuntime,
//! };
//!
//! #[derive(Clone, Copy, Debug, Default)]
//! struct NonVoteLogger;
//!
//! #[async_trait]
//! impl ObserverPlugin for NonVoteLogger {
//! fn config(&self) -> PluginConfig {
//! PluginConfig::new()
//! .with_transaction()
//! .at_commitment(TxCommitmentStatus::Confirmed)
//! }
//!
//! async fn on_transaction(&self, event: &TransactionEvent) {
//! if event.kind == TxKind::VoteOnly {
//! return;
//! }
//! tracing::info!(slot = event.slot, kind = ?event.kind, "transaction observed");
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), sof::runtime::RuntimeError> {
//! let host = PluginHost::builder().add_plugin(NonVoteLogger).build();
//!
//! ObserverRuntime::new()
//! .with_plugin_host(host)
//! .run_until_termination_signal()
//! .await
//! }
//! ```
//!
//! # Request Explicit Inline Transaction Delivery
//!
//! ```no_run
//! use async_trait::async_trait;
//! use sof::{
//! event::TxKind,
//! framework::{
//! ObserverPlugin, PluginConfig, PluginHost, TransactionDispatchMode, TransactionEvent,
//! TxCommitmentStatus,
//! },
//! runtime::ObserverRuntime,
//! };
//!
//! #[derive(Clone, Copy, Debug, Default)]
//! struct InlineTxLogger;
//!
//! #[async_trait]
//! impl ObserverPlugin for InlineTxLogger {
//! fn config(&self) -> PluginConfig {
//! PluginConfig::new()
//! .with_transaction_mode(TransactionDispatchMode::Inline)
//! .at_commitment(TxCommitmentStatus::Processed)
//! }
//!
//! async fn on_transaction(&self, event: &TransactionEvent) {
//! if event.kind == TxKind::VoteOnly {
//! return;
//! }
//! tracing::info!(slot = event.slot, kind = ?event.kind, "inline transaction observed");
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), sof::runtime::RuntimeError> {
//! let host = PluginHost::builder().add_plugin(InlineTxLogger).build();
//!
//! ObserverRuntime::new()
//! .with_plugin_host(host)
//! .run_until_termination_signal()
//! .await
//! }
//! ```
//!
//! [`crate::framework::TransactionDispatchMode::Inline`] is an explicit delivery contract for
//! `on_transaction`. SOF dispatches that hook as soon as one full serialized transaction is ready
//! on the anchored contiguous inline path, and falls back to the completed-dataset point only when
//! the early path is not yet reconstructable.
//!
//! # Prefer Compiled Transaction Prefilters
//!
//! ```no_run
//! use async_trait::async_trait;
//! use solana_pubkey::Pubkey;
//! use sof::{
//! framework::{
//! ObserverPlugin, PluginConfig, PluginHost, TransactionDispatchMode,
//! TransactionInterest, TransactionPrefilter,
//! },
//! runtime::ObserverRuntime,
//! };
//!
//! #[derive(Clone, Debug)]
//! struct RaydiumPoolWatcher {
//! filter: TransactionPrefilter,
//! }
//!
//! impl Default for RaydiumPoolWatcher {
//! fn default() -> Self {
//! let pool = Pubkey::new_unique();
//! let program = Pubkey::new_unique();
//! Self {
//! filter: TransactionPrefilter::new(TransactionInterest::Critical)
//! .with_account_required([pool, program]),
//! }
//! }
//! }
//!
//! #[async_trait]
//! impl ObserverPlugin for RaydiumPoolWatcher {
//! fn config(&self) -> PluginConfig {
//! PluginConfig::new().with_transaction_mode(TransactionDispatchMode::Inline)
//! }
//!
//! fn transaction_prefilter(&self) -> Option<&TransactionPrefilter> {
//! Some(&self.filter)
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), sof::runtime::RuntimeError> {
//! let host = PluginHost::builder()
//! .add_plugin(RaydiumPoolWatcher::default())
//! .build();
//!
//! ObserverRuntime::new()
//! .with_plugin_host(host)
//! .run_until_termination_signal()
//! .await
//! }
//! ```
//!
//! Prefer [`crate::framework::TransactionPrefilter`] when your plugin only
//! matches exact signatures or account-key presence. On the inline path, SOF can
//! use that compiled matcher on a sanitized transaction view and skip full owned
//! transaction decode for misses.
//!
//! More user-facing examples live in `crates/sof-observer/README.md` and the published example
//! programs under `crates/sof-observer/examples/`.
pub use ;
/// Runtime environment override storage used by code-driven setup APIs.
/// Runtime transaction event types.
/// Plugin framework for attaching custom runtime hooks.
/// Processed provider-stream ingress types and adapters.
/// Packaged runtime entrypoints for embedding SOF.
/// Runtime-stage counters for ingress, dataset reconstruction, and tx delivery.