Install SOF

Use this page when adding SOF crates to an existing Rust project.

If Solana traffic internals are still unfamiliar, read these first:

If the goal is to try the packaged repository examples first, use the commands in First Runtime Bring-Up.

Add The Crates To Your Project

Observer/runtime:

cargo add sof

Transaction SDK:

cargo add sof-tx

Typed tuning profiles:

cargo add sof-gossip-tuning

Only add sof-gossip-tuning if you are embedding sof and want typed host/runtime tuning in code.

Common feature combinations:

sof = { version = "0.18.1", features = ["gossip-bootstrap"] }
sof-tx = { version = "0.18.1", features = ["sof-adapters"] }

Choose Your Starting Point

Start with the app shape that matches what you need to build right now.

Observer/runtime host

Cargo.toml:

[dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
sof = "0.18.1"

src/main.rs:

use sof::runtime::ObserverRuntime;

#[tokio::main]
async fn main() -> Result<(), sof::runtime::RuntimeError> {
    ObserverRuntime::new().run_until_termination_signal().await
}

Use this when you need ingest, plugin events, datasets, or local control-plane signals.

Observer/runtime host with one plugin

Cargo.toml:

[dependencies]
async-trait = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
sof = "0.18.1"
tracing = "0.1"

src/main.rs:

use async_trait::async_trait;
use sof::{
    event::TxKind,
    framework::{Plugin, PluginConfig, PluginHost, TransactionEvent},
    runtime::ObserverRuntime,
};

#[derive(Clone, Copy, Debug, Default)]
struct NonVoteLogger;

#[async_trait]
impl Plugin for NonVoteLogger {
    fn config(&self) -> PluginConfig {
        PluginConfig::new().with_transaction()
    }

    async fn on_transaction(&self, event: &TransactionEvent) {
        if event.kind == TxKind::VoteOnly {
            return;
        }
        tracing::info!(slot = event.slot, kind = ?event.kind, "non-vote 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
}

Use this when you already know you want to consume SOF events in your own code.

Submitter using external control-plane state

Cargo.toml:

[dependencies]
sof-tx = "0.18.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

src/main.rs:

use sof_tx::TxSubmitClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _client = TxSubmitClient::builder()
        .with_rpc_defaults("https://api.mainnet-beta.solana.com")?
        .build();
    Ok(())
}

Use this when you only need the send pipeline and want sof-tx to source recent blockhashes from the same RPC endpoint you submit to.

If your signer already lives elsewhere and you only call submit_signed(...), you can stop at transport setup instead of wiring blockhash sourcing too.

Minimal App-Level Validation

After adding the dependency, validate it in your own application with a small compile target or a minimal runtime bring-up.

Example sof runtime skeleton:

use sof::runtime::ObserverRuntime;

#[tokio::main]
async fn main() -> Result<(), sof::runtime::RuntimeError> {
    ObserverRuntime::new().run_until_termination_signal().await
}

Example sof-tx provider wiring skeleton:

use sof_tx::TxSubmitClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _client = TxSubmitClient::builder()
        .with_rpc_defaults("https://api.mainnet-beta.solana.com")?
        .build();
    Ok(())
}

Example sof-tx signed-bytes skeleton:

use std::sync::Arc;

use sof_tx::{TxSubmitClient, submit::JsonRpcTransport};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _client = TxSubmitClient::builder()
        .with_rpc_transport(Arc::new(JsonRpcTransport::new(
            "https://api.mainnet-beta.solana.com",
        )?))
        .build();
    Ok(())
}

If you want sof-tx to consume live control-plane state from sof, enable the sof-adapters feature before wiring the adapter types.

Once your minimal path compiles, the next useful step is not more docs, but one real example:

If You Want To Try SOF From This Repository

From a checkout of this repository, you can use the packaged examples:

cargo run --release -p sof --example observer_runtime

Or:

cargo test -p sof-tx

Repository and contributor material lives under Maintain SOF.