mevshare 0.1.1

A Rust client library for subscribing to the Flashbots MEV-Share SSE event stream.
Documentation
use bon::Builder;
use tracing::{debug, info, instrument};

use crate::{Error, subscription::Subscription};

/// A client for the Flashbots MEV-Share SSE event stream.
///
/// # Examples
///
/// Using the default mainnet endpoint:
/// ```rust,no_run
/// use mevshare::Client;
/// use futures::StreamExt;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let client = Client::builder().build();
///     let sub = client.subscribe().await?;
///     let mut stream = sub.into_stream();
///
///     while let Some(event) = stream.next().await {
///         println!("{event:#?}");
///     }
///     Ok(())
/// }
/// ```
///
/// Using a custom endpoint:
/// ```rust,no_run
/// use mevshare::Client;
/// use futures::StreamExt;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let client = Client::builder()
///         .endpoint("https://mev-share.flashbots.net")
///         .user_agent("my-bot/1.0")
///         .build();
///
///     let sub = client.subscribe().await?;
///     let mut stream = sub.into_stream();
///
///     while let Some(event) = stream.next().await {
///         println!("{event:#?}");
///     }
///     Ok(())
/// }
/// ```
#[derive(Debug, Default, Clone, Builder)]
pub struct Client {
    /// The MEV-Share SSE endpoint URL.
    /// Defaults to the Flashbots mainnet endpoint.
    #[builder(into, default = "https://mev-share.flashbots.net")]
    pub endpoint: String,
    /// The `User-Agent` header sent with every request.
    /// Defaults to `<crate_name>/<crate_version>`.
    #[builder(into, default = format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")))]
    pub user_agent: String,
    /// The underlying HTTP client.
    /// Defaults to a pre-configured [`reqwest::Client`].
    #[builder(default = reqwest::Client::builder().build().expect("Failed to build reqwest client"))]
    pub client: reqwest::Client,
}

impl Client {
    /// Opens a connection to the MEV-Share SSE event stream.
    ///
    /// Returns a [`Subscription`] handle. Call [`.into_stream()`](Subscription::into_stream)
    /// on it to get an async [`Stream`](futures::Stream) of [`Event`](alloy::rpc::types::mev::mevshare::Event)s.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Http`] if the HTTP request fails.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use mevshare::Client;
    /// use futures::StreamExt;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     let client = Client::builder().build();
    ///     let sub = client.subscribe().await?;
    ///     let mut stream = sub.into_stream();
    ///     while let Some(event) = stream.next().await {
    ///         println!("{event:#?}");
    ///     }
    ///     Ok(())
    /// }
    /// ```
    #[instrument(skip(self), fields(endpoint = %self.endpoint, user_agent = %self.user_agent))]
    pub async fn subscribe(&self) -> Result<Subscription, Error> {
        info!("connecting to MEV-Share stream");
        let response = self
            .client
            .get(&self.endpoint)
            .header("Accept", "text/event-stream")
            .header("Cache-Control", "no-cache")
            .header("User-Agent", &self.user_agent)
            .send()
            .await?
            .error_for_status()?;

        debug!(status = %response.status(), "connected successfully");

        Ok(Subscription { response })
    }
}