2 unstable releases
Uses new Rust 2024
new 0.2.0-rc2+solana.2 | Jul 10, 2025 |
---|---|
0.2.0-rc1+solana.2 |
|
0.2.0-pre.2+solana.2.1 |
|
0.1.1-pre.2+solana.2.1.11 | Feb 20, 2025 |
#671 in Magic Beans
265 downloads per month
Used in yellowstone-fumarole-cli
100KB
2K
SLoC
Yellowstone Fumarole Client
See repository for more details
lib.rs
:
A Rust implementation of the Yellowstone Fumarole Client using Tokio and Tonic.
Fumarole Client uses gRPC connections to communicate with the Fumarole service.
Yellowstone-GRPC vs Yellowstone-Fumarole
For the most part, the API is similar to the original yellowstone-grpc
client.
However, there are some differences:
- The
yellowstone-fumarole
(Coming soon) client uses multiple gRPC connections to communicate with the Fumarole service : avoidsHoL
blocking. - The
yellowstone-fumarole
subscribers are persistent and can be reused across multiple sessions (not computer). - The
yellowstone-fumarole
can reconnect to the Fumarole service if the connection is lost.
Examples
Examples can be found in the examples
directory.
Create a FumaroleClient
To create a FumaroleClient
, you need to provide a configuration object.
use yellowstone_fumarole_client::FumaroleClient;
use yellowstone_fumarole_client::config::FumaroleConfig;
#[tokio::main]
async fn main() {
let config = FumaroleConfig {
endpoint: "https://example.com".to_string(),
x_token: Some("00000000-0000-0000-0000-000000000000".to_string()),
max_decoding_message_size_bytes: FumaroleConfig::default_max_decoding_message_size_bytes(),
x_metadata: Default::default(),
};
let fumarole_client = FumaroleClient::connect(config)
.await
.expect("Failed to connect to fumarole");
}
The prefered way to create FumaroleConfig
is use serde_yaml
to deserialize from a YAML file.
let config_file = std::fs::File::open("path/to/config.yaml").unwrap();
let config: FumaroleConfig = serde_yaml::from_reader(config_file).unwrap();
Here's an example of a YAML file:
endpoint: https://example.com
x-token: 00000000-0000-0000-0000-000000000000
Dragonsmouth-like Subscribe
use {
clap::Parser,
solana_sdk::{bs58, pubkey::Pubkey},
std::{collections::HashMap, path::PathBuf},
yellowstone_fumarole_client::{
config::FumaroleConfig, DragonsmouthAdapterSession, FumaroleClient,
},
yellowstone_grpc_proto::geyser::{
subscribe_update::UpdateOneof, SubscribeRequest,
SubscribeRequestFilterTransactions, SubscribeUpdateAccount, SubscribeUpdateTransaction,
},
};
#[derive(Debug, Clone, Parser)]
#[clap(author, version, about = "Yellowstone Fumarole Example")]
struct Args {
/// Path to static config file
#[clap(long)]
config: PathBuf,
#[clap(subcommand)]
action: Action,
}
#[derive(Debug, Clone, Parser)]
enum Action {
/// Subscribe to fumarole events
Subscribe(SubscribeArgs),
}
#[derive(Debug, Clone, Parser)]
struct SubscribeArgs {
/// Name of the persistent subscriber to use
#[clap(long)]
name: String,
}
fn summarize_account(account: SubscribeUpdateAccount) -> Option<String> {
let slot = account.slot;
let account = account.account?;
let pubkey = Pubkey::try_from(account.pubkey).expect("Failed to parse pubkey");
let owner = Pubkey::try_from(account.owner).expect("Failed to parse owner");
Some(format!("account,{},{},{}", slot, pubkey, owner))
}
fn summarize_tx(tx: SubscribeUpdateTransaction) -> Option<String> {
let slot = tx.slot;
let tx = tx.transaction?;
let sig = bs58::encode(tx.signature).into_string();
Some(format!("tx,{slot},{sig}"))
}
async fn subscribe(args: SubscribeArgs, config: FumaroleConfig) {
// This request listen for all account updates and transaction updates
let request = SubscribeRequest {
transactions: HashMap::from([(
"f1".to_owned(),
SubscribeRequestFilterTransactions::default(),
)]),
..Default::default()
};
let mut fumarole_client = FumaroleClient::connect(config)
.await
.expect("Failed to connect to fumarole");
let dragonsmouth_session = fumarole_client
.dragonsmouth_subscribe(args.name, request)
.await
.expect("Failed to subscribe");
let DragonsmouthAdapterSession {
sink: _,
mut source,
mut fumarole_handle,
} = dragonsmouth_session;
loop {
tokio::select! {
result = &mut fumarole_handle => {
eprintln!("Fumarole handle closed: {:?}", result);
break;
}
maybe = source.recv() => {
match maybe {
None => {
eprintln!("Source closed");
break;
}
Some(result) => {
let event = result.expect("Failed to receive event");
let message = if let Some(oneof) = event.update_oneof {
match oneof {
UpdateOneof::Account(account_update) => {
summarize_account(account_update)
}
UpdateOneof::Transaction(tx) => {
summarize_tx(tx)
}
_ => None,
}
} else {
None
};
if let Some(message) = message {
println!("{}", message);
}
}
}
}
}
}
}
#[tokio::main]
async fn main() {
let args: Args = Args::parse();
let config = std::fs::read_to_string(&args.config).expect("Failed to read config file");
let config: FumaroleConfig =
serde_yaml::from_str(&config).expect("Failed to parse config file");
match args.action {
Action::Subscribe(sub_args) => {
subscribe(sub_args, config).await;
}
}
}
Enable Prometheus Metrics
To enable Prometheus metrics, add the features = [prometheus]
to your Cargo.toml
file:
[dependencies]
yellowstone-fumarole-client = { version = "x.y.z", features = ["prometheus"] }
Then, you can use the metrics
module to register and expose metrics:
use yellowstone_fumarole_client::metrics;
use prometheus::{Registry};
let r = Registry::new();
metrics::register_metrics(&r);
// After registering, you should see `fumarole_` prefixed metrics in the registry.
Getting Started
Follows the instruction in the README
file to get started.
Feature Flags
prometheus
: Enables Prometheus metrics for the Fumarole client.
Dependencies
~17–31MB
~577K SLoC